Browse Source

广告优化

liuxiaolong 2 years ago
parent
commit
cf25b3242f
100 changed files with 1794 additions and 690 deletions
  1. 26 0
      app/build.gradle
  2. 6 5
      app/src/main/java/com/convenient/android/lib/ui/sample/ad/AdMainActivity.kt
  3. 0 1
      app/src/main/java/com/convenient/android/lib/ui/sample/ad/activity/AdAppOpenActivity.kt
  4. 0 2
      app/src/main/java/com/convenient/android/lib/ui/sample/ad/activity/AdBannerActivity.kt
  5. 0 2
      app/src/main/java/com/convenient/android/lib/ui/sample/ad/activity/AdFullScreenAdActivity.kt
  6. 4 3
      app/src/main/java/com/convenient/android/lib/ui/sample/ad/activity/AdNativeActivity.kt
  7. 0 2
      app/src/main/java/com/convenient/android/lib/ui/sample/ad/activity/AdNativeTemplateActivity.kt
  8. 1 1
      app/src/main/java/com/convenient/android/lib/ui/sample/ad/activity/AppWelcomeActivity.kt
  9. 7 5
      app/src/main/java/com/convenient/android/lib/ui/sample/ad/model/Datas.kt
  10. 3 1
      app/src/main/java/com/convenient/android/lib/ui/sample/ad/page/AdHomePage.kt
  11. 3 5
      app/src/main/java/com/convenient/android/lib/ui/sample/ad/page/AdInterstitialPage.kt
  12. 1 9
      app/src/main/java/com/convenient/android/lib/ui/sample/ad/page/AdMainPage.kt
  13. 0 5
      app/src/main/java/com/convenient/android/lib/ui/sample/ad/util/CoilImageLoader.kt
  14. 9 9
      app/src/main/java/com/convenient/android/lib/ui/sample/ad/viewmodel/AdConfigViewModel.kt
  15. 0 2
      app/src/main/java/com/convenient/android/lib/ui/sample/media/MediaFilesPage.kt
  16. 0 1
      app/src/main/java/com/convenient/android/lib/ui/sample/media/MediaFilesRepository.kt
  17. 0 4
      app/src/main/java/com/convenient/android/lib/ui/sample/media/MediaFilesViewModel.kt
  18. 0 2
      app/src/main/java/com/convenient/android/lib/ui/theme/Type.kt
  19. 0 1
      app/src/main/res/layout/activity_ad_banner.xml
  20. 0 1
      app/src/main/res/layout/activity_ad_interstitial.xml
  21. 7 7
      app/src/main/res/layout/activity_ad_native.xml
  22. 3 4
      app/src/main/res/layout/activity_ad_native_template.xml
  23. 8 8
      app/src/main/res/layout/view_rv_ad_native_item.xml
  24. 3 4
      app/src/main/res/layout/view_rv_ad_native_template_item.xml
  25. 1 1
      app/src/main/res/values-night/themes.xml
  26. 1 1
      app/src/main/res/values/themes.xml
  27. 26 3
      lib_ad_admob/build.gradle
  28. 36 1
      lib_ad_admob/proguard-rules.pro
  29. 2 4
      lib_ad_admob/src/androidTest/java/com/composition/android/ad/admob/ExampleInstrumentedTest.kt
  30. 0 1
      lib_ad_admob/src/main/java/com/composition/android/ad/admob/impl/AdmobAdSize.kt
  31. 86 62
      lib_ad_admob/src/main/java/com/composition/android/ad/admob/load/AdmobAdLoader.kt
  32. 3 3
      lib_ad_admob/src/main/java/com/composition/android/ad/admob/load/request/AdMobNativeRequestImpl.kt
  33. 3 5
      lib_ad_admob/src/main/java/com/composition/android/ad/admob/load/request/AdmobAppOpenRequestImpl.kt
  34. 5 7
      lib_ad_admob/src/main/java/com/composition/android/ad/admob/load/request/AdmobBannerRequestImpl.kt
  35. 3 3
      lib_ad_admob/src/main/java/com/composition/android/ad/admob/load/request/AdmobInterstitialRequestImpl.kt
  36. 4 4
      lib_ad_admob/src/main/java/com/composition/android/ad/admob/load/request/AdmobRewardInterstitialRequestImpl.kt
  37. 3 3
      lib_ad_admob/src/main/java/com/composition/android/ad/admob/load/request/AdmobRewardRequestImpl.kt
  38. 0 1
      lib_ad_admob/src/main/java/com/composition/android/ad/admob/load/view/AdmobBannerView.kt
  39. 49 58
      lib_ad_admob/src/main/java/com/composition/android/ad/admob/load/view/AdmobNativeAdView.kt
  40. 22 44
      lib_ad_admob/src/main/java/com/composition/android/ad/admob/util/AdmobAdExpan.kt
  41. 2 1
      lib_ad_admob/src/main/res/layout/layout_admob_native_media_view.xml
  42. 1 2
      lib_ad_admob/src/test/java/com/composition/android/ad/admob/ExampleUnitTest.kt
  43. 1 0
      lib_ad_applovinmax/.gitignore
  44. 41 0
      lib_ad_applovinmax/build.gradle
  45. 0 0
      lib_ad_applovinmax/consumer-rules.pro
  46. 21 0
      lib_ad_applovinmax/proguard-rules.pro
  47. 22 0
      lib_ad_applovinmax/src/androidTest/java/com/kdanmobile/android/ad/applovinmax/ExampleInstrumentedTest.kt
  48. 4 0
      lib_ad_applovinmax/src/main/AndroidManifest.xml
  49. 16 0
      lib_ad_applovinmax/src/test/java/com/kdanmobile/android/ad/applovinmax/ExampleUnitTest.kt
  50. 25 3
      lib_ad_core/build.gradle
  51. 3 0
      lib_ad_core/consumer-rules.pro
  52. 4 1
      lib_ad_core/proguard-rules.pro
  53. 2 4
      lib_ad_core/src/androidTest/java/com/composition/android/lib/ad/ExampleInstrumentedTest.kt
  54. 1 2
      lib_ad_core/src/main/AndroidManifest.xml
  55. 130 44
      lib_ad_core/src/main/java/com/composition/android/lib/ad/AdLoad.kt
  56. 25 5
      lib_ad_core/src/main/java/com/composition/android/lib/ad/AdManager.kt
  57. 28 2
      lib_ad_core/src/main/java/com/composition/android/lib/ad/AdUnitConfigManager.kt
  58. 0 19
      lib_ad_core/src/main/java/com/composition/android/lib/ad/basic/AdResult.kt
  59. 11 9
      lib_ad_core/src/main/java/com/composition/android/lib/ad/basic/AdType.kt
  60. 0 6
      lib_ad_core/src/main/java/com/composition/android/lib/ad/basic/BannerAdViewHolder.kt
  61. 5 8
      lib_ad_core/src/main/java/com/composition/android/lib/ad/basic/BasicAdView.kt
  62. 8 11
      lib_ad_core/src/main/java/com/composition/android/lib/ad/basic/NativeAdViewHolder.kt
  63. 0 13
      lib_ad_core/src/main/java/com/composition/android/lib/ad/basic/NormalImageLoader.kt
  64. 1 1
      lib_ad_core/src/main/java/com/composition/android/lib/ad/bean/AdUnitBean.kt
  65. 1 1
      lib_ad_core/src/main/java/com/composition/android/lib/ad/bean/AdUnitLoadConfig.kt
  66. 4 4
      lib_ad_core/src/main/java/com/composition/android/lib/ad/factory/AdLoaderFactory.kt
  67. 190 29
      lib_ad_core/src/main/java/com/composition/android/lib/ad/impl/Api.kt
  68. 2 2
      lib_ad_core/src/main/java/com/composition/android/lib/ad/impl/NormalStrategy.kt
  69. 12 8
      lib_ad_core/src/main/java/com/composition/android/lib/ad/impl/UnknownAdLoader.kt
  70. 45 9
      lib_ad_core/src/main/java/com/composition/android/lib/ad/interfaces/AdLoader.kt
  71. 21 0
      lib_ad_core/src/main/java/com/composition/android/lib/ad/interfaces/IAdSize.kt
  72. 0 1
      lib_ad_core/src/main/java/com/composition/android/lib/ad/interfaces/ImageLoader.kt
  73. 106 0
      lib_ad_core/src/main/java/com/composition/android/lib/ad/util/AdCoreExtenstions.kt
  74. 41 4
      lib_ad_core/src/main/java/com/composition/android/lib/ad/util/AdLog.kt
  75. 16 3
      lib_ad_core/src/main/java/com/composition/android/lib/ad/widget/BannerAdView.kt
  76. 101 51
      lib_ad_core/src/main/java/com/composition/android/lib/ad/widget/NativeAdView.kt
  77. 19 13
      lib_ad_core/src/main/res/values/attr.xml
  78. 26 2
      lib_ad_csj/build.gradle
  79. BIN
      lib_ad_csj/libs/open_ad_sdk.aar
  80. 2 4
      lib_ad_csj/src/androidTest/java/com/composition/android/ad/csj/ExampleInstrumentedTest.kt
  81. 41 0
      lib_ad_csj/src/main/java/com/composition/android/ad/csj/impl/CSJAppDownloadListener.kt
  82. 3 4
      lib_ad_csj/src/main/java/com/composition/android/ad/csj/impl/CSJInitialize.kt
  83. 17 0
      lib_ad_csj/src/main/java/com/composition/android/ad/csj/impl/CSJNativeAdImageSize.kt
  84. 87 37
      lib_ad_csj/src/main/java/com/composition/android/ad/csj/load/CSJAdLoader.kt
  85. 7 13
      lib_ad_csj/src/main/java/com/composition/android/ad/csj/load/request/CSJAppOpenRequestImpl.kt
  86. 2 2
      lib_ad_csj/src/main/java/com/composition/android/ad/csj/load/request/CSJBannerRequestImpl.kt
  87. 2 2
      lib_ad_csj/src/main/java/com/composition/android/ad/csj/load/request/CSJInterstitialRequestImpl.kt
  88. 63 0
      lib_ad_csj/src/main/java/com/composition/android/ad/csj/load/request/CSJNativeRequestImpl.kt
  89. 11 6
      lib_ad_csj/src/main/java/com/composition/android/ad/csj/load/request/CSJNativeTemplateRequestImpl.kt
  90. 2 2
      lib_ad_csj/src/main/java/com/composition/android/ad/csj/load/request/CSJRewardRequestImpl.kt
  91. 190 0
      lib_ad_csj/src/main/java/com/composition/android/ad/csj/load/view/CSJNativeAdView.kt
  92. 6 13
      lib_ad_csj/src/main/java/com/composition/android/ad/csj/load/view/CSJNativeTemplateAdView.kt
  93. 10 39
      lib_ad_csj/src/main/java/com/composition/android/ad/csj/util/CSJAdExpan.kt
  94. 38 0
      lib_ad_csj/src/main/res/layout/csj_native_ad_media_image_views.xml
  95. 1 1
      lib_ad_csj/src/main/res/xml/ttad_file_paths.xml
  96. 1 2
      lib_ad_csj/src/test/java/com/composition/android/ad/csj/ExampleUnitTest.kt
  97. 49 18
      lib_ad_gromore/build.gradle
  98. BIN
      lib_ad_gromore/libs/open_ad_sdk.aar
  99. 2 4
      lib_ad_gromore/src/androidTest/java/com/composition/android/ad/gromore/ExampleInstrumentedTest.kt
  100. 0 0
      lib_ad_gromore/src/main/java/com/composition/android/ad/gromore/load/impl/GMBannerAdSize.kt

+ 26 - 0
app/build.gradle

@@ -47,10 +47,36 @@ android {
     composeOptions {
         kotlinCompilerExtensionVersion compose_version
     }
+
 }
 
 
 dependencies {
+
+    implementation fileTree(dir: 'libs', include: ['*.jar','*.aar'])
+
+//    implementation 'com.google.android.gms:play-services-ads:21.3.0'
+//    implementation 'com.pangle.cn:ads-sdk-pro:4.8.0.8'
+//    implementation "com.gromore.cn:gromore-sdk:3.7.0.3"
+//    implementation "com.gromore.cn:pangle-adapter:4.8.0.8.2"
+    //mintegral
+//    api("com.mbridge.msdk.support:videojs:16.2.27")
+//    api("com.mbridge.msdk.support:mbjscommon:16.2.27")
+//    api("com.mbridge.msdk.support:playercommon:16.2.27")
+//    api("com.mbridge.msdk.support:reward:16.2.27")
+//    api("com.mbridge.msdk.support:videocommon:16.2.27")
+//    api("com.mbridge.msdk.support:chinasame:16.2.27")
+//    api("com.mbridge.msdk.support:interstitialvideo:16.2.27")
+//    api("com.mbridge.msdk.support:mbnative:16.2.27")
+//    api("com.mbridge.msdk.support:nativeex:16.2.27")
+//    api("com.mbridge.msdk.support:mbnativeadvanced:16.2.27")
+//    api("com.mbridge.msdk.support:interstitial:16.2.27")
+//    api("com.mbridge.msdk.support:mbbanner:16.2.27")
+//    api("com.mbridge.msdk.support:mbsplash:16.2.27")
+//    api("com.mbridge.msdk.support:mbbid:16.2.27")
+//    api("com.mbridge.msdk.support:newinterstitial:16.2.27")
+
+
     implementation project(':lib_common')
     implementation project(':lib_ad_core')
     implementation project(':lib_ad_admob')

+ 6 - 5
app/src/main/java/com/convenient/android/lib/ui/sample/ad/AdMainActivity.kt

@@ -1,20 +1,21 @@
 package com.convenient.android.lib.ui.sample.ad
 
-import androidx.appcompat.app.AppCompatActivity
 import android.os.Bundle
 import android.util.Log
 import androidx.activity.compose.setContent
+import androidx.appcompat.app.AppCompatActivity
+import com.composition.android.ad.admob.impl.AdmobInitialize
+import com.composition.android.ad.csj.impl.CSJInitialize
+import com.composition.android.ad.gromore.impl.GroMoreInitialize
 import com.composition.android.lib.ad.AdManager
 import com.composition.android.lib.ad.AdUnitConfigManager
+import com.composition.android.lib.ad.bean.AdUnitBean
 import com.convenient.android.lib.ui.sample.ad.model.Datas
 import com.convenient.android.lib.ui.sample.ad.page.AdMainPage
 import com.convenient.android.lib.ui.theme.SampleTheme
-import com.composition.android.ad.admob.impl.AdmobInitialize
-import com.composition.android.ad.csj.impl.CSJInitialize
-import com.composition.android.ad.gromore.load.impl.GroMoreInitialize
-import com.composition.android.lib.ad.bean.AdUnitBean
 
 class AdMainActivity : AppCompatActivity() {
+
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContent {

+ 0 - 1
app/src/main/java/com/convenient/android/lib/ui/sample/ad/activity/AdAppOpenActivity.kt

@@ -12,7 +12,6 @@ import com.convenient.android.lib.R
 import com.convenient.android.lib.databinding.ActivityAdAppOpenBinding
 import com.convenient.android.lib.ui.sample.ad.model.Datas
 import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.launch
 
 /**

+ 0 - 2
app/src/main/java/com/convenient/android/lib/ui/sample/ad/activity/AdBannerActivity.kt

@@ -7,10 +7,8 @@ import com.composition.android.lib.ad.AdLoad
 import com.composition.android.lib.ad.AdManager
 import com.composition.android.lib.ad.basic.AdResult
 import com.composition.android.lib.ad.basic.buildBannerAdViewHolder
-import com.composition.android.lib.ad.bean.AdUnitBean
 import com.composition.android.lib.ad.impl.NormalAdListener
 import com.composition.android.lib.ad.util.adLogE
-import com.composition.android.lib.ad.widget.BannerAdView
 import com.convenient.android.common.base.viewbinding.BaseBindingActivity
 import com.convenient.android.common.extension.setViewsClick
 import com.convenient.android.common.utils.ToastUtil

+ 0 - 2
app/src/main/java/com/convenient/android/lib/ui/sample/ad/activity/AdFullScreenAdActivity.kt

@@ -5,8 +5,6 @@ import androidx.lifecycle.lifecycleScope
 import com.composition.android.lib.ad.AdLoad
 import com.composition.android.lib.ad.AdManager
 import com.composition.android.lib.ad.basic.AdResult
-import com.composition.android.lib.ad.basic.AdType
-import com.composition.android.lib.ad.impl.NormalAdListener
 import com.composition.android.lib.ad.util.adLogE
 import com.convenient.android.common.base.viewbinding.BaseBindingActivity
 import com.convenient.android.common.extension.setViewsClick

+ 4 - 3
app/src/main/java/com/convenient/android/lib/ui/sample/ad/activity/AdNativeActivity.kt

@@ -84,12 +84,13 @@ class AdNativeActivity : BaseBindingActivity<ActivityAdNativeBinding>(ActivityAd
 
             val nativeViewBinding = ViewAdNativeBinding.inflate(layoutInflater)
             val viewHolder = buildAdNativeViewHolder {
-                this.rootView = nativeViewBinding.root
+                this.customLayoutRootView = nativeViewBinding.root
                 this.titleView = nativeViewBinding.tvAdTitle
                 this.titleDescView = nativeViewBinding.tvAdTitleDesc
                 this.iconView = nativeViewBinding.ivAdIcon
-                this.contentMediaGroup = nativeViewBinding.flAdMediaContent
-                this.callActionView = nativeViewBinding.btnCallToAction
+                this.contentMediaViewGroup = nativeViewBinding.flAdMediaContent
+                this.callActionButtonView = nativeViewBinding.btnCallToAction
+                this.dislikeView
                 this.activity= this@AdNativeActivity
                 this.adListener = object : NormalAdListener(){
                     override fun onAdDisLike() {

+ 0 - 2
app/src/main/java/com/convenient/android/lib/ui/sample/ad/activity/AdNativeTemplateActivity.kt

@@ -14,9 +14,7 @@ import com.convenient.android.common.extension.readyGo
 import com.convenient.android.common.extension.setViewsClick
 import com.convenient.android.common.utils.ToastUtil
 import com.convenient.android.lib.R
-import com.convenient.android.lib.databinding.ActivityAdNativeBinding
 import com.convenient.android.lib.databinding.ActivityAdNativeTemplateBinding
-import com.convenient.android.lib.databinding.ViewAdNativeBinding
 import com.convenient.android.lib.ui.sample.ad.model.Datas
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch

+ 1 - 1
app/src/main/java/com/convenient/android/lib/ui/sample/ad/activity/AppWelcomeActivity.kt

@@ -6,8 +6,8 @@ import com.composition.android.lib.ad.AdLoad
 import com.composition.android.lib.ad.basic.AdResult
 import com.convenient.android.common.base.viewbinding.BaseBindingActivity
 import com.convenient.android.common.extension.readyGo
-import com.convenient.android.lib.databinding.ActivityAppWelcomeBinding
 import com.convenient.android.lib.MainActivity
+import com.convenient.android.lib.databinding.ActivityAppWelcomeBinding
 import com.convenient.android.lib.ui.sample.ad.model.Datas
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch

+ 7 - 5
app/src/main/java/com/convenient/android/lib/ui/sample/ad/model/Datas.kt

@@ -1,14 +1,15 @@
 package com.convenient.android.lib.ui.sample.ad.model
 
 import android.content.Context
-import com.composition.android.ad.csj.impl.CSJBannerAdSize
-import com.composition.android.ad.gromore.load.impl.GMBannerAdSize
+import com.composition.android.ad.csj.util.buildCSJBannerAdSize
+import com.composition.android.ad.gromore.util.buildGroMoreAdImageSize
 import com.composition.android.lib.ad.AdLoad
 import com.composition.android.lib.ad.basic.AdResult
 import com.composition.android.lib.ad.basic.AdType
 import com.composition.android.lib.ad.basic.Advertisers
 import com.composition.android.lib.ad.bean.AdUnitBean
 import com.composition.android.lib.ad.bean.AdUnitLoadConfig
+import com.composition.android.lib.ad.interfaces.AdSize
 import com.convenient.android.common.media.MediaBean
 import com.convenient.android.lib.ui.sample.ad.adapter.NativeAdSampleListAdapter
 import kotlinx.coroutines.flow.firstOrNull
@@ -56,7 +57,7 @@ object Datas {
 
     val CSJAdUnitList: List<AdUnitBean> =
         listOf(
-            AdUnitBean(BANNER, Advertisers.CSJ.name, AdType.BANNER.name, "949726232", adSize = CSJBannerAdSize(600F, 90F)),
+            AdUnitBean(BANNER, Advertisers.CSJ.name, AdType.BANNER.name, "949726232", adSize = AdSize.Builder.create().buildCSJBannerAdSize(600F, 90F)),
             AdUnitBean(NATIVE_TEMPLATE, Advertisers.CSJ.name, AdType.NATIVE_TEMPLATE.name, "949726254"),
             AdUnitBean(INTERSTITIAL, Advertisers.CSJ.name, AdType.INTERSTITIAL.name, "949726267"),
             AdUnitBean(REWARDED_VIDEO, Advertisers.CSJ.name, AdType.REWARDED_VIDEO.name, "949726565"),
@@ -65,7 +66,7 @@ object Datas {
 
     val GroMoreAdUnitList: List<AdUnitBean> =
         listOf(
-            AdUnitBean(BANNER, Advertisers.GroMore.name, AdType.BANNER.name, "102138574", adSize = GMBannerAdSize(600 to 90 /*GroMore的横幅广告为一张图片样式,这里的尺寸最好与GroMore代码位配置的一致*/)),
+            AdUnitBean(BANNER, Advertisers.GroMore.name, AdType.BANNER.name, "102138574", adSize = AdSize.Builder.create().buildGroMoreAdImageSize(600, 90)),
             AdUnitBean(INTERSTITIAL, Advertisers.GroMore.name, AdType.INTERSTITIAL.name, "102138039"),
             AdUnitBean(APP_OPEN, Advertisers.GroMore.name, AdType.APP_OPEN.name, "102137355"),
             AdUnitBean(REWARDED_VIDEO, Advertisers.GroMore.name, AdType.REWARDED_VIDEO.name, "102137844"),
@@ -73,11 +74,12 @@ object Datas {
         )
 
 
+
     suspend fun getNativeADSampleLists(context: Context, adSlotName: String): List<NativeListBean> {
 
         val list = mutableListOf<NativeListBean>()
 
-        var files = (0..20).map {
+        var files = (0..100).map {
             MediaBean(name = "测试:${it}", lastModified = System.currentTimeMillis(), isFile = true, extension = "png", parentPath = "", mediaPath = "")
         }.map {
             NativeListBean(viewType = NativeAdSampleListAdapter.ITEM_NORMAL, media = it)

+ 3 - 1
app/src/main/java/com/convenient/android/lib/ui/sample/ad/page/AdHomePage.kt

@@ -4,7 +4,9 @@ import androidx.compose.foundation.layout.*
 import androidx.compose.material.DropdownMenu
 import androidx.compose.material.DropdownMenuItem
 import androidx.compose.material.RadioButton
-import androidx.compose.material3.*
+import androidx.compose.material3.Button
+import androidx.compose.material3.FilledTonalButton
+import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember

+ 3 - 5
app/src/main/java/com/convenient/android/lib/ui/sample/ad/page/AdInterstitialPage.kt

@@ -9,8 +9,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.material3.FilledTonalButton
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalContext
@@ -93,9 +91,9 @@ private fun loadInterstitial(context: Context, composable: CoroutineScope, resul
 
 
 private fun showInterstitial(activity: ComponentActivity, adResult: AdResult.Success) {
-    AdLoad.showInterstitialAd(activity, success = adResult) {
-
-    }
+//    AdLoad.showInterstitialAd(activity, success = adResult) {
+//
+//    }
 
 }
 

+ 1 - 9
app/src/main/java/com/convenient/android/lib/ui/sample/ad/page/AdMainPage.kt

@@ -1,22 +1,14 @@
 package com.convenient.android.lib.ui.sample.ad.page
 
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.*
-import androidx.compose.runtime.*
-import androidx.compose.ui.Alignment
+import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.RectangleShape
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
 import androidx.lifecycle.viewmodel.compose.viewModel
-import androidx.navigation.NavController
 import androidx.navigation.NavHostController
 import androidx.navigation.compose.composable
 import androidx.navigation.compose.rememberNavController
-import com.composition.android.lib.ad.basic.Advertisers
-import com.convenient.android.lib.ui.sample.ad.model.Datas
 import com.convenient.android.lib.ui.sample.ad.viewmodel.AdConfigViewModel
 import com.convenient.android.lib.ui.theme.SampleTheme
 

+ 0 - 5
app/src/main/java/com/convenient/android/lib/ui/sample/ad/util/CoilImageLoader.kt

@@ -1,9 +1,7 @@
 package com.convenient.android.lib.ui.sample.ad.util
 
 import android.content.Context
-import android.view.View
 import androidx.appcompat.widget.AppCompatImageView
-import coil.Coil
 import coil.load
 import com.composition.android.lib.ad.interfaces.ImageLoader
 
@@ -19,7 +17,4 @@ class CoilImageLoader : ImageLoader<AppCompatImageView> {
         imageView.load(path)
     }
 
-    override fun createImageView(context: Context?): AppCompatImageView {
-        return AppCompatImageView(context!!)
-    }
 }

+ 9 - 9
app/src/main/java/com/convenient/android/lib/ui/sample/ad/viewmodel/AdConfigViewModel.kt

@@ -2,13 +2,13 @@ package com.convenient.android.lib.ui.sample.ad.viewmodel
 
 import androidx.compose.runtime.mutableStateOf
 import androidx.lifecycle.ViewModel
+import com.composition.android.ad.admob.load.AdmobAdLoader
+import com.composition.android.ad.csj.load.CSJAdLoader
+import com.composition.android.ad.gromore.load.GroMoreAdLoader
 import com.composition.android.lib.ad.AdManager
 import com.composition.android.lib.ad.basic.Advertisers
 import com.composition.android.lib.ad.impl.UnknownAdLoader
 import com.composition.android.lib.ad.interfaces.AdLoader
-import com.composition.android.ad.admob.load.AdmobAdLoader
-import com.composition.android.ad.csj.load.CSJAdLoader
-import com.composition.android.ad.gromore.load.GroMoreAdLoader
 
 /**
  * @classname:
@@ -27,15 +27,15 @@ class AdConfigViewModel : ViewModel() {
         } else {
             this.advertisers.value = advertisers
         }
-        AdManager.instance.registerAdLoader(getAdLoader(this.advertisers.value))
+        AdManager.instance.registerAdLoader(getAdLoader(advertisers = this.advertisers.value))
     }
 
-    fun getAdLoader(advertisers: Advertisers): Pair<Advertisers, AdLoader> {
+    fun getAdLoader(advertisers: Advertisers):  AdLoader {
         return when (advertisers) {
-            Advertisers.Admob -> Advertisers.Admob to AdmobAdLoader()
-            Advertisers.CSJ -> Advertisers.CSJ to CSJAdLoader()
-            Advertisers.GroMore -> Advertisers.GroMore to GroMoreAdLoader()
-            else -> Advertisers.UNKNOWN to UnknownAdLoader()
+            Advertisers.Admob ->  AdmobAdLoader()
+            Advertisers.CSJ ->  CSJAdLoader()
+            Advertisers.GroMore ->  GroMoreAdLoader()
+            else -> UnknownAdLoader()
         }
     }
 

+ 0 - 2
app/src/main/java/com/convenient/android/lib/ui/sample/media/MediaFilesPage.kt

@@ -19,8 +19,6 @@ import androidx.compose.material.TextField
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Delete
 import androidx.compose.material3.*
-import androidx.compose.material3.ButtonDefaults
-import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.*
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier

+ 0 - 1
app/src/main/java/com/convenient/android/lib/ui/sample/media/MediaFilesRepository.kt

@@ -1,6 +1,5 @@
 package com.convenient.android.lib.ui.sample.media
 
-import com.convenient.android.common.extension.withIO
 import com.convenient.android.common.media.MediaBean
 import com.convenient.android.common.media.config.MediaQueryConfig
 import com.convenient.android.common.media.scan.FileStore

+ 0 - 4
app/src/main/java/com/convenient/android/lib/ui/sample/media/MediaFilesViewModel.kt

@@ -1,7 +1,6 @@
 package com.convenient.android.lib.ui.sample.media
 
 import android.app.Application
-import android.util.Log
 import androidx.lifecycle.AndroidViewModel
 import androidx.lifecycle.viewModelScope
 import com.convenient.android.common.extension.spGetString
@@ -9,13 +8,10 @@ import com.convenient.android.common.media.MediaBean
 import com.convenient.android.common.media.config.MediaQueryConfig
 import com.convenient.android.common.media.config.MediaSortOrder
 import com.convenient.android.common.media.config.MediaSortType
-import com.convenient.android.common.utils.string.SharedPreferencesSave
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.launch
 import java.io.File
-import java.util.*
-import kotlin.collections.ArrayList
 
 /**
  * @classname:

+ 0 - 2
app/src/main/java/com/convenient/android/lib/ui/theme/Type.kt

@@ -2,10 +2,8 @@ package com.convenient.android.lib.ui.theme
 
 import androidx.compose.material3.Typography
 import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.font.Font
 import androidx.compose.ui.text.font.FontFamily
 import androidx.compose.ui.text.font.FontWeight
-import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 
 //Replace with your font locations

+ 0 - 1
app/src/main/res/layout/activity_ad_banner.xml

@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:orientation="vertical"
     android:layout_height="match_parent">

+ 0 - 1
app/src/main/res/layout/activity_ad_interstitial.xml

@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:orientation="vertical"
     android:layout_height="match_parent">

+ 7 - 7
app/src/main/res/layout/activity_ad_native.xml

@@ -69,13 +69,13 @@
         <com.composition.android.lib.ad.widget.NativeAdView
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            app:native_ad_slot_name="native"
-            app:native_admob_advertisers_root_view="@layout/layout_admob_native_ad_view_root"
-            app:ad_view_title="@id/tv_ad_title_1"
-            app:ad_view_title_desc="@id/tv_ad_title_desc_1"
-            app:ad_view_icon="@id/iv_ad_icon_1"
-            app:ad_view_media_content_group="@id/fl_ad_media_content_1"
-            app:ad_view_call_to_action="@id/btn_call_to_action_1"
+            app:ad_slot_name="native"
+            app:ad_admob_native_ad_view_layout="@layout/layout_admob_native_ad_view_root"
+            app:ad_view_title_id="@id/tv_ad_title_1"
+            app:ad_view_title_desc_id="@id/tv_ad_title_desc_1"
+            app:ad_view_icon_id="@id/iv_ad_icon_1"
+            app:ad_view_media_content_group_id="@id/fl_ad_media_content_1"
+            app:ad_view_call_to_action_id="@id/btn_call_to_action_1"
             >
 
             <androidx.constraintlayout.widget.ConstraintLayout

+ 3 - 4
app/src/main/res/layout/activity_ad_native_template.xml

@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
@@ -68,10 +67,10 @@
         <com.composition.android.lib.ad.widget.NativeAdView
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            app:native_ad_slot_name="NativeTemplate"
+            app:ad_slot_name="NativeTemplate"
             app:ad_auto_populate="true"
-            app:native_admob_advertisers_root_view="@layout/layout_admob_native_ad_view_root"
-            app:native_gro_more_advertisers_root_view="@layout/layout_gm_native_ad_view_root"
+            app:ad_admob_native_ad_view_layout="@layout/layout_admob_native_ad_view_root"
+            app:ad_gro_more_tt_native_ad_view_layout="@layout/layout_gro_more_native_template_ad_view_root"
             >
 
 

+ 8 - 8
app/src/main/res/layout/view_rv_ad_native_item.xml

@@ -11,18 +11,18 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         app:ad_auto_populate="false"
-        app:ad_view_call_to_action="@id/btn_call_to_action_1"
-        app:ad_view_icon="@id/iv_ad_icon_1"
-        app:ad_view_media_content_group="@id/fl_ad_media_content_1"
-        app:ad_view_title="@id/tv_ad_title_1"
-        app:ad_view_title_desc="@id/tv_ad_title_desc_1"
+        app:ad_view_call_to_action_id="@id/btn_call_to_action_1"
+        app:ad_view_icon_id="@id/iv_ad_icon_1"
+        app:ad_view_media_content_group_id="@id/fl_ad_media_content_1"
+        app:ad_view_title_id="@id/tv_ad_title_1"
+        app:ad_view_title_desc_id="@id/tv_ad_title_desc_1"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="parent"
-        app:native_ad_slot_name="native"
-        app:native_admob_advertisers_root_view="@layout/layout_admob_native_ad_view_root"
-        app:native_gro_more_advertisers_root_view="@layout/layout_gm_native_ad_view_root"
+        app:ad_slot_name="native"
+        app:ad_admob_native_ad_view_layout="@layout/layout_admob_native_ad_view_root"
+        app:ad_gro_more_tt_native_ad_view_layout="@layout/layout_gro_more_native_template_ad_view_root"
         >
 
         <androidx.constraintlayout.widget.ConstraintLayout

+ 3 - 4
app/src/main/res/layout/view_rv_ad_native_template_item.xml

@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="wrap_content">
 
@@ -15,9 +14,9 @@
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="parent"
-        app:native_ad_slot_name="NativeTemplate"
-        app:native_admob_advertisers_root_view="@layout/layout_admob_native_ad_view_root"
-        app:native_gro_more_advertisers_root_view="@layout/layout_gm_native_ad_view_root"
+        app:ad_slot_name="NativeTemplate"
+        app:ad_admob_native_ad_view_layout="@layout/layout_admob_native_ad_view_root"
+        app:ad_gro_more_tt_native_ad_view_layout="@layout/layout_gro_more_native_template_ad_view_root"
         >
 
         <androidx.constraintlayout.widget.ConstraintLayout

+ 1 - 1
app/src/main/res/values-night/themes.xml

@@ -1,4 +1,4 @@
-<resources xmlns:tools="http://schemas.android.com/tools">
+<resources>
     <!-- Base application theme. -->
     <style name="Theme.Lib" parent="Theme.MaterialComponents.DayNight.NoActionBar">
         <!-- Primary brand color. -->

+ 1 - 1
app/src/main/res/values/themes.xml

@@ -1,4 +1,4 @@
-<resources xmlns:tools="http://schemas.android.com/tools">
+<resources>
     <!-- Base application theme. -->
     <style name="Theme.Lib" parent="Theme.MaterialComponents.Light.NoActionBar">
         <!-- Primary brand color. -->

+ 26 - 3
lib_ad_admob/build.gradle

@@ -3,6 +3,11 @@ plugins {
     id 'org.jetbrains.kotlin.android'
 }
 
+def version = '1.0.0'
+def releaseTime() {
+    return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
+}
+
 android {
     compileSdk = rootProject.ext.compileSdkVersion
     buildToolsVersion rootProject.ext.buildToolsVersion
@@ -12,13 +17,31 @@ android {
         targetSdkVersion rootProject.ext.targetSdkVersion
 
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
-        consumerProguardFiles "consumer-rules.pro"
+        consumerProguardFiles "proguard-rules.pro"
     }
 
     buildTypes {
+
+        debug{
+            libraryVariants.all { variant->
+                variant.outputs.all { output ->
+                    if (outputFile != null && outputFileName.endsWith('.aar')) {
+                        outputFileName = "ad_admob_${version}_${releaseTime()}_release.aar"
+                    }
+                }
+            }
+        }
+
         release {
-            minifyEnabled false
+            minifyEnabled true
             proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+            libraryVariants.all { variant->
+                variant.outputs.all { output ->
+                    if (outputFile != null && outputFileName.endsWith('.aar')) {
+                        outputFileName = "ad_admob_${version}_${releaseTime()}_release.aar"
+                    }
+                }
+            }
         }
     }
     compileOptions {
@@ -33,7 +56,7 @@ android {
 dependencies {
     implementation project(':lib_ad_core')
 
-    api 'com.google.android.gms:play-services-ads:21.2.0'
+    implementation 'com.google.android.gms:play-services-ads:21.3.0'
 
     implementation 'androidx.core:core-ktx:1.8.0'
     implementation 'androidx.appcompat:appcompat:1.5.1'

+ 36 - 1
lib_ad_admob/proguard-rules.pro

@@ -18,4 +18,39 @@
 
 # If you keep the line number information, uncomment this to
 # hide the original source file name.
-#-renamesourcefileattribute SourceFile
+#-renamesourcefileattribute SourceFile
+
+
+# 代码混淆压缩比,在0~7之间,默认为5,一般不做修改
+-optimizationpasses 5
+
+# 混合时不使用大小写混合,混合后的类名为小写
+-dontusemixedcaseclassnames
+
+# 指定不去忽略非公共库的类
+-dontskipnonpubliclibraryclasses
+
+# 这句话能够使我们的项目混淆后产生映射文件
+# 包含有类名->混淆后类名的映射关系
+-verbose
+
+# 指定不去忽略非公共库的类成员
+-dontskipnonpubliclibraryclassmembers
+
+# 不做预校验,preverify是proguard的四个步骤之一,Android不需要preverify,去掉这一步能够加快混淆速度。
+-dontpreverify
+
+# 保留Annotation不混淆
+-keepattributes *Annotation*,InnerClasses
+
+# 避免混淆泛型
+-keepattributes Signature
+
+# 抛出异常时保留代码行号
+-keepattributes SourceFile,LineNumberTable
+
+# 指定混淆是采用的算法,后面的参数是一个过滤器
+# 这个过滤器是谷歌推荐的算法,一般不做更改
+-optimizations !code/simplification/cast,!field/*,!class/merging/*
+-keep class com.composition.android.ad.admob.** {*;}
+-keep interface com.composition.android.ad.admob.**{*;}

+ 2 - 4
lib_ad_admob/src/androidTest/java/com/composition/android/ad/admob/ExampleInstrumentedTest.kt

@@ -1,13 +1,11 @@
 package com.composition.android.ad.admob
 
-import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.ext.junit.runners.AndroidJUnit4
-
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.Assert.*
 import org.junit.Test
 import org.junit.runner.RunWith
 
-import org.junit.Assert.*
-
 /**
  * Instrumented test, which will execute on an Android device.
  *

+ 0 - 1
lib_ad_admob/src/main/java/com/composition/android/ad/admob/impl/AdmobAdSize.kt

@@ -1,6 +1,5 @@
 package com.composition.android.ad.admob.impl
 
-import com.composition.android.lib.ad.AdManager
 import com.composition.android.lib.ad.interfaces.IAdSize
 import com.google.android.gms.ads.AdSize
 

+ 86 - 62
lib_ad_admob/src/main/java/com/composition/android/ad/admob/load/AdmobAdLoader.kt

@@ -4,23 +4,24 @@ import android.app.Activity
 import android.content.Context
 import android.view.View
 import android.view.ViewGroup
+import com.composition.android.ad.admob.load.request.*
+import com.composition.android.ad.admob.load.view.AdmobBannerView
+import com.composition.android.ad.admob.load.view.AdmobNativeAdView
 import com.composition.android.lib.ad.AdManager
+import com.composition.android.lib.ad.basic.*
 import com.composition.android.lib.ad.bean.AdUnitBean
 import com.composition.android.lib.ad.interfaces.AdListener
 import com.composition.android.lib.ad.interfaces.AdLoader
-import com.composition.android.lib.ad.util.adLogE
+import com.composition.android.lib.ad.util.adLog
 import com.google.android.gms.ads.AdError
+import com.google.android.gms.ads.AdView
 import com.google.android.gms.ads.FullScreenContentCallback
 import com.google.android.gms.ads.appopen.AppOpenAd
 import com.google.android.gms.ads.interstitial.InterstitialAd
+import com.google.android.gms.ads.nativead.NativeAd
 import com.google.android.gms.ads.nativead.NativeAdView
 import com.google.android.gms.ads.rewarded.RewardedAd
 import com.google.android.gms.ads.rewardedinterstitial.RewardedInterstitialAd
-import com.composition.android.ad.admob.load.request.*
-import com.composition.android.ad.admob.load.view.AdmobBannerView
-import com.composition.android.ad.admob.load.view.AdmobNativeAdView
-import com.composition.android.lib.ad.basic.*
-import com.composition.android.lib.ad.util.adLog
 
 /**
  * @classname:
@@ -30,66 +31,69 @@ import com.composition.android.lib.ad.util.adLog
  */
 class AdmobAdLoader : AdLoader {
 
+    override val advertisers: Advertisers
+        get() = Advertisers.Admob
 
     override suspend fun load(context: Context, adUnitBean: AdUnitBean): AdResult {
-        if (adUnitBean.isAvailable().not()){
+        if (adUnitBean.isAvailable().not()) {
             return AdResult.Fail(adUnitBean, msg = "AdUnitBean无效")
         }
         //获取到具体的广告格式加载类
-        val adFormatRequestImpl = when(adUnitBean.getAdType()){
-            AdType.REWARDED_VIDEO-> AdmobRewardRequestImpl()
-            AdType.INTERSTITIAL-> AdmobInterstitialRequestImpl()
+        val adFormatRequestImpl = when (adUnitBean.getAdType()) {
+            AdType.REWARDED_VIDEO -> AdmobRewardRequestImpl()
+            AdType.INTERSTITIAL -> AdmobInterstitialRequestImpl()
             AdType.REWARDED_INTERSTITIAL -> AdmobRewardInterstitialRequestImpl()
             AdType.APP_OPEN -> AdmobAppOpenRequestImpl()
-            AdType.BANNER-> AdmobBannerRequestImpl()
-            AdType.NATIVE-> AdMobNativeRequestImpl()
-            else-> null
+            AdType.BANNER -> AdmobBannerRequestImpl()
+            AdType.NATIVE -> AdMobNativeRequestImpl()
+            else -> null
         }
         //根据策略进行加载
         return loadByStrategy(context, adUnitBean, adFormatRequestImpl)
     }
 
     override fun getBannerView(context: Context, adResult: AdResult.Success, viewHolder: BannerAdViewHolder): BasicAdView<*>? {
-        return if (adResult.adObject != null){
+        return if (adResult.adObject != null) {
             AdmobBannerView(context).apply {
                 setBannerAdViewHolder(viewHolder)
                 render(adResult)
                 AdManager.instance.globalListener?.invoke(adResult.adBean)
             }
-        }else{
+        } else {
             null
         }
     }
 
     override fun getNativeAdView(context: Context, adResult: AdResult.Success, viewHolder: NativeAdViewHolder): View? {
-        return if (adResult.adObject != null){
+        return if (adResult.adObject != null) {
             AdmobNativeAdView(context).apply {
                 setNativeAdViewHolder(viewHolder)
                 render(adResult)
                 AdManager.instance.globalListener?.invoke(adResult.adBean)
             }
-        }else{
+        } else {
             null
         }
     }
 
-    override fun populateNativeAdView(context: Context, adResult: AdResult.Success, viewHolder: NativeAdViewHolder, adRootView: View) {
-        if (adRootView is NativeAdView){
-            AdManager.instance.globalListener?.invoke(adResult.adBean)
-            AdmobNativeAdView.populateNativeAdView(context, adResult, viewHolder, adRootView)
-        }else{
-            adLogE(AdManager.TAG, "Admob原生广告填充失败,adRootView 类型错误,根布局应该为:NativeAdView")
+
+    override fun populateNativeAdView(context: Context, adResult: AdResult.Success, viewHolder: NativeAdViewHolder, advertisersContainerView: ViewGroup) {
+        AdManager.instance.globalListener?.invoke(adResult.adBean)
+        if (advertisersContainerView is NativeAdView) {
+            AdmobNativeAdView.populateNativeAdView(context, adResult, viewHolder, advertisersContainerView)
+        } else {
+            adLog(msg = "Admob广告-填充原生-advertisersContainerView类型错误, 应该为NativeAdView")
         }
     }
 
     /**
      * 显示插屏广告
      */
-    override fun showInterstitialAd(activity: Activity, success: AdResult.Success, adListener: AdListener) {
+    override fun showInterstitialAd(activity: Activity, adResult: AdResult.Success, adListener: AdListener) {
 
-        if (success.adObject is InterstitialAd){
+        if (adResult.adObject is InterstitialAd) {
 
-            val interstitialAd = success.adObject as InterstitialAd
+            val interstitialAd = adResult.adObject as InterstitialAd
             interstitialAd.fullScreenContentCallback = object : FullScreenContentCallback() {
                 override fun onAdDismissedFullScreenContent() {
                     super.onAdDismissedFullScreenContent()
@@ -106,17 +110,19 @@ class AdmobAdLoader : AdLoader {
                 override fun onAdShowedFullScreenContent() {
                     super.onAdShowedFullScreenContent()
                     adLog(msg = "Admob广告-插屏-展示")
-                    adListener.onAdShow(adUnitBean = success.adBean)
-                    AdManager.instance.globalListener?.invoke(success.adBean)
+                    adListener.onAdShow(adUnitBean = adResult.adBean)
+                    AdManager.instance.globalListener?.invoke(adResult.adBean)
                 }
             }
             interstitialAd.setOnPaidEventListener {
 
             }
             interstitialAd.show(activity)
-        }else{
-            adLog(msg = "Admob广告-插屏-异常\n" +
-                    "类型错误,当前类型为:${success.adObject?.javaClass?.simpleName}, 应为:InterstitialAd")
+        } else {
+            adLog(
+                msg = "Admob广告-插屏-异常\n" +
+                        "类型错误,当前类型为:${adResult.adObject?.javaClass?.simpleName}, 应为:InterstitialAd"
+            )
             adListener.onAdLoadedFail()
         }
     }
@@ -124,9 +130,9 @@ class AdmobAdLoader : AdLoader {
     /**
      * 显示激励广告
      */
-    override fun showRewardedAd(activity: Activity, success: AdResult.Success, adListener: AdListener) {
-        if (success.adObject is RewardedAd) {
-            val adObj = success.adObject as RewardedAd
+    override fun showRewardedAd(activity: Activity, adResult: AdResult.Success, adListener: AdListener) {
+        if (adResult.adObject is RewardedAd) {
+            val adObj = adResult.adObject as RewardedAd
             adObj.fullScreenContentCallback = object : FullScreenContentCallback() {
 
                 override fun onAdDismissedFullScreenContent() {
@@ -142,8 +148,8 @@ class AdmobAdLoader : AdLoader {
 
                 override fun onAdShowedFullScreenContent() {
                     adLog(msg = "Admob广告-激励-展示")
-                    adListener.onAdShow(success.adBean)
-                    AdManager.instance.globalListener?.invoke(success.adBean)
+                    adListener.onAdShow(adResult.adBean)
+                    AdManager.instance.globalListener?.invoke(adResult.adBean)
                 }
             }
             adObj.setOnPaidEventListener {
@@ -151,16 +157,18 @@ class AdmobAdLoader : AdLoader {
             adObj.show(activity) {
                 adListener.onAdRewarded()
             }
-        }else{
-            adLog(msg = "Admob广告-激励-异常\n" +
-                    "类型错误,当前类型为:${success.adObject?.javaClass?.simpleName}, 应为:RewardedAd")
-            adListener.onAdShowFail("类型错误,当前类型为:${success.adObject?.javaClass?.simpleName}, 应为:RewardedAd")
+        } else {
+            adLog(
+                msg = "Admob广告-激励-异常\n" +
+                        "类型错误,当前类型为:${adResult.adObject?.javaClass?.simpleName}, 应为:RewardedAd"
+            )
+            adListener.onAdShowFail("类型错误,当前类型为:${adResult.adObject?.javaClass?.simpleName}, 应为:RewardedAd")
         }
     }
 
-    override fun showRewardedInterstitialAd(activity: Activity, success: AdResult.Success, adListener: AdListener) {
-        if (success.adObject is RewardedInterstitialAd) {
-            val rewardedInterstitialAd = success.adObject as RewardedInterstitialAd
+    override fun showRewardedInterstitialAd(activity: Activity, adResult: AdResult.Success, adListener: AdListener) {
+        if (adResult.adObject is RewardedInterstitialAd) {
+            val rewardedInterstitialAd = adResult.adObject as RewardedInterstitialAd
             rewardedInterstitialAd.fullScreenContentCallback = object : FullScreenContentCallback() {
                 override fun onAdDismissedFullScreenContent() {
                     adLog(msg = "Admob广告-激励插屏-关闭")
@@ -175,8 +183,8 @@ class AdmobAdLoader : AdLoader {
 
                 override fun onAdShowedFullScreenContent() {
                     adLog(msg = "Admob广告-激励插屏-展示")
-                    adListener.onAdShow(success.adBean)
-                    AdManager.instance.globalListener?.invoke(success.adBean)
+                    adListener.onAdShow(adResult.adBean)
+                    AdManager.instance.globalListener?.invoke(adResult.adBean)
                 }
             }
             rewardedInterstitialAd.setOnPaidEventListener {
@@ -184,10 +192,12 @@ class AdmobAdLoader : AdLoader {
             rewardedInterstitialAd.show(activity) {
                 adListener.onAdRewarded()
             }
-        }else{
-            adLog(msg = "Admob广告-激励插屏-异常\n" +
-                    "类型错误,当前类型为:${success.adObject?.javaClass?.simpleName}, 应为:RewardedInterstitialAd")
-            adListener.onAdShowFail("类型错误,当前类型为:${success.adObject?.javaClass?.simpleName}, 应为:RewardedInterstitialAd")
+        } else {
+            adLog(
+                msg = "Admob广告-激励插屏-异常\n" +
+                        "类型错误,当前类型为:${adResult.adObject?.javaClass?.simpleName}, 应为:RewardedInterstitialAd"
+            )
+            adListener.onAdShowFail("类型错误,当前类型为:${adResult.adObject?.javaClass?.simpleName}, 应为:RewardedInterstitialAd")
         }
     }
 
@@ -196,20 +206,20 @@ class AdmobAdLoader : AdLoader {
      * 包括:
      * 插屏广告,激励视频广告,激励插屏广告
      */
-    override fun showFullScreenAd(activity: Activity, success: AdResult.Success, adListener: AdListener) {
-        when(success.adBean.getAdType()){
-            AdType.INTERSTITIAL-> showInterstitialAd(activity, success, adListener)
-            AdType.REWARDED_VIDEO -> showRewardedAd(activity, success, adListener)
-            AdType.REWARDED_INTERSTITIAL -> showRewardedInterstitialAd(activity, success, adListener)
-            else-> adListener.onAdShowFail("无对应广告类型的广告进行展示 AdType:${success.adBean.adType}")
+    override fun showFullScreenAd(activity: Activity, adResult: AdResult.Success, adListener: AdListener) {
+        when (adResult.adBean.getAdType()) {
+            AdType.INTERSTITIAL -> showInterstitialAd(activity, adResult, adListener)
+            AdType.REWARDED_VIDEO -> showRewardedAd(activity, adResult, adListener)
+            AdType.REWARDED_INTERSTITIAL -> showRewardedInterstitialAd(activity, adResult, adListener)
+            else -> adListener.onAdShowFail("无对应广告类型的广告进行展示 AdType:${adResult.adBean.adType}")
         }
     }
 
     /**
      * 显示开屏广告
      */
-    override fun showSplashAd(activity: Activity, splashViewGroup: ViewGroup?, success: AdResult.Success, adListener: AdListener) {
-        val appOpenAd = success.adObject as? AppOpenAd
+    override fun showSplashAd(activity: Activity, splashViewGroup: ViewGroup?, adResult: AdResult.Success, adListener: AdListener) {
+        val appOpenAd = adResult.adObject as? AppOpenAd
         if (appOpenAd != null) {
             val callback: FullScreenContentCallback = object : FullScreenContentCallback() {
                 override fun onAdDismissedFullScreenContent() {
@@ -224,21 +234,35 @@ class AdmobAdLoader : AdLoader {
 
                 override fun onAdShowedFullScreenContent() {
                     adLog(msg = "Admob广告-开屏-展示")
-                    adListener.onAdShow(success.adBean)
-                    AdManager.instance.globalListener?.invoke(success.adBean)
+                    adListener.onAdShow(adResult.adBean)
+                    AdManager.instance.globalListener?.invoke(adResult.adBean)
                 }
             }
             appOpenAd.fullScreenContentCallback = callback
             appOpenAd.setOnPaidEventListener {
             }
             appOpenAd.show(activity)
-        }else{
+        } else {
             adLog(msg = "Admob广告-开屏-异常-广告实例为空")
-            adListener?.onAdShowFail("Admob广告-开屏-异常-广告实例为空")
+            adListener.onAdShowFail("Admob广告-开屏-异常-广告实例为空")
         }
     }
 
 
-
+    override fun destroyAd(adResult: AdResult) {
+        try {
+            if (adResult is AdResult.Success) {
+                when (val ad = adResult.adObject) {
+                    is AdView -> ad.destroy()
+                    is NativeAd -> ad.destroy()
+                    else -> {}
+                }
+                adResult.adObject = null
+            }
+            adLog(msg = "Admob广告-销毁广告对象完成")
+        }catch (e : Exception){
+            e.printStackTrace()
+        }
+    }
 
 }

+ 3 - 3
lib_ad_admob/src/main/java/com/composition/android/ad/admob/load/request/AdMobNativeRequestImpl.kt

@@ -1,16 +1,16 @@
 package com.composition.android.ad.admob.load.request
 
 import android.content.Context
-import com.composition.android.ad.admob.util.printAdFailInfo
 import com.composition.android.lib.ad.basic.AdLoadCode
 import com.composition.android.lib.ad.basic.AdResult
 import com.composition.android.lib.ad.bean.AdUnitBean
 import com.composition.android.lib.ad.interfaces.IAdFormatRequest
+import com.composition.android.lib.ad.util.printAdFailInfo
+import com.composition.android.lib.ad.util.printAdInfo
 import com.google.android.gms.ads.AdListener
 import com.google.android.gms.ads.AdLoader
 import com.google.android.gms.ads.AdRequest
 import com.google.android.gms.ads.LoadAdError
-import com.composition.android.ad.admob.util.printAdInfo
 import kotlinx.coroutines.suspendCancellableCoroutine
 import kotlin.coroutines.resume
 
@@ -36,7 +36,7 @@ class AdMobNativeRequestImpl : IAdFormatRequest{
             }
             val adLoader = AdLoader.Builder(context, adUnitBean.adUnitId)
                 .forNativeAd {nativeAd->
-                    printAdInfo(adUnitBean, nativeAd.responseInfo)
+                    printAdInfo(adUnitBean)
                     if (it.isActive) {
                         it.resume(
                             AdResult.Success(

+ 3 - 5
lib_ad_admob/src/main/java/com/composition/android/ad/admob/load/request/AdmobAppOpenRequestImpl.kt

@@ -1,16 +1,14 @@
 package com.composition.android.ad.admob.load.request
 
 import android.content.Context
-import com.composition.android.ad.admob.util.printAdFailInfo
-import com.composition.android.lib.ad.AdManager
 import com.composition.android.lib.ad.basic.AdLoadCode
 import com.composition.android.lib.ad.basic.AdResult
 import com.composition.android.lib.ad.bean.AdUnitBean
 import com.composition.android.lib.ad.interfaces.IAdFormatRequest
-import com.composition.android.lib.ad.util.adLogE
+import com.composition.android.lib.ad.util.printAdFailInfo
+import com.composition.android.lib.ad.util.printAdInfo
 import com.google.android.gms.ads.LoadAdError
 import com.google.android.gms.ads.appopen.AppOpenAd
-import com.composition.android.ad.admob.util.printAdInfo
 import kotlinx.coroutines.suspendCancellableCoroutine
 import kotlin.coroutines.resume
 
@@ -41,7 +39,7 @@ class AdmobAppOpenRequestImpl : IAdFormatRequest {
 
                     }
                     override fun onAdLoaded(p0: AppOpenAd) {
-                        printAdInfo(adUnitBean, p0.responseInfo)
+                        printAdInfo(adUnitBean)
                         if (it.isActive) {
                             it.resume(
                                 AdResult.Success(

+ 5 - 7
lib_ad_admob/src/main/java/com/composition/android/ad/admob/load/request/AdmobBannerRequestImpl.kt

@@ -1,19 +1,17 @@
 package com.composition.android.ad.admob.load.request
 
 import android.content.Context
-import com.composition.android.lib.ad.AdManager
+import com.composition.android.ad.admob.impl.AdmobAdSize
+import com.composition.android.ad.admob.util.getAdSize
 import com.composition.android.lib.ad.basic.AdLoadCode
 import com.composition.android.lib.ad.basic.AdResult
 import com.composition.android.lib.ad.bean.AdUnitBean
 import com.composition.android.lib.ad.interfaces.IAdFormatRequest
-import com.composition.android.lib.ad.util.adLogE
+import com.composition.android.lib.ad.util.printAdFailInfo
+import com.composition.android.lib.ad.util.printAdInfo
 import com.google.android.gms.ads.AdListener
 import com.google.android.gms.ads.AdView
 import com.google.android.gms.ads.LoadAdError
-import com.composition.android.ad.admob.impl.AdmobAdSize
-import com.composition.android.ad.admob.util.getAdSize
-import com.composition.android.ad.admob.util.printAdFailInfo
-import com.composition.android.ad.admob.util.printAdInfo
 import kotlinx.coroutines.suspendCancellableCoroutine
 import kotlin.coroutines.resume
 
@@ -40,7 +38,7 @@ class AdmobBannerRequestImpl : IAdFormatRequest {
             val listener: AdListener = object : AdListener() {
                 override fun onAdLoaded() {
                     super.onAdLoaded()
-                    printAdInfo(adUnitBean, adView.responseInfo)
+                    printAdInfo(adUnitBean)
                     if (it.isActive) {
                         it.resume(
                             AdResult.Success(

+ 3 - 3
lib_ad_admob/src/main/java/com/composition/android/ad/admob/load/request/AdmobInterstitialRequestImpl.kt

@@ -1,15 +1,15 @@
 package com.composition.android.ad.admob.load.request
 
 import android.content.Context
-import com.composition.android.ad.admob.util.printAdFailInfo
 import com.composition.android.lib.ad.basic.AdLoadCode
 import com.composition.android.lib.ad.basic.AdResult
 import com.composition.android.lib.ad.bean.AdUnitBean
 import com.composition.android.lib.ad.interfaces.IAdFormatRequest
+import com.composition.android.lib.ad.util.printAdFailInfo
+import com.composition.android.lib.ad.util.printAdInfo
 import com.google.android.gms.ads.LoadAdError
 import com.google.android.gms.ads.interstitial.InterstitialAd
 import com.google.android.gms.ads.interstitial.InterstitialAdLoadCallback
-import com.composition.android.ad.admob.util.printAdInfo
 import kotlinx.coroutines.suspendCancellableCoroutine
 import kotlin.coroutines.resume
 
@@ -26,7 +26,7 @@ class AdmobInterstitialRequestImpl : IAdFormatRequest {
         return suspendCancellableCoroutine {
             val listener: InterstitialAdLoadCallback = object : InterstitialAdLoadCallback() {
                 override fun onAdLoaded(p0: InterstitialAd) {
-                    printAdInfo(adUnitBean, p0.responseInfo)
+                    printAdInfo(adUnitBean)
                     if (it.isActive){
                         it.resume(
                             AdResult.Success(

+ 4 - 4
lib_ad_admob/src/main/java/com/composition/android/ad/admob/load/request/AdmobRewardInterstitialRequestImpl.kt

@@ -1,16 +1,16 @@
 package com.composition.android.ad.admob.load.request
 
 import android.content.Context
+import com.composition.android.ad.admob.util.getFailedCodeInfo
 import com.composition.android.lib.ad.basic.AdLoadCode
 import com.composition.android.lib.ad.basic.AdResult
 import com.composition.android.lib.ad.bean.AdUnitBean
 import com.composition.android.lib.ad.interfaces.IAdFormatRequest
+import com.composition.android.lib.ad.util.printAdFailInfo
+import com.composition.android.lib.ad.util.printAdInfo
 import com.google.android.gms.ads.LoadAdError
 import com.google.android.gms.ads.rewardedinterstitial.RewardedInterstitialAd
 import com.google.android.gms.ads.rewardedinterstitial.RewardedInterstitialAdLoadCallback
-import com.composition.android.ad.admob.util.getFailedCodeInfo
-import com.composition.android.ad.admob.util.printAdFailInfo
-import com.composition.android.ad.admob.util.printAdInfo
 import kotlinx.coroutines.suspendCancellableCoroutine
 import kotlin.coroutines.resume
 
@@ -28,7 +28,7 @@ class AdmobRewardInterstitialRequestImpl : IAdFormatRequest {
             val listener: RewardedInterstitialAdLoadCallback =
                 object : RewardedInterstitialAdLoadCallback() {
                     override fun onAdLoaded(p0: RewardedInterstitialAd) {
-                        printAdInfo(adUnitBean, p0.responseInfo)
+                        printAdInfo(adUnitBean)
                         if (it.isActive) {
                             it.resume(
                                 AdResult.Success(

+ 3 - 3
lib_ad_admob/src/main/java/com/composition/android/ad/admob/load/request/AdmobRewardRequestImpl.kt

@@ -1,15 +1,15 @@
 package com.composition.android.ad.admob.load.request
 
 import android.content.Context
-import com.composition.android.ad.admob.util.printAdFailInfo
 import com.composition.android.lib.ad.basic.AdLoadCode
 import com.composition.android.lib.ad.basic.AdResult
 import com.composition.android.lib.ad.bean.AdUnitBean
 import com.composition.android.lib.ad.interfaces.IAdFormatRequest
+import com.composition.android.lib.ad.util.printAdFailInfo
+import com.composition.android.lib.ad.util.printAdInfo
 import com.google.android.gms.ads.LoadAdError
 import com.google.android.gms.ads.rewarded.RewardedAd
 import com.google.android.gms.ads.rewarded.RewardedAdLoadCallback
-import com.composition.android.ad.admob.util.printAdInfo
 import kotlinx.coroutines.suspendCancellableCoroutine
 import kotlin.coroutines.resume
 
@@ -33,7 +33,7 @@ class AdmobRewardRequestImpl : IAdFormatRequest {
                 }
 
                 override fun onAdLoaded(p0: RewardedAd) {
-                    printAdInfo(adUnitBean, p0.responseInfo)
+                    printAdInfo(adUnitBean)
                     if (it.isActive) {
                         it.resume(
                             AdResult.Success(

+ 0 - 1
lib_ad_admob/src/main/java/com/composition/android/ad/admob/load/view/AdmobBannerView.kt

@@ -7,7 +7,6 @@ import com.composition.android.lib.ad.basic.BasicAdView
 import com.composition.android.lib.ad.util.adLog
 import com.google.android.gms.ads.AdListener
 import com.google.android.gms.ads.AdView
-import com.google.android.gms.ads.LoadAdError
 
 /**
  * @classname:

+ 49 - 58
lib_ad_admob/src/main/java/com/composition/android/ad/admob/load/view/AdmobNativeAdView.kt

@@ -6,13 +6,13 @@ import android.view.View
 import androidx.appcompat.widget.AppCompatImageView
 import androidx.appcompat.widget.AppCompatTextView
 import androidx.core.view.isVisible
+import com.composition.android.ad.admob.R
 import com.composition.android.lib.ad.basic.AdResult
 import com.composition.android.lib.ad.basic.BasicAdView
 import com.composition.android.lib.ad.basic.NativeAdViewHolder
 import com.google.android.gms.ads.nativead.MediaView
 import com.google.android.gms.ads.nativead.NativeAd
 import com.google.android.gms.ads.nativead.NativeAdView
-import com.composition.android.ad.admob.R
 
 /**
  * @classname:
@@ -34,8 +34,10 @@ class AdmobNativeAdView(context: Context?) : BasicAdView<NativeAdView>(context)
         if (null == adResult || nativeAdViewHolder == null) {
             return null
         }
-        return populateNativeAdView(context, adResult = adResult!!, nativeAdViewHolder = nativeAdViewHolder!!)
-
+        val nativeAdView = LayoutInflater.from(context).inflate(R.layout.layout_admob_native_ad_view_root, null) as NativeAdView
+        nativeAdView.addView(nativeAdViewHolder!!.customLayoutRootView)
+        populateNativeAdView(context, adResult!!, nativeAdViewHolder!!, nativeAdView)
+        return nativeAdView
     }
 
 
@@ -52,74 +54,63 @@ class AdmobNativeAdView(context: Context?) : BasicAdView<NativeAdView>(context)
     }
 
 
-    companion object{
-
-
-        /**
-         * 填充原生广告
-         * @param context 上下文
-         * @param adResult 请求成功的广告结果
-         * @param nativeAdViewHolder 填充广告的viewHolder
-         */
-        fun populateNativeAdView(context: Context, adResult: AdResult.Success, nativeAdViewHolder: NativeAdViewHolder) : NativeAdView {
-            val nativeAdView = LayoutInflater.from(context).inflate(R.layout.layout_admob_native_ad_view_root,null) as NativeAdView
-            populateNativeAdView(context, adResult, nativeAdViewHolder, nativeAdView)
-            return nativeAdView
-        }
+    companion object {
 
 
         fun populateNativeAdView(context: Context, success: AdResult.Success, nativeAdViewHolder: NativeAdViewHolder, nativeAdView: NativeAdView) {
+            try {
+                val nativeAd = success.adObject as NativeAd
+                val mediaView = LayoutInflater.from(context).inflate(R.layout.layout_admob_native_media_view, null) as MediaView
 
-            val nativeAd = success.adObject as NativeAd
-            val mediaView = LayoutInflater.from(context).inflate(R.layout.layout_admob_native_media_view, null) as MediaView
-
-            if (nativeAdView != nativeAdViewHolder.rootView && nativeAdViewHolder.rootView != null){
-               nativeAdView.addView(nativeAdViewHolder.rootView)
-            }
-            nativeAdViewHolder.titleView?.let {
-                it.text = nativeAd.headline
-                nativeAdView.headlineView = it
-            }
+                nativeAdViewHolder.titleView?.let {
+                    it.text = nativeAd.headline
+                    nativeAdView.headlineView = it
+                }
 
-            nativeAdViewHolder.contentMediaGroup?.let {
-                it.removeAllViews()
-                it.addView(mediaView)
-                nativeAdView.mediaView = mediaView
+                nativeAdViewHolder.contentMediaViewGroup?.let {
+                    it.removeAllViews()
+                    it.addView(mediaView)
+                    nativeAdView.mediaView = mediaView
+                    nativeAd.mediaContent?.let { it1 -> mediaView.mediaContent = it1 }
+                }
 
-            }
-            if (nativeAd.callToAction.isNullOrEmpty()) {
-                nativeAdViewHolder.callActionView?.isVisible = false
-            } else {
-                nativeAdViewHolder.callActionView?.isVisible = true
-                nativeAdViewHolder.callActionView?.let {
-                    it.text = nativeAd.callToAction
-                    nativeAdView.callToActionView = it
+                if (nativeAd.callToAction.isNullOrEmpty()) {
+                    nativeAdViewHolder.callActionButtonView?.isVisible = false
+                } else {
+                    nativeAdViewHolder.callActionButtonView?.isVisible = true
+                    nativeAdViewHolder.callActionButtonView?.let {
+                        it.text = nativeAd.callToAction
+                        nativeAdView.callToActionView = it
+                    }
                 }
-            }
-            if (nativeAd.icon == null) {
-                nativeAdView.iconView?.visibility = View.GONE
-            } else {
-                nativeAdView.iconView?.visibility = View.VISIBLE
-                nativeAd.icon?.let { icon ->
-                    (nativeAdViewHolder.iconView as? AppCompatImageView)?.let {
-                        it.setImageDrawable(icon.drawable)
-                        nativeAdView.iconView = it
+                if (nativeAd.icon == null) {
+                    nativeAdView.iconView?.visibility = View.GONE
+                } else {
+                    nativeAdView.iconView?.visibility = View.VISIBLE
+                    nativeAd.icon?.let { icon ->
+                        (nativeAdViewHolder.iconView as? AppCompatImageView)?.let {
+                            it.setImageDrawable(icon.drawable)
+                            nativeAdView.iconView = it
+                        }
                     }
                 }
-            }
 
-            if (nativeAd.body == null) {
-                nativeAdViewHolder.titleDescView?.visibility = View.INVISIBLE
-            } else {
-                nativeAdViewHolder.titleDescView?.visibility = View.VISIBLE
-                (nativeAdViewHolder.titleDescView as? AppCompatTextView)?.let {
-                    it.text = nativeAd.body
-                    nativeAdView.bodyView = it
+                if (nativeAd.body == null) {
+                    nativeAdViewHolder.titleDescView?.visibility = View.INVISIBLE
+                } else {
+                    nativeAdViewHolder.titleDescView?.visibility = View.VISIBLE
+                    (nativeAdViewHolder.titleDescView as? AppCompatTextView)?.let {
+                        it.text = nativeAd.body
+                        nativeAdView.bodyView = it
+                    }
                 }
+                nativeAdViewHolder.dislikeView?.isVisible = false
+                nativeAdViewHolder.advertiserLogoImageView?.isVisible = false
+                nativeAdView.setNativeAd(nativeAd)
+            } catch (e: Exception) {
+
             }
-            nativeAdView.setNativeAd(nativeAd)
         }
-
     }
 }
 

+ 22 - 44
lib_ad_admob/src/main/java/com/composition/android/ad/admob/util/AdmobAdExpan.kt

@@ -1,14 +1,12 @@
 package com.composition.android.ad.admob.util
 
 import android.content.Context
-import com.composition.android.lib.ad.AdManager
-import com.composition.android.lib.ad.bean.AdUnitBean
-import com.composition.android.lib.ad.util.adLog
-import com.convenient.android.common.extension.getDensity
-import com.convenient.android.common.extension.getScreenWidth
+import com.composition.android.ad.admob.impl.AdmobAdSize
+import com.composition.android.lib.ad.interfaces.IAdSize
+import com.composition.android.lib.ad.util.getDensity
+import com.composition.android.lib.ad.util.getScreenWidth
 import com.google.android.gms.ads.AdRequest
 import com.google.android.gms.ads.AdSize
-import com.google.android.gms.ads.ResponseInfo
 
 
 /**
@@ -30,52 +28,32 @@ internal fun getFailedCodeInfo(code: Int): String {
 
 
 
-internal fun printAdFailInfo(code : Int, msg : String?){
-    adLog(
-        AdManager.TAG,
-        "-------------------------------------------------------------------"
-    )
-
-    adLog(AdManager.TAG, "广告加载失败")
-    adLog(AdManager.TAG, "Code:${code}")
-    adLog(AdManager.TAG, "错误信息:${msg}")
-
-    adLog(
-        AdManager.TAG,
-        "-------------------------------------------------------------------"
-    )
-}
-
-internal fun printAdInfo(adBean: AdUnitBean, responseInfo: ResponseInfo?) {
-    adLog(
-        AdManager.TAG,
-        "-------------------------------------------------------------------"
-    )
-
-    adLog(AdManager.TAG, "广告加载成功")
-    adLog(AdManager.TAG, "广告商:${adBean.advertisersName}")
-    adLog(AdManager.TAG, "广告位:${adBean.adSlotName}")
-    adLog(AdManager.TAG, "广告ID:${adBean.adUnitId}")
-    adLog(AdManager.TAG, "广告类型:${adBean.adType}")
-    adLog(AdManager.TAG, "广告中介提供商:${responseInfo?.mediationAdapterClassName}")
-
-    adLog(
-        AdManager.TAG,
-        "-------------------------------------------------------------------"
-    )
-}
-
-
 /**
  * 横幅广告尺寸
  */
 internal fun getAdSize(context: Context): AdSize {
 
     // Step 2 - Determine the screen width (less decorations) to use for the ad width.
-    val widthPixels: Int = context.getScreenWidth()
-    val density: Float =context.getDensity()
+    val widthPixels: Int = getScreenWidth(context)
+    val density: Float = getDensity(context)
     val adWidth = (widthPixels / density).toInt()
 
     // Step 3 - Get adaptive ad size and return for setting on the ad view.
     return AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(context, adWidth)
+}
+
+
+/**
+ * 横幅 自适应尺寸
+ */
+fun AdSize.autoSize(context: Context) : AdSize{
+    return getAdSize(context)
+}
+
+
+/**
+ * 构建Admob横幅广告尺寸
+ */
+fun com.composition.android.lib.ad.interfaces.AdSize.Builder.buildAdmobBannerAdSize(adSize : AdSize) : IAdSize<AdSize>{
+    return AdmobAdSize(adSize)
 }

+ 2 - 1
lib_ad_admob/src/main/res/layout/layout_admob_native_media_view.xml

@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <com.google.android.gms.ads.nativead.MediaView xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="wrap_content"
+    >
 
 </com.google.android.gms.ads.nativead.MediaView>

+ 1 - 2
lib_ad_admob/src/test/java/com/composition/android/ad/admob/ExampleUnitTest.kt

@@ -1,9 +1,8 @@
 package com.composition.android.ad.admob
 
+import org.junit.Assert.assertEquals
 import org.junit.Test
 
-import org.junit.Assert.*
-
 /**
  * Example local unit test, which will execute on the development machine (host).
  *

+ 1 - 0
lib_ad_applovinmax/.gitignore

@@ -0,0 +1 @@
+/build

+ 41 - 0
lib_ad_applovinmax/build.gradle

@@ -0,0 +1,41 @@
+plugins {
+    id 'com.android.library'
+    id 'org.jetbrains.kotlin.android'
+}
+
+android {
+    compileSdk 32
+
+    defaultConfig {
+        minSdk 21
+        targetSdk 32
+
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+        consumerProguardFiles "consumer-rules.pro"
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+    kotlinOptions {
+        jvmTarget = '1.8'
+    }
+}
+
+dependencies {
+
+    implementation project(':lib_ad_core')
+    implementation 'androidx.core:core-ktx:1.7.0'
+    implementation 'androidx.appcompat:appcompat:1.5.1'
+    implementation 'com.google.android.material:material:1.6.1'
+    testImplementation 'junit:junit:4.13.2'
+    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
+    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+}

lib_ad_admob/consumer-rules.pro → lib_ad_applovinmax/consumer-rules.pro


+ 21 - 0
lib_ad_applovinmax/proguard-rules.pro

@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile

+ 22 - 0
lib_ad_applovinmax/src/androidTest/java/com/kdanmobile/android/ad/applovinmax/ExampleInstrumentedTest.kt

@@ -0,0 +1,22 @@
+package com.kdanmobile.android.ad.applovinmax
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.Assert.*
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+    @Test
+    fun useAppContext() {
+        // Context of the app under test.
+        val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+        assertEquals("com.kdanmobile.android.ad.applovinmax.test", appContext.packageName)
+    }
+}

+ 4 - 0
lib_ad_applovinmax/src/main/AndroidManifest.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest package="com.kdanmobile.android.ad.applovinmax">
+
+</manifest>

+ 16 - 0
lib_ad_applovinmax/src/test/java/com/kdanmobile/android/ad/applovinmax/ExampleUnitTest.kt

@@ -0,0 +1,16 @@
+package com.kdanmobile.android.ad.applovinmax
+
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+class ExampleUnitTest {
+    @Test
+    fun addition_isCorrect() {
+        assertEquals(4, 2 + 2)
+    }
+}

+ 25 - 3
lib_ad_core/build.gradle

@@ -2,6 +2,10 @@ plugins {
     id 'com.android.library'
     id 'org.jetbrains.kotlin.android'
 }
+def version = '1.0.0'
+def releaseTime() {
+    return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
+}
 
 android {
     compileSdk = rootProject.ext.compileSdkVersion
@@ -16,9 +20,28 @@ android {
     }
 
     buildTypes {
+
+        debug {
+//            libraryVariants.all { variant->
+//                variant.outputs.all { output ->
+//                    if (outputFile != null && outputFileName.endsWith('.aar')) {
+//                        outputFileName = "ad_core_${version}_${releaseTime()}_debug.aar"
+//                    }
+//                }
+//            }
+        }
+
         release {
-            minifyEnabled false
+            minifyEnabled true
             proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+
+            libraryVariants.all { variant->
+                variant.outputs.all { output ->
+                    if (outputFile != null && outputFileName.endsWith('.aar')) {
+                        outputFileName = "ad_core_${version}_${releaseTime()}_release.aar"
+                    }
+                }
+            }
         }
     }
     compileOptions {
@@ -32,8 +55,6 @@ android {
 
 dependencies {
 
-    api project(':lib_common')
-
     implementation 'androidx.core:core-ktx:1.8.0'
     implementation 'androidx.appcompat:appcompat:1.5.0'
     implementation 'com.google.android.material:material:1.6.1'
@@ -47,6 +68,7 @@ dependencies {
     api "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
     api 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.3'
     api 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.3'
+    api("androidx.lifecycle:lifecycle-runtime-ktx:2.5.1")
 
 
 }

+ 3 - 0
lib_ad_core/consumer-rules.pro

@@ -0,0 +1,3 @@
+
+-keep class com.composition.android.lib.ad.**{*;}
+-keep class com.composition.android.lib.ad.AdLoad

+ 4 - 1
lib_ad_core/proguard-rules.pro

@@ -18,4 +18,7 @@
 
 # If you keep the line number information, uncomment this to
 # hide the original source file name.
-#-renamesourcefileattribute SourceFile
+#-renamesourcefileattribute SourceFile
+
+-keep class com.composition.android.lib.ad.**{*;}
+-keep class com.composition.android.lib.ad.AdLoad

+ 2 - 4
lib_ad_core/src/androidTest/java/com/composition/android/lib/ad/ExampleInstrumentedTest.kt

@@ -1,13 +1,11 @@
 package com.composition.android.lib.ad
 
-import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.ext.junit.runners.AndroidJUnit4
-
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.Assert.*
 import org.junit.Test
 import org.junit.runner.RunWith
 
-import org.junit.Assert.*
-
 /**
  * Instrumented test, which will execute on an Android device.
  *

+ 1 - 2
lib_ad_core/src/main/AndroidManifest.xml

@@ -1,5 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.composition.android.lib.ad">
+<manifest package="com.composition.android.lib.ad">
 
 </manifest>

+ 130 - 44
lib_ad_core/src/main/java/com/composition/android/lib/ad/AdLoad.kt

@@ -4,12 +4,13 @@ import android.app.Activity
 import android.content.Context
 import android.view.View
 import android.view.ViewGroup
-import com.composition.android.lib.ad.basic.NativeAdViewHolder
 import com.composition.android.lib.ad.basic.AdResult
 import com.composition.android.lib.ad.basic.BannerAdViewHolder
+import com.composition.android.lib.ad.basic.NativeAdViewHolder
 import com.composition.android.lib.ad.bean.AdUnitBean
 import com.composition.android.lib.ad.impl.Api
 import com.composition.android.lib.ad.impl.NormalAdListener
+import com.composition.android.lib.ad.interfaces.AdListener
 import kotlinx.coroutines.flow.Flow
 
 /**
@@ -20,49 +21,50 @@ import kotlinx.coroutines.flow.Flow
  */
 object AdLoad {
 
-    /**
-     * 每个广告位最大缓存数量
-     */
-    var adSlotMaxCacheCount = 1
+    fun addToCache(adResult: AdResult.Success){
+        Api.instance.addToCache(adResult)
+    }
 
     /**
-     * 缓存集合
+     * 预加载广告
+     * @param context 上下文
+     * @param adSlotNames 要预加载的广告位名称
      */
-    private var cacheList: MutableList<AdResult.Success> = mutableListOf()
-
-    fun addToCache(success: AdResult.Success){
-        cacheList.add(success)
-    }
-
     suspend fun preloadAd(context: Context, vararg adSlotNames: String) {
-        val beans = AdUnitConfigManager.instance.getAdUnitBySlotName(adSlotNames.toList())
-        preloadAd(context, beans)
+        Api.instance.preloadAd(context, adSlotNames = adSlotNames)
     }
 
-
+    /**
+     * 预加载广告
+     * @param context 上下文
+     * @param adUnitBeans 要预加载的广告单元配置
+     */
     suspend fun preloadAd(context: Context, adUnitBeans: List<AdUnitBean>) {
-        loadAd(context, adUnitBeans)
-            .collect {
-                if (it is AdResult.Success) {
-                    cacheList.add(it)
-                }
-            }
+        Api.instance.preloadAd(context, adUnitBeans)
     }
 
+    /**
+     * 检查是否有缓存的广告
+     * @param adSlotName 广告位名称
+     */
     suspend fun hasCacheAd(adSlotName: String): Boolean {
-        return cacheList.any {
-            it.adBean.adSlotName.equals(adSlotName)
-        }
+        return Api.instance.hasCacheAd(adSlotName)
     }
 
+    /**
+     * 获取缓存的广告
+     *
+     */
     suspend fun getCacheAd(adUnitBean: AdUnitBean): AdResult.Success? {
-        val ad = cacheList.find {
-            it.adBean.equals(adUnitBean)
-        }
-        if (ad != null) {
-            cacheList.remove(ad)
-        }
-        return ad
+        return Api.instance.getCacheAd(adUnitBean)
+    }
+
+    /**
+     * 获取缓存的广告
+     *
+     */
+    suspend fun getCacheAd(adSlotName: String): AdResult.Success? {
+        return Api.instance.getCacheAd(adSlotName)
     }
 
 
@@ -81,7 +83,6 @@ object AdLoad {
         return loadAd(context = context, adUnitBeans = beans)
     }
 
-
     /**
      * 根据广告单元信息加载广告
      * @param context 上下文,推荐用activity context, 部分中介广告加载必须要activity context
@@ -99,52 +100,137 @@ object AdLoad {
         return Api.instance.getBannerView(context, success,viewHolder)
     }
 
-
     fun getNativeAdView(context: Context, adResult : AdResult.Success, viewHolder: NativeAdViewHolder) : View?{
         return Api.instance.getNativeAdView(context, adResult, viewHolder)
     }
 
-    fun populateNativeAdView(context: Context, adResult: AdResult.Success, viewHolder: NativeAdViewHolder, adRootView : View){
-        Api.instance.populateNativeAdView(context, adResult, viewHolder, adRootView)
+    fun populateNativeAdView(context: Context, adResult: AdResult.Success, viewHolder: NativeAdViewHolder, advertisersContainerView : ViewGroup){
+        Api.instance.populateNativeAdView(context, adResult, viewHolder, advertisersContainerView)
+    }
+
+    fun destroyAd(adResult: AdResult?){
+        Api.instance.destroyAd(adResult)
     }
 
     /**
-     * 显示全屏广告,包含插屏、激励视频、激励插屏,根据传入的数据自行判断
+     *
+     * 显示全屏广告,包含插屏、激励视频、激励插屏,根据传入的AdReslt.adBean.getAdType判断
+     * @param activity
+     * @param adResult 请求成功的广告结果
+     * @param adListener 广告展示监听
+     *
      */
     fun showFullScreenAd(
         activity: Activity,
-        success: AdResult.Success,
+        adResult: AdResult.Success,
+        adListener: AdListener
+    ) {
+        Api.instance.showFullScreenAd(activity, adResult, adListener = adListener)
+    }
+
+    /**
+     * 显示全屏广告
+     * @param activity
+     * @param adResult 请求成功的广告实例
+     * @param adListener 广告展示监听 高阶函数式
+     */
+    fun showFullScreenAd(
+        activity: Activity,
+        adResult: AdResult.Success,
         adListener: NormalAdListener.() -> Unit
     ) {
-        Api.instance.showFullScreenAd(activity, success, adListener = NormalAdListener().also(adListener))
+        showFullScreenAd(activity, adResult, NormalAdListener().also(adListener))
+    }
+
+    /**
+     * 显示插屏广告
+     * @param activity
+     * @param adResult 请求成功的广告实例
+     * @param adListener 插屏广告监听
+     */
+    fun showInterstitialAd(
+        activity: Activity,
+        adResult: AdResult.Success,
+        adListener: AdListener
+    ) {
+        Api.instance.showInterstitialAd(activity = activity, adResult = adResult, adListener = adListener)
     }
 
 
     fun showInterstitialAd(
         activity: Activity,
-        success: AdResult.Success,
+        adResult: AdResult.Success,
         adListener: NormalAdListener.() -> Unit
     ) {
-        Api.instance.showInterstitialAd(activity = activity, success = success, adListener = NormalAdListener().also(adListener))
+       showInterstitialAd(activity = activity, adResult = adResult, adListener = NormalAdListener().also(adListener))
     }
 
 
+    /**
+     * 显示激励视频广告
+     * @param activity
+     * @param adResult 请求成功的广告实例
+     * @param adListener 插屏广告监听
+     */
     fun showRewardedAd(
         activity: Activity,
-        success: AdResult.Success,
+        adResult: AdResult.Success,
+        adListener: NormalAdListener.() -> Unit
+    ) {
+        Api.instance.showRewardedAd(activity = activity, adResult = adResult, adListener = NormalAdListener().also(adListener))
+    }
+
+
+    /**
+     * 显示激励插屏广告
+     * @param activity
+     * @param adResult 请求成功的广告实例
+     * @param adListener 插屏广告监听
+     */
+    fun showRewardedInterstitialAd(
+        activity: Activity,
+        adResult: AdResult.Success,
+        adListener: AdListener
+    ) {
+        Api.instance.showRewardedInterstitialAd(activity = activity, adResult = adResult, adListener = adListener)
+    }
+
+
+    fun showRewardedInterstitialAd(
+        activity: Activity,
+        adResult: AdResult.Success,
         adListener: NormalAdListener.() -> Unit
     ) {
-        Api.instance.showRewardedAd(activity = activity, success = success, adListener = NormalAdListener().also(adListener))
+        showRewardedInterstitialAd(activity = activity, adResult = adResult, adListener = NormalAdListener().also(adListener))
     }
 
+
+    /**
+     * 显示开屏广告
+     * @param activity 用于展示的activity
+     * @param splashViewGroup 用于填充广告的ViewGroup
+     * @param adResult 请求成功的广告实例
+     * @param adListener 广告监听
+     */
     fun showSplash(
         activity: Activity,
         splashViewGroup: ViewGroup?,
-        success: AdResult.Success,
+        adResult: AdResult.Success,
+        adListener: AdListener
+    ) {
+        Api.instance.showSplash(activity = activity, splashViewGroup = splashViewGroup, success = adResult, adListener = adListener)
+    }
+
+
+    fun showSplash(
+        activity: Activity,
+        splashViewGroup: ViewGroup?,
+        adResult: AdResult.Success,
         adListener: NormalAdListener.() -> Unit
     ) {
-        Api.instance.showSplash(activity = activity, splashViewGroup = splashViewGroup, success = success, adListener = NormalAdListener().also(adListener))
+        Api.instance.showSplash(activity = activity, splashViewGroup = splashViewGroup, success = adResult, adListener = NormalAdListener().also(adListener))
     }
 
 
+
 }

+ 25 - 5
lib_ad_core/src/main/java/com/composition/android/lib/ad/AdManager.kt

@@ -1,12 +1,12 @@
 package com.composition.android.lib.ad
 
 import android.content.Context
-import com.composition.android.lib.ad.basic.Advertisers
+import androidx.appcompat.widget.AppCompatImageView
 import com.composition.android.lib.ad.bean.OnAdShow
-import com.composition.android.lib.ad.impl.NormalAdListener
-import com.composition.android.lib.ad.interfaces.AdListener
 import com.composition.android.lib.ad.interfaces.AdLoader
+import com.composition.android.lib.ad.interfaces.ImageLoader
 import com.composition.android.lib.ad.interfaces.Initialize
+import com.composition.android.lib.ad.util.adLog
 
 /**
  * @classname:
@@ -34,16 +34,28 @@ class AdManager {
      * 是否开启日志
      */
     var logEnable = false
+    set(value) {
+        field = value
+        adLog(msg = "日志已${if (value) "开启" else "关闭"}")
+    }
+
+    var isDebug = false
 
     /**
      * 广告全局开关
      */
     var enableAd: Boolean = true
 
-    internal var adLoaderLists = mutableListOf<Pair<Advertisers, AdLoader>>()
+    /**
+     * 注册的广告加载器
+     */
+    internal var adLoaderLists = mutableListOf<AdLoader>()
 
     var globalListener: OnAdShow? = null
 
+    var normalImageLoader : ImageLoader<AppCompatImageView>? = null
+
+
     /**
      * 广告初始化
      */
@@ -55,9 +67,15 @@ class AdManager {
     /**
      * 注册广告加载器
      */
-    fun registerAdLoader(vararg adLoader: Pair<Advertisers, AdLoader>) {
+    fun registerAdLoader(vararg adLoader: AdLoader) {
         adLoaderLists.clear()
         adLoaderLists.addAll(adLoader)
+        adLog(msg = "注册的广告加载器:\n" +
+                "${
+                    adLoader.forEach { 
+                        it.advertisers.name
+                    }
+                }")
     }
 
 
@@ -75,4 +93,6 @@ class AdManager {
         }
     }
 
+
+
 }

+ 28 - 2
lib_ad_core/src/main/java/com/composition/android/lib/ad/AdUnitConfigManager.kt

@@ -2,6 +2,7 @@ package com.composition.android.lib.ad
 
 import com.composition.android.lib.ad.basic.Advertisers
 import com.composition.android.lib.ad.bean.AdUnitBean
+import com.composition.android.lib.ad.util.adLog
 
 /**
  * @classname:
@@ -28,8 +29,13 @@ class AdUnitConfigManager {
      * 设置广告单元集
      */
     fun setAdUnits(adUnits: List<AdUnitBean>) {
+        //去重,每个广告位只准配一个
+        val list = adUnits.distinctBy {
+            it.adSlotName
+        }
         adUnitLists.clear()
-        adUnitLists.addAll(adUnits)
+        adUnitLists.addAll(list)
+        printSetAdUnitInfo(list)
     }
 
 
@@ -46,8 +52,28 @@ class AdUnitConfigManager {
 
     private fun hasAdLoader(advertisers: Advertisers) : Boolean{
         return AdManager.instance.adLoaderLists.any {
-            it.first == advertisers
+            it.advertisers == advertisers
+        }
+    }
+
+    private fun printSetAdUnitInfo(adUnits: List<AdUnitBean>){
+        var builder = StringBuilder()
+        builder.append("启用的广告位:------->\n")
+        for (adUnit in adUnits) {
+            builder.append(adUnit.toString()).append("\n\n")
+        }
+        adLog(msg = builder.toString())
+    }
+
+    /**
+     * 广告位策略是通过状态
+     */
+    fun adSlotStrategyIsAllow(adSlotName : String) : Boolean {
+        val adUnitList = getAdUnitBySlotName(adSlotNames = arrayListOf(adSlotName))
+        return adUnitList.any {
+            it.loadStrategy.allow()
         }
     }
 
+
 }

+ 0 - 19
lib_ad_core/src/main/java/com/composition/android/lib/ad/basic/AdResult.kt

@@ -35,25 +35,6 @@ sealed class AdResult(
         override var msg: String
     ) : AdResult(adBean, code, msg)
 
-    /**
-     * 广告请求取消
-     */
-    class Cancel(
-        override var adBean: AdUnitBean,
-        @AdLoadCode override var code: Int = AdLoadCode.CANCEL,
-        override var msg: String
-    ) : AdResult(adBean, code, msg)
-
-
-    /**
-     * 空,未知
-     */
-    class Empty(
-        override var adBean: AdUnitBean,
-        @AdLoadCode override var code: Int = AdLoadCode.FAIL,
-        override var msg: String = ""
-    ) : AdResult(adBean, code, msg)
-
 
 }
 

+ 11 - 9
lib_ad_core/src/main/java/com/composition/android/lib/ad/basic/AdType.kt

@@ -6,45 +6,47 @@ package com.composition.android.lib.ad.basic
  * @date: 2022/9/1
  * description:广告类型
  */
-enum class AdType(name : String) {
+enum class AdType {
 
     /**
      * 开屏广告
      */
-    APP_OPEN("AppOpen"),
+    APP_OPEN,
 
     /**
      * 横幅广告
      */
-    BANNER("Banner"),
+    BANNER,
 
     /**
      * 原生自填充广告
      */
-    NATIVE("Native"),
+    NATIVE,
 
     /**
      * 原生模板广告,类似于横幅广告,不需要自己填充内容
      */
-    NATIVE_TEMPLATE("NativeTemplate"),
+    NATIVE_TEMPLATE,
 
     /**
      * 插屏广告
      */
-    INTERSTITIAL("Interstitial"),
+    INTERSTITIAL,
 
     /**
      * 激励视频广告
      */
-    REWARDED_VIDEO("RewardedVideo"),
+    REWARDED_VIDEO,
 
     /**
      * 激励插屏广告
      */
-    REWARDED_INTERSTITIAL("RewardedInterstitial"),
+    REWARDED_INTERSTITIAL,
 
     /**
      * 未知
      */
-    UNKNOWN("Unknown")
+    UNKNOWN
+
+
 }

+ 0 - 6
lib_ad_core/src/main/java/com/composition/android/lib/ad/basic/BannerAdViewHolder.kt

@@ -1,13 +1,7 @@
 package com.composition.android.lib.ad.basic
 
 import android.app.Activity
-import android.view.View
-import android.view.ViewGroup
-import androidx.appcompat.widget.AppCompatButton
-import androidx.appcompat.widget.AppCompatImageView
-import androidx.appcompat.widget.AppCompatTextView
 import com.composition.android.lib.ad.interfaces.AdListener
-import com.composition.android.lib.ad.interfaces.ImageLoader
 
 /**
  * @classname:

+ 5 - 8
lib_ad_core/src/main/java/com/composition/android/lib/ad/basic/BasicAdView.kt

@@ -2,16 +2,15 @@ package com.composition.android.lib.ad.basic
 
 import android.content.Context
 import android.util.AttributeSet
-import android.util.Log
 import android.view.Gravity
 import android.view.View
-import android.view.ViewGroup
-import android.widget.FrameLayout
 import android.widget.LinearLayout
-import androidx.core.view.updateLayoutParams
-import androidx.lifecycle.*
+import androidx.lifecycle.DefaultLifecycleObserver
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.findViewTreeLifecycleOwner
 import com.composition.android.lib.ad.AdManager
 import com.composition.android.lib.ad.util.adLog
+import com.composition.android.lib.ad.util.parentRemove
 
 /**
  * @classname:
@@ -42,9 +41,7 @@ abstract class BasicAdView<T : View> : LinearLayout, DefaultLifecycleObserver {
         this.adResult = adResult
         adView = adContentView()
         adView?.let {
-            if (it.parent != null) {
-                (it.parent as? ViewGroup)?.removeView(it)
-            }
+            it.parentRemove()
             removeAllViews()
             addView(it)
         }

+ 8 - 11
lib_ad_core/src/main/java/com/composition/android/lib/ad/basic/NativeAdViewHolder.kt

@@ -6,6 +6,7 @@ import android.view.ViewGroup
 import androidx.appcompat.widget.AppCompatButton
 import androidx.appcompat.widget.AppCompatImageView
 import androidx.appcompat.widget.AppCompatTextView
+import com.composition.android.lib.ad.AdManager
 import com.composition.android.lib.ad.interfaces.AdListener
 import com.composition.android.lib.ad.interfaces.ImageLoader
 
@@ -19,25 +20,21 @@ class NativeAdViewHolder(
     var titleView : AppCompatTextView? = null,
     var titleDescView : View? = null,
     var iconView : View? = null,
-    var rootView : View? = null,
-    var contentView : View? = null,
-    var contentMediaGroup : ViewGroup? = null,
-    var callActionView : AppCompatButton? = null,
-    var ratingBar : View? = null,
-    var advertiserView : View? = null,
-    var adListener : AdListener? = null,
+    var customLayoutRootView : View? = null,
+    var contentMediaViewGroup : ViewGroup? = null,
+    var callActionButtonView : AppCompatButton? = null,
+    var advertiserLogoImageView : AppCompatImageView? = null,
     var dislikeView : View? = null,
+    var adListener : AdListener? = null,
     var activity : Activity? = null, //穿山甲广告需要
-    var imageLoader : ImageLoader<AppCompatImageView>? = null
+    var mediaImageLoader : ImageLoader<AppCompatImageView>? = AdManager.instance.normalImageLoader,
+    var iconImageLoader : ImageLoader<AppCompatImageView>? = AdManager.instance.normalImageLoader
 ) {
 
-
     companion object{
-
         fun buildNativeAdViewHolder(viewHolder : NativeAdViewHolder.()-> Unit) : NativeAdViewHolder{
             return NativeAdViewHolder().also(viewHolder)
         }
-
     }
 
 }

+ 0 - 13
lib_ad_core/src/main/java/com/composition/android/lib/ad/basic/NormalImageLoader.kt

@@ -1,13 +0,0 @@
-package com.composition.android.lib.ad.basic
-
-import android.content.Context
-import android.widget.ImageView
-import com.composition.android.lib.ad.interfaces.ImageLoader
-
-abstract class NormalImageLoader : ImageLoader<ImageView> {
-
-    override fun createImageView(context: Context?): ImageView {
-        return ImageView(context)
-    }
-
-}

+ 1 - 1
lib_ad_core/src/main/java/com/composition/android/lib/ad/bean/AdUnitBean.kt

@@ -2,8 +2,8 @@ package com.composition.android.lib.ad.bean
 
 import com.composition.android.lib.ad.basic.AdType
 import com.composition.android.lib.ad.basic.Advertisers
-import com.composition.android.lib.ad.impl.NormalStrategy
 import com.composition.android.lib.ad.basic.strategy.IStrategy
+import com.composition.android.lib.ad.impl.NormalStrategy
 import com.composition.android.lib.ad.interfaces.IAdSize
 
 

+ 1 - 1
lib_ad_core/src/main/java/com/composition/android/lib/ad/bean/AdUnitLoadConfig.kt

@@ -14,6 +14,6 @@ data class AdUnitLoadConfig(
     /**
      * 广告加载超时时间,单位:毫秒
      */
-    var adLoadTimeOut : Long = 10000
+    var adLoadTimeOut : Long = 8000
 
 )

+ 4 - 4
lib_ad_core/src/main/java/com/composition/android/lib/ad/factory/AdLoaderFactory.kt

@@ -1,8 +1,8 @@
 package com.composition.android.lib.ad.factory
 
 import com.composition.android.lib.ad.AdManager
-import com.composition.android.lib.ad.impl.UnknownAdLoader
 import com.composition.android.lib.ad.bean.AdUnitBean
+import com.composition.android.lib.ad.impl.UnknownAdLoader
 import com.composition.android.lib.ad.interfaces.AdLoader
 
 /**
@@ -14,11 +14,11 @@ import com.composition.android.lib.ad.interfaces.AdLoader
 object AdLoaderFactory {
 
 
-    fun getAdLoader(adUnitBean: AdUnitBean) : AdLoader{
+    fun getAdLoader(adUnitBean: AdUnitBean?) : AdLoader{
         return try {
             AdManager.instance.adLoaderLists.find {
-                it.first == adUnitBean.getAdvertisers()
-            }?.second?: UnknownAdLoader()
+                it.advertisers == adUnitBean?.getAdvertisers()
+            }?: UnknownAdLoader()
         }catch (e : Exception){
             UnknownAdLoader()
         }

+ 190 - 29
lib_ad_core/src/main/java/com/composition/android/lib/ad/impl/Api.kt

@@ -4,14 +4,16 @@ import android.app.Activity
 import android.content.Context
 import android.view.View
 import android.view.ViewGroup
-import com.composition.android.lib.ad.AdLoad
+import com.composition.android.lib.ad.AdUnitConfigManager
 import com.composition.android.lib.ad.basic.AdLoadCode
 import com.composition.android.lib.ad.basic.AdResult
 import com.composition.android.lib.ad.basic.BannerAdViewHolder
 import com.composition.android.lib.ad.basic.NativeAdViewHolder
 import com.composition.android.lib.ad.bean.AdUnitBean
 import com.composition.android.lib.ad.factory.AdLoaderFactory
+import com.composition.android.lib.ad.interfaces.AdListener
 import com.composition.android.lib.ad.interfaces.AdLoader
+import com.composition.android.lib.ad.util.adLog
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flow
 
@@ -21,32 +23,137 @@ import kotlinx.coroutines.flow.flow
  * @date: 2022/9/5
  * description:
  */
-class Api {
+internal class Api {
 
-    companion object{
+    companion object {
         val instance by lazy {
             Api()
         }
     }
 
-    private fun getAdLoader(adUnitBean: AdUnitBean) : AdLoader{
+    /**
+     * 每个广告位最大缓存数量
+     */
+    private var adSlotMaxCacheCount = 1
+
+    /**
+     * 缓存集合
+     */
+    private var cacheList: MutableList<AdResult.Success> = mutableListOf()
+
+    /**
+     * 添加请求成功的广告实例到缓存中
+     * @param adResult 请求成功的广告实例
+     */
+    fun addToCache(adResult: AdResult.Success){
+        cacheList.add(adResult)
+    }
+
+    /**
+     * 预加载广告
+     * @param context 上下文
+     * @param adSlotNames 要预加载的广告位名称
+     */
+    suspend fun preloadAd(context: Context, vararg adSlotNames: String) {
+        val beans = AdUnitConfigManager.instance.getAdUnitBySlotName(adSlotNames.toList())
+        preloadAd(context, beans)
+    }
+
+    /**
+     * 预加载广告
+     * @param context 上下文
+     * @param adUnitBeans 要预加载的广告单元配置
+     */
+    suspend fun preloadAd(context: Context, adUnitBeans: List<AdUnitBean>) {
+        val beans = mutableListOf<AdUnitBean>()
+        for (adUnitBean in adUnitBeans) {
+            if (getAdSlotCacheCount(adUnitBean.adSlotName) < adSlotMaxCacheCount){
+                beans.add(adUnitBean)
+            }else{
+                adLog(msg = "广告位:${adUnitBean.adSlotName}已达到最大缓存数量,本次不在进行预加载")
+            }
+        }
+
+        load(context, adUnitBeans)
+            .collect {
+                if (it is AdResult.Success) {
+                    adLog(msg = "预加载广告-${it.adBean.adSlotName}-成功")
+                    addToCache(it)
+                }else{
+                    adLog(msg = "预加载广告-${it.adBean.adSlotName}-失败")
+                }
+            }
+    }
+
+    /**
+     * 检查是否有缓存的广告
+     * @param adSlotName 广告位名称
+     */
+    fun hasCacheAd(adSlotName: String): Boolean {
+        return cacheList.any {
+            it.adBean.adSlotName.equals(adSlotName)
+        }
+    }
+
+    /**
+     * 广告位已缓存的实例数量
+     */
+    private fun getAdSlotCacheCount(adSlotName: String) : Int{
+        return cacheList.count {
+            it.adBean.adSlotName.equals(adSlotName)
+        }
+    }
+
+    /**
+     * 获取缓存的广告
+     *
+     */
+    fun getCacheAd(adUnitBean: AdUnitBean): AdResult.Success? {
+        val ad = cacheList.find {
+            it.adBean.equals(adUnitBean)
+        }
+        if (ad != null) {
+            cacheList.remove(ad)
+        }
+        return ad
+    }
+
+    fun getCacheAd(adSlotName: String) : AdResult.Success? {
+        val ad = cacheList.find {
+            it.adBean.adSlotName.equals(adSlotName)
+        }
+        if (ad != null) {
+            cacheList.remove(ad)
+        }
+        return ad
+    }
+
+
+    private fun getAdLoader(adUnitBean: AdUnitBean): AdLoader {
         return AdLoaderFactory.getAdLoader(adUnitBean)
     }
 
 
-    fun load(context: Context, adUnitBeans : List<AdUnitBean>) : Flow<AdResult>{
+    /**
+     * 加载广告
+     * @param context 上下文
+     * @param adUnitBeans 加载的广告单元配置
+     */
+    fun load(context: Context, adUnitBeans: List<AdUnitBean>): Flow<AdResult> {
         return flow {
-            if (adUnitBeans.isNullOrEmpty()){
+            if (adUnitBeans.isNullOrEmpty()) {
+                adLog(msg = "广告单元配置为空")
                 emit(AdResult.Fail(adBean = AdUnitBean.emptyAdUnitBean(), msg = "AdUnitBeans 为空"))
                 return@flow
             }
             for (adUnitBean in adUnitBeans) {
                 if (adUnitBean.loadStrategy.allow().not()) {
+                    adLog(msg = "广告策略未通过")
                     emit(AdResult.Fail(adUnitBean, AdLoadCode.FAIL, msg = "策略未通过"))
                     continue
                 }
-                val cacheAd = AdLoad.getCacheAd(adUnitBean)
-                if (cacheAd != null){
+                val cacheAd = getCacheAd(adUnitBean)
+                if (cacheAd != null) {
                     emit(cacheAd)
                     continue
                 }
@@ -57,71 +164,125 @@ class Api {
     }
 
 
-    fun getBannerView(context: Context, adResult: AdResult.Success, viewHolder: BannerAdViewHolder) : View?{
-        if (adResult.adBean.loadStrategy.allow().not()){
+    /**
+     * 获取横幅广告View
+     * @param context 上下文
+     * @param adResult 请求成功的广告实例
+     * @param viewHolder 横幅广告的一些配置,可以配置广告的监听
+     */
+    fun getBannerView(context: Context, adResult: AdResult.Success, viewHolder: BannerAdViewHolder): View? {
+        if (adResult.adBean.loadStrategy.allow().not()) {
             return null
         }
         return getAdLoader(adResult.adBean).getBannerView(context, adResult, viewHolder)
     }
 
-    fun getNativeAdView(context: Context, adResult : AdResult.Success, viewHolder: NativeAdViewHolder) : View?{
-        if (adResult.adBean.loadStrategy.allow().not()){
+
+    /**
+     * 获取原生广告View,包含原生模板广告
+     */
+    fun getNativeAdView(context: Context, adResult: AdResult.Success, viewHolder: NativeAdViewHolder): View? {
+        if (adResult.adBean.loadStrategy.allow().not()) {
             return null
         }
         return getAdLoader(adResult.adBean).getNativeAdView(context, adResult, viewHolder)
     }
 
 
-    fun populateNativeAdView(context: Context, adResult: AdResult.Success, viewHolder: NativeAdViewHolder, adRootView : View){
-        getAdLoader(adResult.adBean).populateNativeAdView(context, adResult, viewHolder, adRootView)
+    /**
+     * 填充原生广告内容
+     * 用于外部自己写好了广告布局,然后进行填充操作
+     * @param context 上下文
+     * @param adResult 请求成功的广告实例
+     * @param viewHolder 原生广告布局配置
+     * @param advertisersContainerView 广告商的填充视图
+     * 例如: Admob原生广告最外层需要 com.google.android.gms.ads.nativead.NativeAdView 进行包裹自定义布局内容 , advertisersContainerView就传入这个NativeAdView
+     *
+     * Admob : com.google.android.gms.ads.nativead.NativeAdView
+     * GroMore : com.bytedance.msdk.api.format.TTNativeAdView
+     * CSJ : 不需要特定的布局, 随意ViewGroup都行
+     *
+     */
+    fun populateNativeAdView(context: Context, adResult: AdResult.Success, viewHolder: NativeAdViewHolder, advertisersContainerView: ViewGroup) {
+        getAdLoader(adResult.adBean).populateNativeAdView(context, adResult, viewHolder, advertisersContainerView)
     }
 
+    fun destroyAd(adResult: AdResult?){
+        if (adResult != null) {
+            getAdLoader(adResult.adBean).destroyAd(adResult)
+        }
+    }
 
+    /**
+     * 显示插屏广告
+     */
     fun showInterstitialAd(
         activity: Activity,
-        success: AdResult.Success,
-        adListener: NormalAdListener
-    ){
-        if (success.adBean.loadStrategy.allow().not()){
+        adResult: AdResult.Success,
+        adListener: AdListener
+    ) {
+        if (adResult.adBean.loadStrategy.allow().not()) {
             adListener.onAdShowFail("展示策略未通过")
             return
         }
-        getAdLoader(success.adBean).showInterstitialAd(activity, success, adListener)
+        getAdLoader(adResult.adBean).showInterstitialAd(activity, adResult, adListener)
     }
 
 
+    /**
+     * 显示激励视频广告
+     */
     fun showRewardedAd(
         activity: Activity,
-        success: AdResult.Success,
-        adListener: NormalAdListener
+        adResult: AdResult.Success,
+        adListener: AdListener
+    ) {
+        if (adResult.adBean.loadStrategy.allow().not()) {
+            adListener.onAdShowFail("展示策略未通过")
+            return
+        }
+        getAdLoader(adResult.adBean).showRewardedAd(activity, adResult, adListener)
+    }
+
+    fun showRewardedInterstitialAd(
+        activity: Activity,
+        adResult: AdResult.Success,
+        adListener: AdListener
     ){
-        if (success.adBean.loadStrategy.allow().not()){
+        if (adResult.adBean.loadStrategy.allow().not()) {
             adListener.onAdShowFail("展示策略未通过")
             return
         }
-        getAdLoader(success.adBean).showRewardedAd(activity, success, adListener)
+        getAdLoader(adResult.adBean).showRewardedInterstitialAd(activity, adResult, adListener)
+
     }
 
 
+    /**
+     * 显示开屏广告
+     */
     fun showSplash(
         activity: Activity,
         splashViewGroup: ViewGroup?,
         success: AdResult.Success,
-        adListener: NormalAdListener
-    ){
-        if (success.adBean.loadStrategy.allow().not()){
+        adListener: AdListener
+    ) {
+        if (success.adBean.loadStrategy.allow().not()) {
             adListener.onAdShowFail("展示策略未通过")
             return
         }
         getAdLoader(success.adBean).showSplashAd(activity, splashViewGroup, success, adListener)
     }
 
+    /**
+     * 显示全屏广告
+     */
     fun showFullScreenAd(
         activity: Activity,
         success: AdResult.Success,
-        adListener: NormalAdListener
-    ){
-        if (success.adBean.loadStrategy.allow().not()){
+        adListener: AdListener
+    ) {
+        if (success.adBean.loadStrategy.allow().not()) {
             adListener.onAdShowFail("展示策略未通过")
             return
         }

+ 2 - 2
lib_ad_core/src/main/java/com/composition/android/lib/ad/impl/NormalStrategy.kt

@@ -2,7 +2,7 @@ package com.composition.android.lib.ad.impl
 
 import com.composition.android.lib.ad.AdManager
 import com.composition.android.lib.ad.basic.strategy.IStrategy
-import com.convenient.android.common.extension.isNetworkAvailable
+import com.composition.android.lib.ad.util.isNetworkAvailable
 
 
 /**
@@ -15,7 +15,7 @@ class NormalStrategy : IStrategy {
 
     override fun allow(): Boolean {
         if (AdManager.instance.enableAd.not()) return false
-        if (AdManager.instance.context.isNetworkAvailable().not()) return false
+        if (isNetworkAvailable(AdManager.instance.context).not()) return false
         return true
     }
 

+ 12 - 8
lib_ad_core/src/main/java/com/composition/android/lib/ad/impl/UnknownAdLoader.kt

@@ -8,8 +8,6 @@ import com.composition.android.lib.ad.basic.*
 import com.composition.android.lib.ad.bean.AdUnitBean
 import com.composition.android.lib.ad.interfaces.AdListener
 import com.composition.android.lib.ad.interfaces.AdLoader
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.flowOf
 
 /**
  * @classname:
@@ -19,6 +17,8 @@ import kotlinx.coroutines.flow.flowOf
  */
 class UnknownAdLoader : AdLoader {
 
+    override val advertisers: Advertisers
+        get() = Advertisers.UNKNOWN
 
     override suspend fun load(context: Context, adUnitBean: AdUnitBean): AdResult {
         return AdResult.Fail(adUnitBean, AdLoadCode.FAIL, "未知的广告商,请检查Advertisers,当前:${adUnitBean.advertisersName}")
@@ -32,30 +32,34 @@ class UnknownAdLoader : AdLoader {
         return null
     }
 
-    override fun populateNativeAdView(context: Context, adResult: AdResult.Success, viewHolder: NativeAdViewHolder, adRootView: View) {
+    override fun populateNativeAdView(context: Context, adResult: AdResult.Success, viewHolder: NativeAdViewHolder, advertisersContainerView: ViewGroup) {
 
     }
 
-    override fun showInterstitialAd(activity: Activity, success: AdResult.Success, adListener: AdListener) {
+    override fun showInterstitialAd(activity: Activity, adResult: AdResult.Success, adListener: AdListener) {
         adListener.onAdShowFail("未知的广告加载器")
 
     }
 
-    override fun showRewardedAd(activity: Activity, success: AdResult.Success, adListener: AdListener) {
+    override fun showRewardedAd(activity: Activity, adResult: AdResult.Success, adListener: AdListener) {
         adListener.onAdShowFail("未知的广告加载器")
 
     }
 
-    override fun showSplashAd(activity: Activity, splashViewGroup: ViewGroup?, success: AdResult.Success, adListener: AdListener) {
+    override fun showSplashAd(activity: Activity, splashViewGroup: ViewGroup?, adResult: AdResult.Success, adListener: AdListener) {
         adListener.onAdShowFail("未知的广告加载器")
 
     }
 
-    override fun showFullScreenAd(activity: Activity, success: AdResult.Success, adListener: AdListener) {
+    override fun showFullScreenAd(activity: Activity, adResult: AdResult.Success, adListener: AdListener) {
         adListener.onAdShowFail("未知的广告加载器")
     }
 
-    override fun showRewardedInterstitialAd(activity: Activity, success: AdResult.Success, adListener: AdListener) {
+    override fun showRewardedInterstitialAd(activity: Activity, adResult: AdResult.Success, adListener: AdListener) {
         adListener.onAdShowFail("未知的广告加载器")
     }
+
+    override fun destroyAd(adResult: AdResult) {
+
+    }
 }

+ 45 - 9
lib_ad_core/src/main/java/com/composition/android/lib/ad/interfaces/AdLoader.kt

@@ -7,7 +7,11 @@ import android.view.ViewGroup
 import com.composition.android.lib.ad.basic.*
 import com.composition.android.lib.ad.bean.AdUnitBean
 import com.composition.android.lib.ad.exception.AdLoadFailException
-import kotlinx.coroutines.flow.*
+import com.composition.android.lib.ad.util.adLog
+import kotlinx.coroutines.flow.firstOrNull
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.retry
 import kotlinx.coroutines.withTimeoutOrNull
 
 /**
@@ -18,6 +22,8 @@ import kotlinx.coroutines.withTimeoutOrNull
  */
 interface AdLoader {
 
+    val advertisers : Advertisers
+
 
     suspend fun load(context: Context, adUnitBean : AdUnitBean) : AdResult
 
@@ -45,6 +51,7 @@ interface AdLoader {
                 }.firstOrNull()
             }?:AdResult.Fail(adUnitBean, AdLoadCode.FAIL, "加载失败")
         }catch (e : Exception){
+            adLog(msg = e.message)
             AdResult.Fail(adUnitBean, AdLoadCode.FAIL, "加载失败")
         }
     }
@@ -60,23 +67,52 @@ interface AdLoader {
      */
     fun getNativeAdView(context: Context, adResult: AdResult.Success, viewHolder: NativeAdViewHolder) :View?
 
+    /**
+     * 销毁广告
+     * 会根据adResult.adObject 类型调用destroy()进行销毁
+     */
+    fun destroyAd(adResult: AdResult)
+
     /**
      * 填充原生广告,不会返回View
      * @param adResult 请求成功的广告实例
      * @param viewHolder 要填充的广告布局内容
-     * @param adRootView 广告的根布局,例如Admob: NativeAdView
-     * 此方法适用于在外部直接写好所有整体的布局 NativeAdView中包含了具体布局,比如在RecyclerView中,这样布局已经绘制,只要填充内容就好
+     * @param advertisersContainerView 厂商的广告容器view
+     *
+     * 例如: Admob原生广告最外层需要 com.google.android.gms.ads.nativead.NativeAdView 进行包裹自定义布局内容
+     * advertisersContainerView 就传入这个NativeAdView
+     *
+     * Admob : com.google.android.gms.ads.nativead.NativeAdView
+     * GroMore : com.bytedance.msdk.api.format.TTNativeAdView
+     * CSJ : 不需要特定的布局, 随意ViewGroup都行
+     *
      */
-    fun populateNativeAdView(context: Context, adResult: AdResult.Success, viewHolder: NativeAdViewHolder, adRootView : View)
+    fun populateNativeAdView(context: Context, adResult: AdResult.Success, viewHolder: NativeAdViewHolder, advertisersContainerView : ViewGroup)
 
-    fun showInterstitialAd(activity: Activity, success: AdResult.Success,adListener: AdListener)
+    /**
+     * 显示插屏广告
+     */
+    fun showInterstitialAd(activity: Activity, adResult: AdResult.Success, adListener: AdListener)
 
-    fun showRewardedAd(activity: Activity, success: AdResult.Success, adListener: AdListener)
+    /**
+     * 显示激励视频广告
+     */
+    fun showRewardedAd(activity: Activity, adResult: AdResult.Success, adListener: AdListener)
 
-    fun showRewardedInterstitialAd(activity: Activity, success: AdResult.Success, adListener: AdListener)
+    /**
+     * 显示激励插屏广告
+     */
+    fun showRewardedInterstitialAd(activity: Activity, adResult: AdResult.Success, adListener: AdListener)
 
-    fun showSplashAd(activity: Activity, splashViewGroup : ViewGroup?, success: AdResult.Success, adListener: AdListener)
+    /**
+     * 显示开屏广告
+     */
+    fun showSplashAd(activity: Activity, splashViewGroup : ViewGroup?, adResult: AdResult.Success, adListener: AdListener)
 
-    fun showFullScreenAd(activity: Activity, success: AdResult.Success, adListener: AdListener)
+    /**
+     * 显示全屏广告
+     * 会根据 adResult.adBean.adType判断是插屏、激励视频、激励插屏
+     */
+    fun showFullScreenAd(activity: Activity, adResult: AdResult.Success, adListener: AdListener)
 
 }

+ 21 - 0
lib_ad_core/src/main/java/com/composition/android/lib/ad/interfaces/IAdSize.kt

@@ -10,4 +10,25 @@ interface IAdSize<T> {
 
     fun getAdSize() : T
 
+}
+
+
+/**
+ * @classname:
+ * @author: LiuXiaoLong
+ * @date: 2022/10/25
+ * description:
+ */
+class AdSize {
+
+
+    class Builder{
+
+        companion object{
+
+            fun create() : Builder = Builder()
+        }
+
+    }
+
 }

+ 0 - 1
lib_ad_core/src/main/java/com/composition/android/lib/ad/interfaces/ImageLoader.kt

@@ -11,6 +11,5 @@ interface ImageLoader<T : View> : Serializable {
 
     fun displayImage(context: Context?, path: Any?, imageView: T)
 
-    fun createImageView(context: Context?): T
 
 }

+ 106 - 0
lib_ad_core/src/main/java/com/composition/android/lib/ad/util/AdCoreExtenstions.kt

@@ -0,0 +1,106 @@
+package com.composition.android.lib.ad.util
+
+import android.annotation.SuppressLint
+import android.app.Activity
+import android.content.Context
+import android.content.ContextWrapper
+import android.net.ConnectivityManager
+import android.net.NetworkCapabilities
+import android.os.Build
+import android.view.View
+import android.view.ViewGroup
+
+/**
+ * @classname:
+ * @author: LiuXiaoLong
+ * @date: 2022/11/1
+ * description:
+ */
+
+fun View.parentRemove() {
+    try {
+        if (this.parent != null) {
+            (this.parent as ViewGroup).removeView(this)
+        }
+    }catch (e : Exception){
+
+    }
+}
+
+fun View.getActivityFromView() : Activity?{
+    var cont = context
+    while (cont is ContextWrapper){
+        if (cont is Activity){
+            return cont as Activity
+        }
+        cont = cont.baseContext
+    }
+    return null
+}
+
+/**
+ * @方法说明:网络是否可用
+ * @方法名称:isNetworkAvailable
+ * @param context
+ * @return
+ * @返回值:boolean
+ */
+@SuppressLint("MissingPermission")
+fun isNetworkAvailable(context: Context?): Boolean {
+    if (context == null) return false
+    try {
+        val connectivityManager = context.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
+        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            val networkCapabilities = connectivityManager.activeNetwork ?: return false
+            val actNw = connectivityManager.getNetworkCapabilities(networkCapabilities) ?: return false
+            when {
+                actNw.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ||
+                        actNw.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) ||
+                        actNw.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true
+                else -> false
+            }
+        } else {
+            connectivityManager.run {
+                activeNetworkInfo?.run {
+                    when (type) {
+                        ConnectivityManager.TYPE_WIFI,
+                        ConnectivityManager.TYPE_MOBILE,
+                        ConnectivityManager.TYPE_ETHERNET -> true
+                        else -> false
+                    }
+                } ?: false
+            }
+        }
+    }catch (e : Exception){
+        return false
+    }
+
+}
+
+
+
+
+/**
+ * 屏幕宽度
+ */
+fun getScreenWidth(context: Context): Int = context.resources?.displayMetrics?.widthPixels ?: 0
+
+/**
+ * 获取屏幕密度
+ */
+fun getDensity(context: Context): Float = context.resources?.displayMetrics?.density ?: 0F
+
+/**
+ * 屏幕高度
+ */
+fun getScreenHeight(context: Context): Int =context.resources?.displayMetrics?.heightPixels ?: 0
+
+fun getScreenWidthDp(context: Context) : Float = getScreenWidth(context) / (if (getDensity(context) <= 0) 1F else getDensity(context)) + 0.5f
+
+fun getScreenHeightDp(context: Context) : Float = px2dip(context, getScreenHeight(context).toFloat()).toFloat()
+
+
+fun px2dip(context: Context,pxValue: Float): Int {
+    val scale = getDensity(context)
+    return (pxValue / scale + 0.5f).toInt()
+}

+ 41 - 4
lib_ad_core/src/main/java/com/composition/android/lib/ad/util/AdLog.kt

@@ -2,10 +2,12 @@ package com.composition.android.lib.ad.util
 
 import android.util.Log
 import com.composition.android.lib.ad.AdManager
+import com.composition.android.lib.ad.bean.AdUnitBean
+import com.composition.android.lib.ad.interfaces.IAdFormatRequest
 
 
-fun adLog(tag : String = AdManager.TAG, msg : String?){
-    if (AdManager.instance.logEnable){
+fun adLog(tag: String = AdManager.TAG, msg: String?) {
+    if (AdManager.instance.logEnable) {
         msg?.let {
             Log.i(tag, msg)
         }
@@ -13,10 +15,45 @@ fun adLog(tag : String = AdManager.TAG, msg : String?){
 }
 
 
-fun adLogE(tag : String, msg : String?){
-    if (AdManager.instance.logEnable){
+fun adLogE(tag: String, msg: String?) {
+    if (AdManager.instance.logEnable) {
         msg?.let {
             Log.e(tag, msg)
         }
     }
 }
+
+
+fun IAdFormatRequest.printAdFailInfo(code: Int, msg: String?) {
+    adLog(
+        AdManager.TAG,
+        "-------------------------------------------------------------------"
+    )
+
+    adLog(AdManager.TAG, "广告加载失败")
+    adLog(AdManager.TAG, "Code:${code}")
+    adLog(AdManager.TAG, "错误信息:${msg}")
+
+    adLog(
+        AdManager.TAG,
+        "-------------------------------------------------------------------"
+    )
+}
+
+fun IAdFormatRequest.printAdInfo(adBean: AdUnitBean) {
+    adLog(
+        AdManager.TAG,
+        "-------------------------------------------------------------------"
+    )
+
+    adLog(AdManager.TAG, "广告加载成功")
+    adLog(AdManager.TAG, "广告商:${adBean.advertisersName}")
+    adLog(AdManager.TAG, "广告位:${adBean.adSlotName}")
+    adLog(AdManager.TAG, "广告ID:${adBean.adUnitId}")
+    adLog(AdManager.TAG, "广告类型:${adBean.adType}")
+
+    adLog(
+        AdManager.TAG,
+        "-------------------------------------------------------------------"
+    )
+}

+ 16 - 3
lib_ad_core/src/main/java/com/composition/android/lib/ad/widget/BannerAdView.kt

@@ -12,7 +12,7 @@ import com.composition.android.lib.ad.R
 import com.composition.android.lib.ad.basic.AdResult
 import com.composition.android.lib.ad.basic.buildBannerAdViewHolder
 import com.composition.android.lib.ad.impl.NormalAdListener
-import com.convenient.android.common.extension.getActivityFromView
+import com.composition.android.lib.ad.util.getActivityFromView
 import kotlinx.coroutines.flow.firstOrNull
 import kotlinx.coroutines.launch
 
@@ -31,6 +31,8 @@ class BannerAdView : LinearLayout{
      */
     private var adSlotName : String = ""
 
+    private var adResult : AdResult? = null
+
     init {
         orientation = VERTICAL
     }
@@ -54,18 +56,26 @@ class BannerAdView : LinearLayout{
         loadAd()
     }
 
+    override fun onDetachedFromWindow() {
+        super.onDetachedFromWindow()
+        destroyAd()
+    }
+
+
     private fun loadAd(){
         findViewTreeLifecycleOwner()?.lifecycleScope?.launch {
-            val adResult = AdLoad.loadAd(context, adSlotName)
+            destroyAd()
+            adResult = AdLoad.loadAd(context, adSlotName)
                 .firstOrNull()
             if (adResult is AdResult.Success){
-                AdLoad.getBannerAdView(context, adResult, buildBannerAdViewHolder {
+                AdLoad.getBannerAdView(context, adResult as AdResult.Success, buildBannerAdViewHolder {
                     this.activity = getActivityFromView()
                     this.adListener = object : NormalAdListener(){
                         override fun onAdDisLike() {
                             super.onAdDisLike()
                             isVisible = false
                             removeAllViews()
+                            destroyAd()
                         }
                     }
                 })?.let {
@@ -77,6 +87,9 @@ class BannerAdView : LinearLayout{
         }
     }
 
+    private fun destroyAd(){
+        AdLoad.destroyAd(adResult)
+    }
 
     /**
      * 更新广告

+ 101 - 51
lib_ad_core/src/main/java/com/composition/android/lib/ad/widget/NativeAdView.kt

@@ -3,6 +3,7 @@ package com.composition.android.lib.ad.widget
 import android.content.Context
 import android.content.res.TypedArray
 import android.util.AttributeSet
+import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
@@ -20,7 +21,7 @@ import com.composition.android.lib.ad.basic.NativeAdViewHolder
 import com.composition.android.lib.ad.basic.buildAdNativeViewHolder
 import com.composition.android.lib.ad.impl.NormalAdListener
 import com.composition.android.lib.ad.util.adLogE
-import com.convenient.android.common.extension.getActivityFromView
+import com.composition.android.lib.ad.util.getActivityFromView
 import kotlinx.coroutines.flow.firstOrNull
 import kotlinx.coroutines.launch
 
@@ -64,22 +65,27 @@ class NativeAdView : LinearLayout {
 
     private var viewHolder: NativeAdViewHolder? = null
 
+    private var adResult: AdResult? = null
+
     /**
      * NativeAdViewHolder 中view的id -------->
      */
-    private var adTitleId: Int = 0
+    private var adTitleViewId: Int = 0
+
+    private var adTitleDescViewId: Int = 0
+
+    private var adIconViewId = 0
 
-    private var adTitleDescId: Int = 0
+    private var adMediaContentViewId = 0
 
-    private var adIconId = 0
+    private var adCallToActionViewId = 0
 
-    private var adMediaContentId = 0
+    private var adAdvertisersLogoViewId = 0
 
-    private var adCallToActionId = 0
+    private var adCloseIconViewId = 0
 
-    private var adRatingBarId = 0
+    private var advertisersContainerView: ViewGroup? = null
 
-    private var adRootView: View? = null
     /**
      * <-----------
      */
@@ -95,40 +101,52 @@ class NativeAdView : LinearLayout {
     private fun init(attrs: AttributeSet?) {
         val typedArray: TypedArray = context.obtainStyledAttributes(attrs, R.styleable.NativeAdView)
         //获取到配置的广告名称
-        adSlotName = typedArray.getString(R.styleable.NativeAdView_native_ad_slot_name) ?: ""
+        adSlotName = typedArray.getString(R.styleable.NativeAdView_ad_slot_name) ?: ""
 
         val adUnitBean = AdUnitConfigManager.instance.getAdUnitBySlotName(adSlotNames = listOf(adSlotName)).getOrNull(0)
-        if (adUnitBean == null){
+        if (adUnitBean == null) {
             adLogE(AdManager.TAG, "配置的广告位数据不存在, 当前广告位:${adSlotName}")
         }
         when (adUnitBean?.getAdvertisers()) {
-            Advertisers.Admob -> attachedAdvertisersNativeAdViewToRoot(typedArray, R.styleable.NativeAdView_native_admob_advertisers_root_view)
-            Advertisers.CSJ -> adRootView = this
-            Advertisers.GroMore -> attachedAdvertisersNativeAdViewToRoot(typedArray, R.styleable.NativeAdView_native_gro_more_advertisers_root_view)
+            Advertisers.Admob -> attachedAdvertisersNativeAdViewToRoot(typedArray, R.styleable.NativeAdView_ad_admob_native_ad_view_layout)
+            Advertisers.CSJ -> advertisersContainerView = this
+            Advertisers.GroMore -> attachedAdvertisersNativeAdViewToRoot(typedArray, R.styleable.NativeAdView_ad_gro_more_tt_native_ad_view_layout)
             Advertisers.AppLovinMax -> attachedAdvertisersNativeAdViewToRoot(typedArray, R.styleable.NativeAdView_native_app_lovin_max_advertisers_root_view)
-            Advertisers.CUSTOM -> attachedAdvertisersNativeAdViewToRoot(typedArray, R.styleable.NativeAdView_native_custom_advertisers_root_view)
+            Advertisers.CUSTOM -> advertisersContainerView = this
             else -> {}
         }
 
-        adTitleId = typedArray.getResourceId(R.styleable.NativeAdView_ad_view_title, 0)
-        adTitleDescId = typedArray.getResourceId(R.styleable.NativeAdView_ad_view_title_desc, 0)
-        adIconId = typedArray.getResourceId(R.styleable.NativeAdView_ad_view_icon, 0)
-        adMediaContentId = typedArray.getResourceId(R.styleable.NativeAdView_ad_view_media_content_group, 0)
-        adCallToActionId = typedArray.getResourceId(R.styleable.NativeAdView_ad_view_call_to_action, 0)
-        adRatingBarId = typedArray.getResourceId(R.styleable.NativeAdView_ad_view_rating_bar, 0)
-
+        adTitleViewId = typedArray.getResourceId(R.styleable.NativeAdView_ad_view_title_id, 0)
+        adTitleDescViewId = typedArray.getResourceId(R.styleable.NativeAdView_ad_view_title_desc_id, 0)
+        adIconViewId = typedArray.getResourceId(R.styleable.NativeAdView_ad_view_icon_id, 0)
+        adMediaContentViewId = typedArray.getResourceId(R.styleable.NativeAdView_ad_view_media_content_group_id, 0)
+        adCallToActionViewId = typedArray.getResourceId(R.styleable.NativeAdView_ad_view_call_to_action_id, 0)
+        adAdvertisersLogoViewId = typedArray.getResourceId(R.styleable.NativeAdView_ad_view_advertisers_logo_id, 0)
+        adCloseIconViewId = typedArray.getResourceId(R.styleable.NativeAdView_ad_view_close_ad_icon_id, 0)
         autoPopulate = typedArray.getBoolean(R.styleable.NativeAdView_ad_auto_populate, true)
-        typedArray.recycle()
 
+        typedArray.recycle()
     }
 
-    override fun addView(child: View?, params: ViewGroup.LayoutParams?) {
-        if (childCount != 0 && getChildAt(0) is ViewGroup){
-            //把写的原生广告子布局添加到对应的广告商ViewGroup中
-            (getChildAt(0) as ViewGroup).addView(child, params)
-        }else{
-            //这里第一次会在 attachedAdvertisersNativeAdViewToRoot 中调用,把广告商的原生广告根布局添加进来
-            super.addView(child, params)
+
+    override fun addView(child: View?, index: Int, params: ViewGroup.LayoutParams?) {
+
+        val root = getChildAt(0)
+        if (childCount != 0 && root is ViewGroup) {
+            //这里会把xml 中写的自定义布局加入进来
+            (getChildAt(0) as ViewGroup).addView(child, index, params)
+            Log.e("测试", "if true, child:${child?.javaClass?.simpleName}")
+        } else {
+            //把广告商的View先加进来
+            super.addView(child, index, params)
+            Log.e("测试", "if else, child:${child?.javaClass?.simpleName}")
+
+//            if (advertisersContainerView != this) {
+//                如果广告商的view 不是当前view, 让广告商view 添加child
+//                advertisersContainerView?.addView(child, index, params)
+//            } else {
+//                super.addView(child, index, params)
+//            }
         }
     }
 
@@ -136,22 +154,29 @@ class NativeAdView : LinearLayout {
     override fun onAttachedToWindow() {
         super.onAttachedToWindow()
         initViewHolder()
-        if (autoPopulate){
+        if (autoPopulate) {
             loadAd()
         }
     }
 
-    private fun initViewHolder(){
-        if (viewHolder == null){
+    override fun onDetachedFromWindow() {
+        super.onDetachedFromWindow()
+        destroyAd()
+    }
+
+    private fun initViewHolder() {
+        if (viewHolder == null) {
             viewHolder = buildAdNativeViewHolder {
-                this.titleView = findViewById(adTitleId)
-                this.titleDescView = findViewById(adTitleDescId)
-                this.iconView = findViewById(adIconId)
-                this.callActionView = findViewById(adCallToActionId)
-                this.ratingBar = findViewById(adRatingBarId)
-                this.contentMediaGroup = findViewById(adMediaContentId)
+                this.customLayoutRootView = advertisersContainerView
+                this.titleView = findViewById(adTitleViewId)
+                this.titleDescView = findViewById(adTitleDescViewId)
+                this.iconView = findViewById(adIconViewId)
+                this.callActionButtonView = findViewById(adCallToActionViewId)
+                this.contentMediaViewGroup = findViewById(adMediaContentViewId)
+                this.advertiserLogoImageView = findViewById(adAdvertisersLogoViewId)
+                this.dislikeView = findViewById(adCloseIconViewId)
                 this.activity = getActivityFromView()
-                this.adListener = object : NormalAdListener(){
+                this.adListener = object : NormalAdListener() {
                     override fun onAdDisLike() {
                         super.onAdDisLike()
                         isVisible = false
@@ -163,40 +188,65 @@ class NativeAdView : LinearLayout {
     }
 
     private fun attachedAdvertisersNativeAdViewToRoot(typedArray: TypedArray, id: Int) {
-        return typedArray.getResourceId(id, -1).let {
+        typedArray.getResourceId(id, -1).let {
             if (it != -1) {
-                LayoutInflater.from(context).inflate(it, this, true)
-                adRootView = getChildAt(0)
+                Log.e("测试", "开始解析厂商布局")
+                LayoutInflater.from(context).inflate(it, this, true) as ViewGroup
+                val root = getChildAt(0) as ViewGroup
+                advertisersContainerView = root
+                Log.e("测试", "厂商布局解析完成: root${root.javaClass.simpleName}")
+
             }
         }
     }
 
-    private fun loadAd(){
+    fun loadAd() {
         findViewTreeLifecycleOwner()?.lifecycleScope?.launch {
-            val adResult = AdLoad.loadAd(context, adSlotName).firstOrNull()
-            if (adResult != null && adResult is AdResult.Success && viewHolder != null && adRootView != null){
-                AdLoad.populateNativeAdView(context, adResult, viewHolder!!, adRootView!!)
+            destroyAd()
+            adResult = AdLoad.loadAd(context, adSlotName).firstOrNull()
+            initViewHolder()
+            if (adResult != null && adResult is AdResult.Success && viewHolder != null && advertisersContainerView != null) {
+                AdLoad.populateNativeAdView(context, adResult as AdResult.Success, viewHolder!!, advertisersContainerView!!)
                 isVisible = true
             }
+
+            if (advertisersContainerView == null){
+                adLogE(tag = "原生广告NativeAdView", msg = "厂商的原生广告容器为null, 请指定 ad_admob_native_ad_view_layout 或 其他属性")
+            }
         }
     }
 
+
+    private fun destroyAd() {
+        AdLoad.destroyAd(adResult)
+    }
+
+
     /**
      * 可以不使用View中的自动加载,在这里进行自行填充,非常适用于在RecyclerView中使用
      */
-    fun populateNativeAd(adResult: AdResult.Success){
+    fun populateNativeAd(adResult: AdResult.Success) {
         initViewHolder()
-        if (viewHolder!= null && adRootView != null){
-            AdLoad.populateNativeAdView(context, adResult, viewHolder!!, adRootView!!)
+        if (viewHolder != null && advertisersContainerView != null) {
+            AdLoad.populateNativeAdView(context, adResult, viewHolder!!, advertisersContainerView!!)
         }
     }
 
 
-    fun refreshAd(){
+    fun setAdSlotNameAndLoadAd(adSlotName: String) {
+        this.adSlotName = adSlotName
+        loadAd()
+    }
+
+
+    /**
+     * 刷新广告
+     */
+    fun refreshAd() {
         loadAd()
     }
 
-    fun removeAd(){
+    fun removeAd() {
         isVisible = false
     }
 

+ 19 - 13
lib_ad_core/src/main/res/values/attr.xml

@@ -1,31 +1,37 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
 
-
+    <attr name="ad_slot_name" format="string"/>
     <declare-styleable name="BannerAdView">
-        <attr name="ad_slot_name" format="string"/>
+
+        <attr name="ad_slot_name" />
     </declare-styleable>
 
 
     <declare-styleable name="NativeAdView">
         <!-- NativeAdViewHolder -->
-        <attr name="ad_view_title" format="reference"/>
-        <attr name="ad_view_title_desc" format="reference"/>
-        <attr name="ad_view_icon" format="reference"/>
-        <attr name="ad_view_media_content_group" format="reference"/>
-        <attr name="ad_view_call_to_action" format="reference"/>
-        <attr name="ad_view_rating_bar" format="reference"/>
+
+        <attr name="ad_slot_name" />
+        <attr name="ad_auto_populate" format="boolean"/>
+
+        <attr name="ad_view_title_id" format="reference"/>
+        <attr name="ad_view_title_desc_id" format="reference"/>
+        <attr name="ad_view_icon_id" format="reference"/>
+        <attr name="ad_view_media_content_group_id" format="reference"/>
+        <attr name="ad_view_call_to_action_id" format="reference"/>
+        <attr name="ad_view_advertisers_logo_id" format="reference"/>
+        <attr name="ad_view_close_ad_icon_id" format="reference"/>
+
         <!-- 这里指定广告商提供的原生广告根节点view, 各个广告库中已经写了默认的-->
         <!-- lib_ad_admob/res/layout/layout_admob_native_ad_view_root -->
 
-        <attr name="native_admob_advertisers_root_view" format="reference"/>
-        <attr name="native_gro_more_advertisers_root_view" format="reference"/>
+        <attr name="ad_admob_native_ad_view_layout" format="reference"/>
 
+        <attr name="ad_gro_more_tt_native_ad_view_layout" format="reference"/>
+
+        <!-- 暂未使用 -->
         <attr name="native_app_lovin_max_advertisers_root_view" format="reference"/>
-        <attr name="native_custom_advertisers_root_view" format="reference"/>
-        <attr name="native_ad_slot_name" format="string"/>
 
-        <attr name="ad_auto_populate" format="boolean"/>
 
     </declare-styleable>
 

+ 26 - 2
lib_ad_csj/build.gradle

@@ -2,6 +2,10 @@ plugins {
     id 'com.android.library'
     id 'org.jetbrains.kotlin.android'
 }
+def version = '1.0.0'
+def releaseTime() {
+    return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
+}
 
 android {
     compileSdk = rootProject.ext.compileSdkVersion
@@ -16,9 +20,28 @@ android {
     }
 
     buildTypes {
+
+        debug{
+            libraryVariants.all { variant->
+                variant.outputs.all { output ->
+                    if (outputFile != null && outputFileName.endsWith('.aar')) {
+                        outputFileName = "ad_csj_${version}_${releaseTime()}_debug.aar"
+                    }
+                }
+            }
+        }
+
         release {
             minifyEnabled false
             proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+
+            libraryVariants.all { variant->
+                variant.outputs.all { output->
+                    if (outputFile != null && outputFileName.endsWith('.aar')) {
+                        outputFileName = "ad_csj_${version}_${releaseTime()}_release.aar"
+                    }
+                }
+            }
         }
     }
     compileOptions {
@@ -32,9 +55,10 @@ android {
 
 dependencies {
     implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs')
-    implementation project(':lib_ad_core')
+    compileOnly project(':lib_ad_core')
+    api 'com.pangle.cn:ads-sdk-pro:4.8.0.8'
 
-    api 'androidx.legacy:legacy-support-v4:1.0.0'
+    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
     implementation 'androidx.core:core-ktx:1.8.0'
     implementation 'androidx.appcompat:appcompat:1.5.1'
     implementation 'com.google.android.material:material:1.6.1'

BIN
lib_ad_csj/libs/open_ad_sdk.aar


+ 2 - 4
lib_ad_csj/src/androidTest/java/com/composition/android/ad/csj/ExampleInstrumentedTest.kt

@@ -1,13 +1,11 @@
 package com.composition.android.ad.csj
 
-import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.ext.junit.runners.AndroidJUnit4
-
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.Assert.*
 import org.junit.Test
 import org.junit.runner.RunWith
 
-import org.junit.Assert.*
-
 /**
  * Instrumented test, which will execute on an Android device.
  *

+ 41 - 0
lib_ad_csj/src/main/java/com/composition/android/ad/csj/impl/CSJAppDownloadListener.kt

@@ -0,0 +1,41 @@
+package com.composition.android.ad.csj.impl
+
+import android.content.Context
+import com.bytedance.sdk.openadsdk.TTAppDownloadListener
+import com.composition.android.ad.csj.R
+import com.composition.android.lib.ad.basic.NativeAdViewHolder
+
+/**
+ * @classname:
+ * @author: LiuXiaoLong
+ * @date: 2022/10/25
+ * description:穿山甲广告下载监听
+ */
+class CSJAppDownloadListener(var context: Context, var nativeAdViewHolder: NativeAdViewHolder?) : TTAppDownloadListener {
+    override fun onIdle() {
+        nativeAdViewHolder?.callActionButtonView?.text = context.getString(R.string.start_download)
+    }
+
+    override fun onDownloadActive(p0: Long, p1: Long, p2: String?, p3: String?) {
+        nativeAdViewHolder?.callActionButtonView?.text = context.getString(R.string.downloading)
+    }
+
+    override fun onDownloadPaused(p0: Long, p1: Long, p2: String?, p3: String?) {
+        nativeAdViewHolder?.callActionButtonView?.text = context.getString(R.string.download_pause)
+    }
+
+    override fun onDownloadFailed(p0: Long, p1: Long, p2: String?, p3: String?) {
+        nativeAdViewHolder?.callActionButtonView?.text = context.getString(R.string.download_again)
+
+    }
+
+    override fun onDownloadFinished(p0: Long, p1: String?, p2: String?) {
+        nativeAdViewHolder?.callActionButtonView?.text = context.getString(R.string.click_to_open)
+
+    }
+
+    override fun onInstalled(p0: String?, p1: String?) {
+        nativeAdViewHolder?.callActionButtonView?.text = context.getString(R.string.click_to_install)
+
+    }
+}

+ 3 - 4
lib_ad_csj/src/main/java/com/composition/android/ad/csj/impl/CSJInitialize.kt

@@ -8,7 +8,6 @@ import com.bytedance.sdk.openadsdk.TTCustomController
 import com.composition.android.lib.ad.AdManager
 import com.composition.android.lib.ad.interfaces.Initialize
 import com.composition.android.lib.ad.util.adLogE
-import com.convenient.android.common.config.MyPdfBaseModule
 
 /**
  * @classname:
@@ -26,7 +25,7 @@ class CSJInitialize(var appId: String) : Initialize {
             .appName(context.packageName)
             .titleBarTheme(TTAdConstant.TITLE_BAR_THEME_DARK)
             .allowShowNotify(true)
-            .debug(MyPdfBaseModule.isDebug)
+            .debug(AdManager.instance.isDebug)
             .supportMultiProcess(false)
             .asyncInit(true)
             .needClearTaskReset()
@@ -36,11 +35,11 @@ class CSJInitialize(var appId: String) : Initialize {
                 }
 
                 override fun isCanUsePhoneState(): Boolean {
-                    return MyPdfBaseModule.isAgreeServerAgreement
+                    return false
                 }
 
                 override fun isCanUseAndroidId(): Boolean {
-                    return MyPdfBaseModule.isAgreeServerAgreement
+                    return false
                 }
 
 

+ 17 - 0
lib_ad_csj/src/main/java/com/composition/android/ad/csj/impl/CSJNativeAdImageSize.kt

@@ -0,0 +1,17 @@
+package com.composition.android.ad.csj.impl
+
+import com.composition.android.lib.ad.interfaces.IAdSize
+
+/**
+ * @classname:
+ * @author: LiuXiaoLong
+ * @date: 2022/9/13
+ * description:穿山甲原生广告图片尺寸
+ */
+class CSJNativeAdImageSize(var width : Int, var height : Int) : IAdSize<Pair<Int, Int>> {
+
+    override fun getAdSize(): Pair<Int, Int> {
+        return width to width
+    }
+
+}

+ 87 - 37
lib_ad_csj/src/main/java/com/composition/android/ad/csj/load/CSJAdLoader.kt

@@ -8,6 +8,7 @@ import android.view.ViewGroup
 import com.bytedance.sdk.openadsdk.*
 import com.composition.android.ad.csj.load.request.*
 import com.composition.android.ad.csj.load.view.CSJBannerView
+import com.composition.android.ad.csj.load.view.CSJNativeAdView
 import com.composition.android.ad.csj.load.view.CSJNativeTemplateAdView
 import com.composition.android.lib.ad.AdManager
 import com.composition.android.lib.ad.basic.*
@@ -15,6 +16,7 @@ import com.composition.android.lib.ad.bean.AdUnitBean
 import com.composition.android.lib.ad.interfaces.AdListener
 import com.composition.android.lib.ad.interfaces.AdLoader
 import com.composition.android.lib.ad.util.adLog
+import com.composition.android.lib.ad.util.parentRemove
 
 /**
  * @classname:
@@ -25,6 +27,9 @@ import com.composition.android.lib.ad.util.adLog
 class CSJAdLoader : AdLoader {
 
 
+    override val advertisers: Advertisers
+        get() = Advertisers.CSJ
+
     override suspend fun load(context: Context, adUnitBean: AdUnitBean): AdResult {
         if (adUnitBean.isAvailable().not()) {
             return AdResult.Fail(adUnitBean, msg = "AdUnitBean无效")
@@ -36,6 +41,7 @@ class CSJAdLoader : AdLoader {
                 AdType.INTERSTITIAL -> CSJInterstitialRequestImpl()
                 AdType.APP_OPEN -> CSJAppOpenRequestImpl()
                 AdType.BANNER -> CSJBannerRequestImpl()
+                AdType.NATIVE -> CSJNativeRequestImpl()
                 AdType.NATIVE_TEMPLATE -> CSJNativeTemplateRequestImpl()
                 else -> null
             }
@@ -56,28 +62,51 @@ class CSJAdLoader : AdLoader {
     override fun getNativeAdView(context: Context, adResult: AdResult.Success, viewHolder: NativeAdViewHolder): View? {
         return if (adResult.adObject != null) {
             AdManager.instance.globalListener?.invoke(adResult.adBean)
-            CSJNativeTemplateAdView(context).apply {
-                setViewHolder(viewHolder)
-                render(adResult)
+            when (adResult.adBean.getAdType()) {
+                AdType.NATIVE -> {
+                    CSJNativeAdView(context).apply {
+                        setViewHolder(viewHolder)
+                        render(adResult)
+                    }
+                }
+                AdType.NATIVE_TEMPLATE -> {
+                    CSJNativeTemplateAdView(context).apply {
+                        setViewHolder(viewHolder)
+                        render(adResult)
+                    }
+                }
+                else -> {
+                    null
+                }
             }
         } else {
             null
         }
     }
 
-    override fun populateNativeAdView(context: Context, adResult: AdResult.Success, viewHolder: NativeAdViewHolder, adRootView: View) {
+
+    override fun populateNativeAdView(context: Context, adResult: AdResult.Success, viewHolder: NativeAdViewHolder, advertisersContainerView: ViewGroup) {
         AdManager.instance.globalListener?.invoke(adResult.adBean)
-        CSJNativeTemplateAdView.populateNativeAdView(context, adResult, viewHolder, adRootView)
+        if (adResult.adBean.getAdType() == AdType.NATIVE) {
+            CSJNativeAdView.populateNativeAdView(context = context, adResult, viewHolder, advertisersContainerView)
+        } else {
+            val ttNativeExpressAd = adResult.adObject as TTNativeExpressAd
+            advertisersContainerView.removeAllViews()
+            val expressAdView = ttNativeExpressAd.expressAdView
+            expressAdView.parentRemove()
+            advertisersContainerView.addView(expressAdView)
+            CSJNativeTemplateAdView.populateNativeTemplateAdView(context, adResult, viewHolder)
+        }
     }
 
-    override fun showInterstitialAd(activity: Activity, success: AdResult.Success, adListener: AdListener) {
-        if (success.adObject is TTFullScreenVideoAd) {
-            val ttFullScreenVideoAd = success.adObject as TTFullScreenVideoAd
+    override fun showInterstitialAd(activity: Activity, adResult: AdResult.Success, adListener: AdListener) {
+        if (adResult.adObject is TTFullScreenVideoAd) {
+            val ttFullScreenVideoAd = adResult.adObject as TTFullScreenVideoAd
             ttFullScreenVideoAd.setFullScreenVideoAdInteractionListener(object : TTFullScreenVideoAd.FullScreenVideoAdInteractionListener {
                 override fun onAdShow() {
                     adLog(msg = "穿山甲广告-插屏-展示")
-                    adListener.onAdShow(success.adBean)
-                    AdManager.instance.globalListener?.invoke(success.adBean)
+                    adListener.onAdShow(adResult.adBean)
+                    AdManager.instance.globalListener?.invoke(adResult.adBean)
                 }
 
                 override fun onAdVideoBarClick() {
@@ -88,7 +117,7 @@ class CSJAdLoader : AdLoader {
                 override fun onAdClose() {
                     adLog(msg = "穿山甲广告-插屏-关闭")
                     adListener.onAdClose()
-                    success.adObject = null
+                    adResult.adObject = null
                 }
 
                 override fun onVideoComplete() {
@@ -103,23 +132,25 @@ class CSJAdLoader : AdLoader {
             })
             ttFullScreenVideoAd.showFullScreenVideoAd(activity, TTAdConstant.RitScenes.CUSTOMIZE_SCENES, null)
         } else {
-            adLog(msg = "穿山甲广告-插屏-错误\n" +
-                    "类型错误,当前为:${success.adObject?.javaClass?.simpleName}, 应该为:TTNativeExpressAd")
-            adListener.onAdShowFail("类型错误,当前为:${success.adObject?.javaClass?.simpleName}, 应该为:TTNativeExpressAd")
+            adLog(
+                msg = "穿山甲广告-插屏-错误\n" +
+                        "类型错误,当前为:${adResult.adObject?.javaClass?.simpleName}, 应该为:TTNativeExpressAd"
+            )
+            adListener.onAdShowFail("类型错误,当前为:${adResult.adObject?.javaClass?.simpleName}, 应该为:TTNativeExpressAd")
         }
     }
 
-    override fun showRewardedAd(activity: Activity, success: AdResult.Success, adListener: AdListener) {
+    override fun showRewardedAd(activity: Activity, adResult: AdResult.Success, adListener: AdListener) {
 
-        if (success.adObject is TTRewardVideoAd) {
-            val ttRewardVideoAd = success.adObject as TTRewardVideoAd
+        if (adResult.adObject is TTRewardVideoAd) {
+            val ttRewardVideoAd = adResult.adObject as TTRewardVideoAd
 
 
             ttRewardVideoAd.setRewardAdInteractionListener(object : TTRewardVideoAd.RewardAdInteractionListener {
                 override fun onAdShow() {
                     adLog(msg = "穿山甲广告-激励视频-展示")
-                    adListener.onAdShow(success.adBean)
-                    AdManager.instance.globalListener?.invoke(success.adBean)
+                    adListener.onAdShow(adResult.adBean)
+                    AdManager.instance.globalListener?.invoke(adResult.adBean)
                 }
 
                 override fun onAdVideoBarClick() {
@@ -159,23 +190,25 @@ class CSJAdLoader : AdLoader {
             ttRewardVideoAd.showRewardVideoAd(activity)
 
         } else {
-            adLog(msg = "穿山甲广告-激励视频-异常\n" +
-                    "类型错误,当前类型为:${success.adObject?.javaClass?.simpleName}, 应为:TTRewardVideoAd")
+            adLog(
+                msg = "穿山甲广告-激励视频-异常\n" +
+                        "类型错误,当前类型为:${adResult.adObject?.javaClass?.simpleName}, 应为:TTRewardVideoAd"
+            )
 
-            adListener.onAdShowFail("类型错误,当前类型为:${success.adObject?.javaClass?.simpleName}, 应为:TTRewardVideoAd")
+            adListener.onAdShowFail("类型错误,当前类型为:${adResult.adObject?.javaClass?.simpleName}, 应为:TTRewardVideoAd")
         }
 
 
     }
 
-    override fun showRewardedInterstitialAd(activity: Activity, success: AdResult.Success, adListener: AdListener) {
+    override fun showRewardedInterstitialAd(activity: Activity, adResult: AdResult.Success, adListener: AdListener) {
         adLog(msg = "穿山甲广告-激励插屏-不支持的广告类型")
         adListener.onAdShowFail("穿山甲广告不支持激励插屏广告,请使用激励视频广告")
     }
 
-    override fun showSplashAd(activity: Activity, splashViewGroup: ViewGroup?, success: AdResult.Success, adListener: AdListener) {
-        if (success.adObject is TTSplashAd) {
-            val ttSplashAd = success.adObject as TTSplashAd
+    override fun showSplashAd(activity: Activity, splashViewGroup: ViewGroup?, adResult: AdResult.Success, adListener: AdListener) {
+        if (adResult.adObject is TTSplashAd) {
+            val ttSplashAd = adResult.adObject as TTSplashAd
             ttSplashAd.setSplashInteractionListener(object : TTSplashAd.AdInteractionListener {
                 override fun onAdClicked(p0: View?, p1: Int) {
                     adLog(msg = "穿山甲广告-开屏-点击")
@@ -184,8 +217,8 @@ class CSJAdLoader : AdLoader {
 
                 override fun onAdShow(p0: View?, p1: Int) {
                     adLog(msg = "穿山甲广告-开屏-展示")
-                    adListener.onAdShow(success.adBean)
-                    AdManager.instance.globalListener?.invoke(success.adBean)
+                    adListener.onAdShow(adResult.adBean)
+                    AdManager.instance.globalListener?.invoke(adResult.adBean)
                 }
 
                 override fun onAdSkip() {
@@ -201,21 +234,38 @@ class CSJAdLoader : AdLoader {
             splashViewGroup?.removeAllViews()
             splashViewGroup?.addView(ttSplashAd.splashView)
         } else {
-            adLog(msg = "穿山甲广告-开屏-异常\n" +
-                    "类型错误,当前类型为:${success.adObject?.javaClass?.simpleName}, 应为:TTRewardVideoAd")
-            adListener.onAdShowFail("类型错误,当前类型为:${success.adObject?.javaClass?.simpleName}, 应为:TTRewardVideoAd")
+            adLog(
+                msg = "穿山甲广告-开屏-异常\n" +
+                        "类型错误,当前类型为:${adResult.adObject?.javaClass?.simpleName}, 应为:TTRewardVideoAd"
+            )
+            adListener.onAdShowFail("类型错误,当前类型为:${adResult.adObject?.javaClass?.simpleName}, 应为:TTRewardVideoAd")
         }
     }
 
-    override fun showFullScreenAd(activity: Activity, success: AdResult.Success, adListener: AdListener) {
-        when (success.adBean.getAdType()) {
-            AdType.INTERSTITIAL -> showInterstitialAd(activity, success, adListener)
-            AdType.REWARDED_VIDEO -> showRewardedAd(activity, success, adListener)
+    override fun showFullScreenAd(activity: Activity, adResult: AdResult.Success, adListener: AdListener) {
+        when (adResult.adBean.getAdType()) {
+            AdType.INTERSTITIAL -> showInterstitialAd(activity, adResult, adListener)
+            AdType.REWARDED_VIDEO -> showRewardedAd(activity, adResult, adListener)
             else -> {
-                adLog(msg = "穿山甲广告-全屏-不支持的广告类型:${success.adBean.adType}")
-                adListener.onAdShowFail("穿山甲广告-全屏-不支持的广告类型:${success.adBean.adType}")
+                adLog(msg = "穿山甲广告-全屏-不支持的广告类型:${adResult.adBean.adType}")
+                adListener.onAdShowFail("穿山甲广告-全屏-不支持的广告类型:${adResult.adBean.adType}")
             }
         }
+    }
+
+    override fun destroyAd(adResult: AdResult) {
+        try {
+            if (adResult is AdResult.Success) {
+                when (val ad = adResult.adObject) {
+                    is TTNativeExpressAd -> ad.destroy()
+                    is TTNativeAd -> ad.destroy()
+                    else -> {}
+                }
+                adResult.adObject = null
+            }
+            adLog(msg = "CSJ广告-销毁广告对象完成")
+        }catch (e : Exception){
 
+        }
     }
 }

+ 7 - 13
lib_ad_csj/src/main/java/com/composition/android/ad/csj/load/request/CSJAppOpenRequestImpl.kt

@@ -2,16 +2,10 @@ package com.composition.android.ad.csj.load.request
 
 import android.content.Context
 import com.bytedance.sdk.openadsdk.*
-import com.composition.android.ad.csj.util.printAdFailInfo
-import com.composition.android.ad.csj.util.printAdInfo
 import com.composition.android.lib.ad.basic.AdResult
 import com.composition.android.lib.ad.bean.AdUnitBean
 import com.composition.android.lib.ad.interfaces.IAdFormatRequest
-import com.composition.android.lib.ad.util.adLog
-import com.convenient.android.common.extension.getScreenHeight
-import com.convenient.android.common.extension.getScreenHeightDp
-import com.convenient.android.common.extension.getScreenWidth
-import com.convenient.android.common.extension.getScreenWidthDp
+import com.composition.android.lib.ad.util.*
 import kotlinx.coroutines.suspendCancellableCoroutine
 import kotlin.coroutines.resume
 
@@ -26,14 +20,14 @@ class CSJAppOpenRequestImpl : IAdFormatRequest {
 
         return suspendCancellableCoroutine {
             //step3:创建开屏广告请求参数AdSlot,具体参数含义参考文档
-            val splashWidthDp: Float = context.getScreenWidthDp()
-            val splashWidthPx: Int = context.getScreenWidth()
-            val screenHeightPx: Int = context.getScreenHeight()
-            val screenHeightDp: Float = context.getScreenHeightDp()
+            val splashWidthDp: Float = getScreenWidthDp(context)
+            val splashWidthPx: Int = getScreenWidth(context)
+            val screenHeightPx: Int = getScreenHeight(context)
+            val screenHeightDp: Float = getScreenHeightDp(context)
 
             // 开屏高度 = 屏幕高度 - 下方预留的高度,demo中是预留了屏幕高度的1/5,因此开屏高度传入 屏幕高度*4/5
-            val splashHeightDp: Float = screenHeightDp * 4 / 5f
-            val splashHeightPx: Int = (screenHeightPx * 4 / 5f).toInt()
+            val splashHeightDp: Float = screenHeightDp * 4 / 4f
+            val splashHeightPx: Int = (screenHeightPx * 4 / 4f).toInt()
 
             val adSlot = AdSlot.Builder()
                 .setCodeId(adUnitBean.adUnitId)

+ 2 - 2
lib_ad_csj/src/main/java/com/composition/android/ad/csj/load/request/CSJBannerRequestImpl.kt

@@ -3,12 +3,12 @@ package com.composition.android.ad.csj.load.request
 import android.content.Context
 import com.bytedance.sdk.openadsdk.*
 import com.composition.android.ad.csj.impl.CSJBannerAdSize
-import com.composition.android.ad.csj.util.printAdFailInfo
-import com.composition.android.ad.csj.util.printAdInfo
 import com.composition.android.lib.ad.basic.AdResult
 import com.composition.android.lib.ad.bean.AdUnitBean
 import com.composition.android.lib.ad.interfaces.IAdFormatRequest
 import com.composition.android.lib.ad.util.adLog
+import com.composition.android.lib.ad.util.printAdFailInfo
+import com.composition.android.lib.ad.util.printAdInfo
 import kotlinx.coroutines.suspendCancellableCoroutine
 import kotlin.coroutines.resume
 

+ 2 - 2
lib_ad_csj/src/main/java/com/composition/android/ad/csj/load/request/CSJInterstitialRequestImpl.kt

@@ -2,12 +2,12 @@ package com.composition.android.ad.csj.load.request
 
 import android.content.Context
 import com.bytedance.sdk.openadsdk.*
-import com.composition.android.ad.csj.util.printAdFailInfo
-import com.composition.android.ad.csj.util.printAdInfo
 import com.composition.android.lib.ad.basic.AdResult
 import com.composition.android.lib.ad.bean.AdUnitBean
 import com.composition.android.lib.ad.interfaces.IAdFormatRequest
 import com.composition.android.lib.ad.util.adLog
+import com.composition.android.lib.ad.util.printAdFailInfo
+import com.composition.android.lib.ad.util.printAdInfo
 import kotlinx.coroutines.suspendCancellableCoroutine
 import kotlin.coroutines.resume
 

+ 63 - 0
lib_ad_csj/src/main/java/com/composition/android/ad/csj/load/request/CSJNativeRequestImpl.kt

@@ -0,0 +1,63 @@
+package com.composition.android.ad.csj.load.request
+
+import android.content.Context
+import com.bytedance.sdk.openadsdk.*
+import com.composition.android.ad.csj.impl.CSJNativeAdImageSize
+import com.composition.android.lib.ad.basic.AdResult
+import com.composition.android.lib.ad.bean.AdUnitBean
+import com.composition.android.lib.ad.interfaces.IAdFormatRequest
+import com.composition.android.lib.ad.util.printAdFailInfo
+import com.composition.android.lib.ad.util.printAdInfo
+import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlin.coroutines.resume
+
+/**
+ * @classname:
+ * @author: LiuXiaoLong
+ * @date: 2022/9/13
+ * description:穿山甲原生广告加载类
+ */
+class CSJNativeRequestImpl : IAdFormatRequest {
+
+    override suspend fun load(context: Context, adUnitBean: AdUnitBean): AdResult {
+
+        return suspendCancellableCoroutine {
+
+            val adSize = if (adUnitBean.adSize != null && adUnitBean.adSize is CSJNativeAdImageSize){
+                val size = adUnitBean.adSize as CSJNativeAdImageSize
+                size.width to size.height
+            } else {
+                640 to 320
+            }
+            val adSlot = AdSlot.Builder()
+                .setCodeId(adUnitBean.adUnitId)
+                .setSupportDeepLink(true)
+                .setAdCount(1)
+                .setImageAcceptedSize(adSize.first, adSize.second)
+                .setNativeAdType(AdSlot.TYPE_BANNER)
+                .setAdLoadType(TTAdLoadType.PRELOAD)
+                .build()
+
+            TTAdSdk.getAdManager().createAdNative(context)
+                .loadNativeAd(adSlot, object : TTAdNative.NativeAdListener {
+                    override fun onError(p0: Int, p1: String?) {
+                        printAdFailInfo(p0, p1)
+                        if (it.isActive) {
+                            it.resume(AdResult.Fail(adUnitBean, msg = p1 ?: "穿山甲广告-原生-加载失败"))
+                        }
+                    }
+
+                    override fun onNativeAdLoad(p0: MutableList<TTNativeAd>?) {
+                        printAdInfo(adUnitBean)
+                        if (it.isActive) {
+                            if (p0.isNullOrEmpty().not()) {
+                                it.resume(AdResult.Success(adBean = adUnitBean, adObject = p0?.get(0), msg = "穿山甲广告-原生-加载成功"))
+                            } else {
+                                it.resume(AdResult.Fail(adUnitBean, msg = "穿山甲广告原生-加载失败, SDK返回的广告实例为空"))
+                            }
+                        }
+                    }
+                })
+        }
+    }
+}

+ 11 - 6
lib_ad_csj/src/main/java/com/composition/android/ad/csj/load/request/CSJNativeTemplateRequestImpl.kt

@@ -2,14 +2,12 @@ package com.composition.android.ad.csj.load.request
 
 import android.content.Context
 import com.bytedance.sdk.openadsdk.*
-import com.composition.android.ad.csj.impl.CSJBannerAdSize
-import com.composition.android.ad.csj.util.printAdFailInfo
-import com.composition.android.ad.csj.util.printAdInfo
-import com.composition.android.lib.ad.AdManager
+import com.composition.android.ad.csj.impl.CSJNativeAdImageSize
 import com.composition.android.lib.ad.basic.AdResult
 import com.composition.android.lib.ad.bean.AdUnitBean
 import com.composition.android.lib.ad.interfaces.IAdFormatRequest
-import com.composition.android.lib.ad.util.adLogE
+import com.composition.android.lib.ad.util.printAdFailInfo
+import com.composition.android.lib.ad.util.printAdInfo
 import kotlinx.coroutines.suspendCancellableCoroutine
 import kotlin.coroutines.resume
 
@@ -24,10 +22,17 @@ class CSJNativeTemplateRequestImpl : IAdFormatRequest {
     override suspend fun load(context: Context, adUnitBean: AdUnitBean): AdResult {
         return suspendCancellableCoroutine {
 
+            val imageAcceptedSize = if (adUnitBean.adSize is CSJNativeAdImageSize){
+                val adSize = adUnitBean.adSize as CSJNativeAdImageSize
+                adSize.width to adSize.height
+            }else{
+                640 to 320
+            }
+
             val adSlot = AdSlot.Builder()
                 .setCodeId(adUnitBean.adUnitId)
                 .setAdCount(1)
-                .setImageAcceptedSize(640, 320)
+                .setImageAcceptedSize(imageAcceptedSize.first, imageAcceptedSize.second)
                 .setAdLoadType(TTAdLoadType.PRELOAD)
                 .build()
 

+ 2 - 2
lib_ad_csj/src/main/java/com/composition/android/ad/csj/load/request/CSJRewardRequestImpl.kt

@@ -2,11 +2,11 @@ package com.composition.android.ad.csj.load.request
 
 import android.content.Context
 import com.bytedance.sdk.openadsdk.*
-import com.composition.android.ad.csj.util.printAdFailInfo
-import com.composition.android.ad.csj.util.printAdInfo
 import com.composition.android.lib.ad.basic.AdResult
 import com.composition.android.lib.ad.bean.AdUnitBean
 import com.composition.android.lib.ad.interfaces.IAdFormatRequest
+import com.composition.android.lib.ad.util.printAdFailInfo
+import com.composition.android.lib.ad.util.printAdInfo
 import kotlinx.coroutines.suspendCancellableCoroutine
 import kotlin.coroutines.resume
 

+ 190 - 0
lib_ad_csj/src/main/java/com/composition/android/ad/csj/load/view/CSJNativeAdView.kt

@@ -0,0 +1,190 @@
+package com.composition.android.ad.csj.load.view
+
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.appcompat.widget.AppCompatImageView
+import androidx.appcompat.widget.AppCompatTextView
+import androidx.core.view.isVisible
+import com.bytedance.sdk.openadsdk.TTAdConstant
+import com.bytedance.sdk.openadsdk.TTAdDislike
+import com.bytedance.sdk.openadsdk.TTNativeAd
+import com.composition.android.ad.csj.R
+import com.composition.android.ad.csj.impl.CSJAppDownloadListener
+import com.composition.android.lib.ad.AdManager
+import com.composition.android.lib.ad.basic.AdResult
+import com.composition.android.lib.ad.basic.BasicAdView
+import com.composition.android.lib.ad.basic.NativeAdViewHolder
+import com.composition.android.lib.ad.util.adLog
+
+/**
+ * @classname:
+ * @author: LiuXiaoLong
+ * @date: 2022/9/13
+ * description: 穿山甲原生模板广告View
+ */
+class CSJNativeAdView(context: Context?) : BasicAdView<View>(context) {
+
+    private var viewHolder: NativeAdViewHolder? = null
+
+    fun setViewHolder(viewHolder: NativeAdViewHolder) {
+        this.viewHolder = viewHolder
+    }
+
+    override fun adContentView(): View? {
+        if (adResult == null || viewHolder == null) {
+            return null
+        }
+
+        populateNativeAdView(context, adResult = adResult!!, viewHolder = viewHolder!!, null)
+        return viewHolder?.customLayoutRootView
+    }
+
+    override fun onResume() {
+
+    }
+
+    override fun onPause() {
+
+    }
+
+    override fun onDestroy() {
+        val ttNativeAd = adResult?.adObject as? TTNativeAd
+        ttNativeAd?.destroy()
+    }
+
+    companion object {
+
+        fun populateNativeAdView(context: Context, adResult: AdResult.Success, viewHolder: NativeAdViewHolder, advertisersContainerView: ViewGroup?) {
+
+            val ttNativeAd = adResult.adObject as TTNativeAd
+            viewHolder.activity?.apply {
+                ttNativeAd.getDislikeDialog(this).apply {
+                    setDislikeInteractionCallback(object : TTAdDislike.DislikeInteractionCallback {
+                        override fun onShow() {
+
+                        }
+
+                        override fun onSelected(p0: Int, p1: String?, p2: Boolean) {
+                            adLog(msg = "穿山甲广告-原生-点击不喜欢按钮")
+                            viewHolder.adListener?.onAdDisLike()
+                        }
+
+                        override fun onCancel() {
+                        }
+                    })
+                    viewHolder.dislikeView?.setOnClickListener {
+                        this.showDislikeDialog()
+                    }
+                }
+
+
+            }
+
+            viewHolder.titleView?.text = ttNativeAd.title
+            (viewHolder.titleDescView as? AppCompatTextView)?.text = ttNativeAd.description
+
+
+            val mediaImageViews = LayoutInflater.from(context).inflate(R.layout.csj_native_ad_media_image_views, null)
+
+            val imageViewList = mutableListOf<View>()
+            imageViewList.add(mediaImageViews.findViewById(R.id.id_ad_media_iv_1))
+            imageViewList.add(mediaImageViews.findViewById(R.id.id_ad_media_iv_2))
+            imageViewList.add(mediaImageViews.findViewById(R.id.id_ad_media_iv_3))
+
+
+            viewHolder.contentMediaViewGroup?.apply {
+                removeAllViews()
+                addView(mediaImageViews)
+            }
+            if (ttNativeAd.imageList.isNullOrEmpty().not()) {
+
+                for (i in 0..2) {
+                    val imageItem = ttNativeAd.imageList.getOrNull(i)
+                    if (imageItem != null) {
+                        if (imageItem.isValid) {
+                            viewHolder.mediaImageLoader?.displayImage(context, imageItem.imageUrl, imageViewList[i] as AppCompatImageView)
+                        }
+                    } else {
+                        imageViewList.getOrNull(i)?.isVisible = false
+                    }
+                }
+            }
+            if (ttNativeAd.icon != null && ttNativeAd.icon.isValid && viewHolder.iconView != null) {
+                viewHolder.iconImageLoader?.displayImage(context, ttNativeAd.icon.imageUrl, viewHolder.iconView as AppCompatImageView)
+            }
+            when (ttNativeAd.interactionType) {
+                TTAdConstant.INTERACTION_TYPE_DOWNLOAD -> {
+                    ttNativeAd.setActivityForDownloadApp(viewHolder.activity)
+                    viewHolder.callActionButtonView?.isVisible = true
+                    ttNativeAd.setDownloadListener(CSJAppDownloadListener(context, viewHolder))
+                }
+                TTAdConstant.INTERACTION_TYPE_DIAL -> {
+                    viewHolder.callActionButtonView?.also {
+                        it.isVisible = true
+                        it.text = context.getString(R.string.call_now)
+                    }
+                }
+                TTAdConstant.INTERACTION_TYPE_LANDING_PAGE -> {
+                    viewHolder.callActionButtonView?.also {
+                        it.isVisible = true
+                        it.text = context.getString(R.string.check_the_details)
+                    }
+                }
+                else -> {
+                    viewHolder.callActionButtonView?.isVisible = false
+                }
+            }
+
+            (viewHolder.advertiserLogoImageView as? AppCompatImageView)?.setImageBitmap(ttNativeAd.adLogo)
+
+            val clickViewList = mutableListOf<View>()
+            viewHolder.customLayoutRootView?.let { clickViewList.add(it) }
+
+
+            val creativeViewList = mutableListOf<View>()
+            viewHolder.callActionButtonView?.let {
+                creativeViewList.add(it)
+            }
+
+            ttNativeAd.registerViewForInteraction(
+                viewHolder.customLayoutRootView as ViewGroup,
+                imageViewList,
+                clickViewList,
+                creativeViewList,
+                viewHolder.dislikeView,
+                object : TTNativeAd.AdInteractionListener {
+                    override fun onAdClicked(p0: View?, p1: TTNativeAd?) {
+                        adLog(msg = "穿山甲广告-原生-点击广告")
+                        viewHolder.adListener?.onAdClick()
+                    }
+
+                    override fun onAdCreativeClick(p0: View?, p1: TTNativeAd?) {
+                    }
+
+                    override fun onAdShow(p0: TTNativeAd?) {
+                        adLog(msg = "穿山甲广告-原生-广告展示")
+                    }
+
+                })
+
+            ttNativeAd.setExpressRenderListener { view, width, height, isExpress ->
+                adLog(AdManager.TAG, "穿山甲广告-原生-渲染成功 width:${width},height:${height}")
+                viewHolder.adListener?.onAdShow(adResult.adBean)
+//                (viewHolder.customLayoutRootView as? ViewGroup)?.let {
+//                    val sWidth = context.getScreenWidth()
+//                    val sHeight = (sWidth * height / width).toInt()
+//                    it.removeAllViews()
+//                    it.addView(ttNativeAd., sWidth, sHeight)
+//                }
+            }
+            ttNativeAd.render()
+        }
+    }
+
+    override fun onDetachedFromWindow() {
+        super.onDetachedFromWindow()
+        adResult?.adObject = null
+    }
+}

+ 6 - 13
lib_ad_csj/src/main/java/com/composition/android/ad/csj/load/view/CSJNativeTemplateAdView.kt

@@ -2,15 +2,13 @@ package com.composition.android.ad.csj.load.view
 
 import android.content.Context
 import android.view.View
-import android.view.ViewGroup
-import com.bytedance.sdk.openadsdk.*
+import com.bytedance.sdk.openadsdk.TTAdDislike
+import com.bytedance.sdk.openadsdk.TTNativeExpressAd
 import com.composition.android.lib.ad.AdManager
 import com.composition.android.lib.ad.basic.AdResult
 import com.composition.android.lib.ad.basic.BasicAdView
 import com.composition.android.lib.ad.basic.NativeAdViewHolder
 import com.composition.android.lib.ad.util.adLog
-import com.composition.android.lib.ad.util.adLogE
-import com.convenient.android.common.extension.getScreenWidth
 
 /**
  * @classname:
@@ -30,7 +28,7 @@ class CSJNativeTemplateAdView(context: Context?) : BasicAdView<View>(context) {
         if (adResult == null || viewHolder == null) {
             return null
         }
-        populateNativeAdView(context, adResult = adResult!!, viewHolder = viewHolder!!, null)
+        populateNativeTemplateAdView(context, adResult = adResult!!, viewHolder = viewHolder!!)
         val ttNativeExpressAd = adResult?.adObject as TTNativeExpressAd
         return ttNativeExpressAd.expressAdView
     }
@@ -49,8 +47,10 @@ class CSJNativeTemplateAdView(context: Context?) : BasicAdView<View>(context) {
 
     companion object {
 
-        fun populateNativeAdView(context: Context, adResult: AdResult.Success, viewHolder: NativeAdViewHolder, adRootView : View?) {
+        fun populateNativeTemplateAdView(context: Context, adResult: AdResult.Success, viewHolder: NativeAdViewHolder) {
+
             val ttNativeExpressAd = adResult.adObject as TTNativeExpressAd
+            ttNativeExpressAd.render()
             viewHolder.activity?.let {
                 ttNativeExpressAd.setDislikeCallback(it, object : TTAdDislike.DislikeInteractionCallback {
                     override fun onShow() {
@@ -87,12 +87,6 @@ class CSJNativeTemplateAdView(context: Context?) : BasicAdView<View>(context) {
 
                 override fun onRenderSuccess(p0: View?, width: Float, height: Float) {
                     adLog(AdManager.TAG, "穿山甲广告-原生模板-渲染成功 width:${width},height:${height}")
-                    (adRootView as? ViewGroup)?.let {
-                        val sWidth = context.getScreenWidth()
-                        val sHeight = (sWidth * height / width).toInt()
-                        it.removeAllViews()
-                        it.addView(ttNativeExpressAd.expressAdView, sWidth,sHeight)
-                    }
                 }
 
                 override fun onAdDismiss() {
@@ -100,7 +94,6 @@ class CSJNativeTemplateAdView(context: Context?) : BasicAdView<View>(context) {
                     viewHolder.adListener?.onAdClose()
                 }
             })
-            ttNativeExpressAd.render()
 
         }
     }

+ 10 - 39
lib_ad_csj/src/main/java/com/composition/android/ad/csj/util/CSJAdExpan.kt

@@ -1,48 +1,19 @@
 package com.composition.android.ad.csj.util
 
-import com.composition.android.lib.ad.AdManager
-import com.composition.android.lib.ad.bean.AdUnitBean
-import com.composition.android.lib.ad.util.adLog
+import com.composition.android.ad.csj.impl.CSJBannerAdSize
+import com.composition.android.ad.csj.impl.CSJNativeAdImageSize
+import com.composition.android.lib.ad.interfaces.AdSize
+
 
 /**
- * @classname:
- * @author: LiuXiaoLong
- * @date: 2022/9/13
- * description:
+ * 构建穿山甲横幅广告 尺寸
  */
-
-internal fun printAdFailInfo(code : Int, msg : String?){
-    adLog(
-        AdManager.TAG,
-        "-------------------------------------------------------------------"
-    )
-
-    adLog(AdManager.TAG, "广告加载失败")
-    adLog(AdManager.TAG, "Code:${code}")
-    adLog(AdManager.TAG, "错误信息:${msg}")
-
-    adLog(
-        AdManager.TAG,
-        "-------------------------------------------------------------------"
-    )
+fun AdSize.Builder.buildCSJBannerAdSize(widthDp : Float, heightDp : Float) :CSJBannerAdSize{
+    return CSJBannerAdSize(widthDp, heightDp)
 }
 
+fun AdSize.Builder.buildCSJNativeImageAdSize(width : Int, height : Int) : CSJNativeAdImageSize{
+    return CSJNativeAdImageSize(width, height)
+}
 
-internal fun printAdInfo(adBean: AdUnitBean){
-
-    adLog(
-        AdManager.TAG,
-        "-------------------------------------------------------------------"
-    )
-
-    adLog(AdManager.TAG, "广告加载成功")
-    adLog(AdManager.TAG, "广告商:${adBean.advertisersName}")
-    adLog(AdManager.TAG, "广告位:${adBean.adSlotName}")
-    adLog(AdManager.TAG, "广告ID:${adBean.adUnitId}")
-    adLog(AdManager.TAG, "广告类型:${adBean.adType}")
 
-    adLog(
-        AdManager.TAG,
-        "-------------------------------------------------------------------"
-    )
-}

+ 38 - 0
lib_ad_csj/src/main/res/layout/csj_native_ad_media_image_views.xml

@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal">
+
+
+    <androidx.appcompat.widget.AppCompatImageView
+        android:id="@+id/id_ad_media_iv_1"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        app:layout_goneMarginEnd="0dp"
+        tools:layout_height="100dp"
+        tools:src="@tools:sample/backgrounds/scenic" />
+
+    <androidx.appcompat.widget.AppCompatImageView
+        android:id="@+id/id_ad_media_iv_2"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginHorizontal="4dp"
+        android:layout_weight="1"
+        android:visibility="gone"
+        tools:layout_height="100dp" />
+
+    <androidx.appcompat.widget.AppCompatImageView
+        android:id="@+id/id_ad_media_iv_3"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="4dp"
+        android:visibility="gone"
+        android:layout_weight="1"
+        tools:layout_height="100dp" />
+
+
+</LinearLayout>

+ 1 - 1
lib_ad_csj/src/main/res/xml/ttad_file_paths.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<paths xmlns:android="http://schemas.android.com/apk/res/android">
+<paths>
     <!--为了适配所有路径可以设置 path = "." -->
     <external-path name="tt_external_root" path="." />
     <external-path name="tt_external_download" path="Download" />

+ 1 - 2
lib_ad_csj/src/test/java/com/composition/android/ad/csj/ExampleUnitTest.kt

@@ -1,9 +1,8 @@
 package com.composition.android.ad.csj
 
+import org.junit.Assert.assertEquals
 import org.junit.Test
 
-import org.junit.Assert.*
-
 /**
  * Example local unit test, which will execute on the development machine (host).
  *

+ 49 - 18
lib_ad_gromore/build.gradle

@@ -2,6 +2,10 @@ plugins {
     id 'com.android.library'
     id 'org.jetbrains.kotlin.android'
 }
+def version = '1.0.0'
+def releaseTime() {
+    return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
+}
 
 android {
     compileSdk = rootProject.ext.compileSdkVersion
@@ -15,12 +19,33 @@ android {
         consumerProguardFiles "consumer-rules.pro"
     }
 
+
     buildTypes {
+
+        debug {
+            libraryVariants.all { variant->
+                variant.outputs.all { output->
+                    if (outputFile != null && outputFileName.endsWith('.aar')) {
+                        outputFileName = "ad_gromore_${version}_${releaseTime()}_debug.aar"
+                    }
+                }
+            }
+        }
         release {
             minifyEnabled false
             proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+            libraryVariants.all { variant->
+                variant.outputs.all { output->
+                    if (outputFile != null && outputFileName.endsWith('.aar')) {
+                        outputFileName = "ad_gromore_${version}_${releaseTime()}_release.aar"
+                    }
+                }
+            }
         }
     }
+
+
+
     compileOptions {
         sourceCompatibility JavaVersion.VERSION_1_8
         targetCompatibility JavaVersion.VERSION_1_8
@@ -28,15 +53,21 @@ android {
     kotlinOptions {
         jvmTarget = '1.8'
     }
+
 }
 
+
+
+
 dependencies {
 
     implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs')
 
-    implementation project(':lib_ad_core')
-    api "com.gromore.cn:gromore-sdk:3.7.0.1"
-    implementation "com.gromore.cn:pangle-adapter:4.8.0.8.0"
+    compileOnly project(':lib_ad_core')
+
+    api "com.gromore.cn:gromore-sdk:3.7.0.3"
+    api "com.gromore.cn:pangle-adapter:4.8.0.8.2"
+    api 'com.pangle.cn:ads-sdk-pro:4.8.0.8'
 
     implementation 'androidx.core:core-ktx:1.8.0'
     implementation 'androidx.appcompat:appcompat:1.5.1'
@@ -47,19 +78,19 @@ dependencies {
 
 
     //mintegral
-    implementation("com.mbridge.msdk.support:videojs:16.2.27")
-    implementation("com.mbridge.msdk.support:mbjscommon:16.2.27")
-    implementation("com.mbridge.msdk.support:playercommon:16.2.27")
-    implementation("com.mbridge.msdk.support:reward:16.2.27")
-    implementation("com.mbridge.msdk.support:videocommon:16.2.27")
-    implementation("com.mbridge.msdk.support:chinasame:16.2.27")
-    implementation("com.mbridge.msdk.support:interstitialvideo:16.2.27")
-    implementation("com.mbridge.msdk.support:mbnative:16.2.27")
-    implementation("com.mbridge.msdk.support:nativeex:16.2.27")
-    implementation("com.mbridge.msdk.support:mbnativeadvanced:16.2.27")
-    implementation("com.mbridge.msdk.support:interstitial:16.2.27")
-    implementation("com.mbridge.msdk.support:mbbanner:16.2.27")
-    implementation("com.mbridge.msdk.support:mbsplash:16.2.27")
-    implementation("com.mbridge.msdk.support:mbbid:16.2.27")
-    implementation("com.mbridge.msdk.support:newinterstitial:16.2.27")
+    api("com.mbridge.msdk.support:videojs:16.2.27")
+    api("com.mbridge.msdk.support:mbjscommon:16.2.27")
+    api("com.mbridge.msdk.support:playercommon:16.2.27")
+    api("com.mbridge.msdk.support:reward:16.2.27")
+    api("com.mbridge.msdk.support:videocommon:16.2.27")
+    api("com.mbridge.msdk.support:chinasame:16.2.27")
+    api("com.mbridge.msdk.support:interstitialvideo:16.2.27")
+    api("com.mbridge.msdk.support:mbnative:16.2.27")
+    api("com.mbridge.msdk.support:nativeex:16.2.27")
+    api("com.mbridge.msdk.support:mbnativeadvanced:16.2.27")
+    api("com.mbridge.msdk.support:interstitial:16.2.27")
+    api("com.mbridge.msdk.support:mbbanner:16.2.27")
+    api("com.mbridge.msdk.support:mbsplash:16.2.27")
+    api("com.mbridge.msdk.support:mbbid:16.2.27")
+    api("com.mbridge.msdk.support:newinterstitial:16.2.27")
 }

BIN
lib_ad_gromore/libs/open_ad_sdk.aar


+ 2 - 4
lib_ad_gromore/src/androidTest/java/com/composition/android/ad/gromore/ExampleInstrumentedTest.kt

@@ -1,13 +1,11 @@
 package com.composition.android.ad.gromore
 
-import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.ext.junit.runners.AndroidJUnit4
-
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.Assert.*
 import org.junit.Test
 import org.junit.runner.RunWith
 
-import org.junit.Assert.*
-
 /**
  * Instrumented test, which will execute on an Android device.
  *

+ 0 - 0
lib_ad_gromore/src/main/java/com/composition/android/ad/gromore/load/impl/GMBannerAdSize.kt


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