Skip to content

Commit b512a6d

Browse files
authored
Feature / NAD (#23)
* Implemented native-ad * Fix prod ad unit ID * Implemented gallery ad banner * FOSS impl
1 parent 7597748 commit b512a6d

File tree

12 files changed

+261
-79
lines changed

12 files changed

+261
-79
lines changed

domain/src/main/java/com/shifthackz/aisdv1/domain/feature/AdFeature.kt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@ import android.view.View
66

77
interface AdFeature {
88
fun initialize(activity: Activity)
9-
fun getBannerAdView(context: Context): View?
10-
fun loadAd(view: View)
9+
fun getHomeScreenBannerAd(context: Context): Ad
10+
fun getGalleryDetailBannerAd(context: Context): Ad
11+
fun loadAd(ad: Ad)
12+
13+
data class Ad(
14+
val id: String = "",
15+
val view: View? = null,
16+
) {
17+
val isEmpty: Boolean
18+
get() = id.isEmpty() || view == null
19+
}
1120
}

feature/ads/build.gradle

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ android {
99
namespace 'com.shifthackz.aisdv1.feature.ads'
1010
buildTypes {
1111
release {
12-
buildConfigField "String", "BANNER_AD_UNIT_ID", "\"ca-app-pub-5994265132349884/9250961993\""
12+
buildConfigField "String", "BANNER_HOMESCREEN_AD_UNIT_ID", "\"ca-app-pub-5994265132349884/9338284416\""
13+
buildConfigField "String", "BANNER_GALLERY_AD_UNIT_ID", "\"ca-app-pub-5994265132349884/8450925763\""
1314
}
1415
debug {
15-
buildConfigField "String", "BANNER_AD_UNIT_ID", "\"ca-app-pub-3940256099942544/6300978111\""
16+
buildConfigField "String", "BANNER_HOMESCREEN_AD_UNIT_ID", "\"ca-app-pub-3940256099942544/2247696110\""
17+
buildConfigField "String", "BANNER_GALLERY_AD_UNIT_ID", "\"ca-app-pub-3940256099942544/2247696110\""
1618
}
1719
}
1820
flavorDimensions "type"
@@ -29,5 +31,6 @@ android {
2931
dependencies {
3032
implementation project(":domain")
3133
implementation di.koinCore
34+
playstoreImplementation androidx.constraintLayout
3235
playstoreImplementation proprietary.playServicesAds
3336
}

feature/ads/src/foss/java/com/shifthackz/aisdv1/feature/ads/AdFeatureImpl.kt

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,8 @@ import android.view.View
66
import com.shifthackz.aisdv1.domain.feature.AdFeature
77

88
class AdFeatureImpl : AdFeature {
9-
109
override fun initialize(activity: Activity) = Unit
11-
12-
override fun getBannerAdView(context: Context): View? = null
13-
14-
override fun loadAd(view: View) = Unit
10+
override fun getHomeScreenBannerAd(context: Context) = AdFeature.Ad()
11+
override fun getGalleryDetailBannerAd(context: Context) = AdFeature.Ad()
12+
override fun loadAd(ad: AdFeature.Ad) = Unit
1513
}

feature/ads/src/playstore/java/com/shifthackz/aisdv1/feature/ads/AdFeatureImpl.kt

Lines changed: 25 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,53 +2,39 @@ package com.shifthackz.aisdv1.feature.ads
22

33
import android.app.Activity
44
import android.content.Context
5-
import android.util.Log
6-
import android.view.View
5+
import android.view.LayoutInflater
6+
import androidx.annotation.LayoutRes
77
import com.google.android.gms.ads.*
8+
import com.google.android.gms.ads.nativead.NativeAdView
89
import com.shifthackz.aisdv1.domain.feature.AdFeature
910

1011
class AdFeatureImpl : AdFeature {
1112

12-
override fun initialize(activity: Activity) {
13-
MobileAds.initialize(activity)
13+
override fun initialize(activity: Activity) = MobileAds.initialize(activity)
14+
15+
override fun getHomeScreenBannerAd(context: Context) = AdFeature.Ad(
16+
view = inflateNativeAdView(context, R.layout.native_small_ad_view),
17+
id = BuildConfig.BANNER_HOMESCREEN_AD_UNIT_ID,
18+
)
19+
20+
override fun getGalleryDetailBannerAd(context: Context) = AdFeature.Ad(
21+
view = inflateNativeAdView(context, R.layout.native_small_ad_view),
22+
id = BuildConfig.BANNER_GALLERY_AD_UNIT_ID,
23+
)
24+
25+
override fun loadAd(ad: AdFeature.Ad) {
26+
inflateAdLoader(ad)?.loadAd(AdRequest.Builder().build())
1427
}
1528

16-
override fun getBannerAdView(context: Context): View {
17-
val adId = BuildConfig.BANNER_AD_UNIT_ID
18-
val adView = AdView(context)
19-
adView.setAdSize(AdSize.BANNER)
20-
adView.adUnitId = adId
21-
if (BuildConfig.DEBUG) adView.adListener = object : AdListener() {
22-
override fun onAdClicked() {
23-
Log.d(this::class.simpleName, "[$adId] onAdClicked")
24-
}
25-
26-
override fun onAdClosed() {
27-
Log.d(this::class.simpleName, "[$adId] onAdClosed")
28-
}
29-
30-
override fun onAdFailedToLoad(adError: LoadAdError) {
31-
Log.d(this::class.simpleName, "[$adId] onAdFailedToLoad : $adError")
32-
}
33-
34-
override fun onAdImpression() {
35-
Log.d(this::class.simpleName, "[$adId] onAdImpression")
36-
}
37-
38-
override fun onAdLoaded() {
39-
Log.d(this::class.simpleName, "[$adId] onAdLoaded")
40-
}
41-
42-
override fun onAdOpened() {
43-
Log.d(this::class.simpleName, "[$adId] onAdOpened")
44-
}
45-
}
46-
return adView
29+
private fun inflateNativeAdView(context: Context, @LayoutRes layoutId: Int): NativeAdView {
30+
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
31+
return inflater.inflate(layoutId, null) as NativeAdView
4732
}
4833

49-
override fun loadAd(view: View) {
50-
if (view !is AdView) return
51-
val adRequest = AdRequest.Builder().build()
52-
view.loadAd(adRequest)
34+
private fun inflateAdLoader(ad: AdFeature.Ad) = ad.view?.context?.let { ctx ->
35+
AdLoader.Builder(ctx, ad.id)
36+
.forNativeAd { nativeAd -> AdMobXmlRenderer().invoke(ad, nativeAd) }
37+
.applyLoggableAdListener(ad.id)
38+
.build()
5339
}
5440
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.shifthackz.aisdv1.feature.ads
2+
3+
import android.util.Log
4+
import com.google.android.gms.ads.AdListener
5+
import com.google.android.gms.ads.AdLoader
6+
import com.google.android.gms.ads.LoadAdError
7+
8+
fun AdLoader.Builder.applyLoggableAdListener(adId: String = "Unknown") = apply {
9+
if (BuildConfig.DEBUG) withAdListener(
10+
object : AdListener() {
11+
override fun onAdClicked() {
12+
Log.d(this::class.simpleName, "[$adId] onAdClicked")
13+
}
14+
15+
override fun onAdClosed() {
16+
Log.d(this::class.simpleName, "[$adId] onAdClosed")
17+
}
18+
19+
override fun onAdFailedToLoad(adError: LoadAdError) {
20+
Log.d(this::class.simpleName, "[$adId] onAdFailedToLoad : $adError")
21+
}
22+
23+
override fun onAdImpression() {
24+
Log.d(this::class.simpleName, "[$adId] onAdImpression")
25+
}
26+
27+
override fun onAdLoaded() {
28+
Log.d(this::class.simpleName, "[$adId] onAdLoaded")
29+
}
30+
31+
override fun onAdOpened() {
32+
Log.d(this::class.simpleName, "[$adId] onAdOpened")
33+
}
34+
}
35+
)
36+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
@file:Suppress("DUPLICATE_LABEL_IN_WHEN")
2+
3+
package com.shifthackz.aisdv1.feature.ads
4+
5+
import android.view.View
6+
import android.widget.ImageView
7+
import android.widget.TextView
8+
import com.google.android.gms.ads.nativead.NativeAd
9+
import com.google.android.gms.ads.nativead.NativeAdView
10+
import com.shifthackz.aisdv1.domain.feature.AdFeature
11+
12+
class AdMobXmlRenderer {
13+
14+
fun invoke(ad: AdFeature.Ad, nativeAd: NativeAd) {
15+
if (ad.view == null) return
16+
if (ad.view?.context == null) return
17+
when (ad.id) {
18+
BuildConfig.BANNER_HOMESCREEN_AD_UNIT_ID,
19+
BuildConfig.BANNER_GALLERY_AD_UNIT_ID -> renderHomeScreenBannedAd(ad.view, nativeAd)
20+
}
21+
}
22+
23+
private fun renderHomeScreenBannedAd(adView: View?, nativeAd: NativeAd) {
24+
if (adView == null) return
25+
if (adView !is NativeAdView) return
26+
// Initialize AD title
27+
val headline = adView.findViewById<TextView>(R.id.nativeAdSmallTitle)
28+
headline.text = nativeAd.headline
29+
adView.headlineView = headline
30+
// Initialize AD image
31+
val icon = adView.findViewById<ImageView>(R.id.nativeAdSmallImage)
32+
icon.setImageDrawable(nativeAd.icon?.drawable)
33+
adView.iconView = icon
34+
// Initialize AD body
35+
val body = adView.findViewById<TextView>(R.id.nativeAdSmallDesc)
36+
body.text = nativeAd.body
37+
adView.bodyView = body
38+
// Post initialization stuff
39+
adView.setNativeAd(nativeAd)
40+
adView.findViewById<View>(R.id.container).visibility = View.VISIBLE
41+
}
42+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<com.google.android.gms.ads.nativead.NativeAdView xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:app="http://schemas.android.com/apk/res-auto"
4+
xmlns:tools="http://schemas.android.com/tools"
5+
android:layout_width="match_parent"
6+
android:layout_height="wrap_content">
7+
8+
<androidx.constraintlayout.widget.ConstraintLayout
9+
android:id="@+id/container"
10+
android:layout_width="match_parent"
11+
android:layout_height="wrap_content"
12+
android:orientation="horizontal"
13+
android:visibility="gone"
14+
tools:visibility="visible">
15+
16+
<!-- <TextView-->
17+
<!-- android:layout_width="wrap_content"-->
18+
<!-- android:layout_height="wrap_content"-->
19+
<!-- android:text="Ad"-->
20+
<!-- android:textSize="15sp" />-->
21+
22+
23+
<ImageView
24+
android:id="@+id/nativeAdSmallImage"
25+
android:layout_width="0dp"
26+
android:layout_height="0dp"
27+
android:contentDescription="@null"
28+
app:layout_constraintDimensionRatio="1:1"
29+
app:layout_constraintStart_toStartOf="parent"
30+
app:layout_constraintTop_toTopOf="parent"
31+
tools:background="#0000ff" />
32+
33+
<TextView
34+
android:id="@+id/nativeAdSmallTitle"
35+
android:layout_width="0dp"
36+
android:layout_height="wrap_content"
37+
android:layout_marginHorizontal="@dimen/ad_banner_horizontal_margin"
38+
android:textSize="15sp"
39+
android:textStyle="bold"
40+
app:layout_constraintEnd_toEndOf="parent"
41+
app:layout_constraintStart_toEndOf="@+id/nativeAdSmallImage"
42+
app:layout_constraintTop_toTopOf="parent"
43+
tools:text="Ad Title" />
44+
45+
<TextView
46+
android:id="@+id/nativeAdSmallDesc"
47+
android:layout_width="0dp"
48+
android:layout_height="wrap_content"
49+
android:layout_marginVertical="@dimen/ad_banner_vertical_margin"
50+
android:layout_marginHorizontal="@dimen/ad_banner_horizontal_margin"
51+
android:textSize="13sp"
52+
app:layout_constraintEnd_toEndOf="parent"
53+
app:layout_constraintBottom_toBottomOf="parent"
54+
app:layout_constraintStart_toEndOf="@+id/nativeAdSmallImage"
55+
app:layout_constraintTop_toBottomOf="@+id/nativeAdSmallTitle"
56+
tools:text="Ad description" />
57+
58+
</androidx.constraintlayout.widget.ConstraintLayout>
59+
</com.google.android.gms.ads.nativead.NativeAdView>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<resources>
3+
<dimen name="ad_banner_horizontal_margin">8dp</dimen>
4+
<dimen name="ad_banner_vertical_margin">4dp</dimen>
5+
</resources>

gradle/dependencies.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ ext {
1313
pagingVersion = '3.1.1'
1414
pagingComposeVersion = '1.0.0-alpha18'
1515
imagePickerVersion = 'v2.0.2'
16+
constraintVersion = '2.1.4'
1617

1718
firebaseBomVersion = '31.2.3'
1819
playServicesAdsVersion = '21.5.0'
@@ -23,6 +24,7 @@ ext {
2324
core : "androidx.core:core-ktx:1.9.0",
2425
appcompat : "androidx.appcompat:appcompat:1.6.0",
2526
activity : "androidx.activity:activity-ktx:1.6.1",
27+
constraintLayout : "androidx.constraintlayout:constraintlayout:$constraintVersion",
2628
composeBom : "androidx.compose:compose-bom:$composeBomVersion",
2729
composeRuntime : "androidx.compose.runtime:runtime",
2830
composeMaterial3 : "androidx.compose.material3:material3:$composeMaterialVersion",

0 commit comments

Comments
 (0)