Skip to content

Commit 135f0fa

Browse files
Refactor NativeAdLoader and update dependencies
This commit refactors the `NativeAdLoader` to simplify the native ad view population logic. Key changes include: - Removing reflection-based view binding in `NativeAdLoader` in favor of `findViewById`. This also removes the need for specific ProGuard rules, which have been deleted. - Removing several overloaded `load` methods in `NativeAdLoader` to simplify the API. - Introducing a new string resource `ad_attribution_with_advertiser` for better ad attribution text formatting. - Incrementing the `versionCode` to 54.
1 parent 6626184 commit 135f0fa

File tree

4 files changed

+15
-109
lines changed

4 files changed

+15
-109
lines changed

app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ android {
1313
applicationId 'com.d4rk.androidtutorials.java'
1414
minSdk 23
1515
targetSdk 36
16-
versionCode 53
16+
versionCode 54
1717
versionName '5.0.3'
1818
vectorDrawables.useSupportLibrary = true
1919
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'

app/proguard-rules.pro

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,4 @@
1818

1919
# If you keep the line number information, uncomment this to
2020
# hide the original source file name.
21-
#-renamesourcefileattribute SourceFile
22-
-keep public class com.google.android.gms.ads.** {
23-
public *;
24-
}
25-
-keep public class com.google.ads.** {
26-
public *;
27-
}
28-
-keep class com.google.firebase.** {
29-
public *;
30-
}
21+
#-renamesourcefileattribute SourceFile
Lines changed: 12 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.d4rk.androidtutorials.java.ads.managers;
22

33
import android.content.Context;
4-
import android.content.res.Resources;
54
import android.text.TextUtils;
65
import android.util.Log;
76
import android.view.LayoutInflater;
@@ -13,7 +12,6 @@
1312

1413
import androidx.annotation.LayoutRes;
1514
import androidx.annotation.NonNull;
16-
import androidx.viewbinding.ViewBinding;
1715

1816
import com.d4rk.androidtutorials.java.R;
1917
import com.google.android.gms.ads.AdListener;
@@ -25,40 +23,13 @@
2523
import com.google.android.gms.ads.nativead.NativeAd;
2624
import com.google.android.gms.ads.nativead.NativeAdView;
2725

28-
import java.lang.reflect.Field;
29-
import java.lang.reflect.InvocationTargetException;
30-
import java.lang.reflect.Method;
31-
3226
/**
3327
* Helper class to load AdMob native ads into a container.
3428
*/
3529
public class NativeAdLoader {
3630

3731
private static final String TAG = "NativeAdLoader";
3832

39-
public static void load(@NonNull Context context, @NonNull ViewGroup container) {
40-
load(context, container, R.layout.ad_home_banner_large, null, new AdRequest.Builder().build(), null);
41-
}
42-
43-
public static void load(@NonNull Context context, @NonNull ViewGroup container, @LayoutRes int layoutRes) {
44-
load(context, container, layoutRes, null, new AdRequest.Builder().build(), null);
45-
}
46-
47-
public static void load(@NonNull Context context,
48-
@NonNull ViewGroup container,
49-
@LayoutRes int layoutRes,
50-
@androidx.annotation.Nullable AdListener listener) {
51-
load(context, container, layoutRes, null, new AdRequest.Builder().build(), listener);
52-
}
53-
54-
public static void load(@NonNull Context context,
55-
@NonNull ViewGroup container,
56-
@LayoutRes int layoutRes,
57-
@NonNull AdRequest adRequest,
58-
@androidx.annotation.Nullable AdListener listener) {
59-
load(context, container, layoutRes, null, adRequest, listener);
60-
}
61-
6233
public static void load(@NonNull Context context,
6334
@NonNull ViewGroup container,
6435
@LayoutRes int layoutRes,
@@ -69,6 +40,7 @@ public static void load(@NonNull Context context,
6940
? context.getString(R.string.native_ad_fallback_unit_id)
7041
: adUnitId;
7142

43+
assert resolvedAdUnitId != null;
7244
AdLoader.Builder builder = new AdLoader.Builder(context, resolvedAdUnitId)
7345
.forNativeAd(nativeAd -> {
7446
LayoutInflater inflater = LayoutInflater.from(context);
@@ -79,7 +51,7 @@ public static void load(@NonNull Context context,
7951
adView.setPadding(container.getPaddingLeft(), container.getPaddingTop(),
8052
container.getPaddingRight(), container.getPaddingBottom());
8153
container.setPadding(0, 0, 0, 0);
82-
populateNativeAdView(nativeAd, adView, layoutRes);
54+
populateNativeAdView(nativeAd, adView);
8355
container.removeAllViews();
8456
container.addView(adView);
8557
container.requestLayout();
@@ -98,22 +70,14 @@ public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) {
9870
adLoader.loadAd(adRequest);
9971
}
10072

101-
private static void populateNativeAdView(@NonNull NativeAd nativeAd,
102-
@NonNull NativeAdView adView,
103-
@LayoutRes int layoutRes) {
104-
ViewBinding binding = tryBind(adView, layoutRes);
105-
if (binding == null) {
106-
Log.w(TAG, "Could not bind native ad view for layout " + layoutRes);
107-
return;
108-
}
109-
110-
MediaView mediaView = getBindingField(binding, "adMedia", MediaView.class);
111-
TextView headlineView = getBindingField(binding, "adHeadline", TextView.class);
112-
TextView bodyView = getBindingField(binding, "adBody", TextView.class);
113-
Button callToActionView = getBindingField(binding, "adCallToAction", Button.class);
114-
ImageView iconView = getBindingField(binding, "adAppIcon", ImageView.class);
115-
TextView attributionView = getBindingField(binding, "adAttribution", TextView.class);
116-
AdChoicesView adChoicesView = getBindingField(binding, "adChoices", AdChoicesView.class);
73+
private static void populateNativeAdView(@NonNull NativeAd nativeAd, @NonNull NativeAdView adView) {
74+
MediaView mediaView = adView.findViewById(R.id.ad_media);
75+
TextView headlineView = adView.findViewById(R.id.ad_headline);
76+
TextView bodyView = adView.findViewById(R.id.ad_body);
77+
Button callToActionView = adView.findViewById(R.id.ad_call_to_action);
78+
ImageView iconView = adView.findViewById(R.id.ad_app_icon);
79+
TextView attributionView = adView.findViewById(R.id.ad_attribution);
80+
AdChoicesView adChoicesView = adView.findViewById(R.id.ad_choices);
11781

11882
if (mediaView != null) {
11983
adView.setMediaView(mediaView);
@@ -152,11 +116,10 @@ private static void populateNativeAdView(@NonNull NativeAd nativeAd,
152116
}
153117

154118
if (attributionView != null) {
155-
String adLabel = adView.getContext().getString(R.string.ad);
156119
if (nativeAd.getAdvertiser() == null) {
157-
attributionView.setText(adLabel);
120+
attributionView.setText(R.string.ad);
158121
} else {
159-
attributionView.setText(adLabel + " " + nativeAd.getAdvertiser());
122+
attributionView.setText(adView.getContext().getString(R.string.ad_attribution_with_advertiser, nativeAd.getAdvertiser()));
160123
}
161124
}
162125

@@ -180,53 +143,4 @@ private static void populateNativeAdView(@NonNull NativeAd nativeAd,
180143

181144
adView.setNativeAd(nativeAd);
182145
}
183-
184-
@androidx.annotation.Nullable
185-
private static ViewBinding tryBind(@NonNull NativeAdView adView, @LayoutRes int layoutRes) {
186-
try {
187-
String resourceName = adView.getResources().getResourceEntryName(layoutRes);
188-
String bindingName = toBindingClassName(resourceName);
189-
String fullClassName = adView.getContext().getPackageName() + ".databinding." + bindingName;
190-
Class<?> bindingClass = Class.forName(fullClassName);
191-
Method bindMethod = bindingClass.getMethod("bind", View.class);
192-
return (ViewBinding) bindMethod.invoke(null, adView);
193-
} catch (Resources.NotFoundException | ClassNotFoundException | NoSuchMethodException |
194-
IllegalAccessException | InvocationTargetException e) {
195-
Log.w(TAG, "Failed to create view binding for native ad layout", e);
196-
return null;
197-
}
198-
}
199-
200-
@androidx.annotation.Nullable
201-
private static <T> T getBindingField(@NonNull ViewBinding binding,
202-
@NonNull String fieldName,
203-
@NonNull Class<T> type) {
204-
try {
205-
Field field = binding.getClass().getField(fieldName);
206-
Object value = field.get(binding);
207-
if (type.isInstance(value)) {
208-
return type.cast(value);
209-
}
210-
} catch (NoSuchFieldException | IllegalAccessException e) {
211-
Log.w(TAG, "Unable to access binding field " + fieldName, e);
212-
}
213-
return null;
214-
}
215-
216-
@NonNull
217-
private static String toBindingClassName(@NonNull String resourceName) {
218-
StringBuilder builder = new StringBuilder(resourceName.length());
219-
boolean capitalize = true;
220-
for (int i = 0; i < resourceName.length(); i++) {
221-
char c = resourceName.charAt(i);
222-
if (c == '_') {
223-
capitalize = true;
224-
} else {
225-
builder.append(capitalize ? Character.toUpperCase(c) : c);
226-
capitalize = false;
227-
}
228-
}
229-
builder.append("Binding");
230-
return builder.toString();
231-
}
232146
}

app/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
<string name="learn_more">Learn more</string>
2828
<string name="play_store">Play Store</string>
2929
<string name="ad">Ad</string>
30+
<string name="ad_attribution_with_advertiser">Ad from %1$s</string>
3031
<string name="search_lessons_hint">Search lessons</string>
3132
<string name="search_lessons_content_description">Search lessons</string>
3233

0 commit comments

Comments
 (0)