Skip to content

Commit 02c4bd3

Browse files
committed
Allow serving Native ads in AdLoaderAd
Allow the `AdLoaderAd` instance to serve `Native` ads, which are instances of: * `NativeAd` under Android, and * `GADNativeAd` under iOS
1 parent b9b6596 commit 02c4bd3

18 files changed

+643
-23
lines changed

packages/google_mobile_ads/android/src/main/java/io/flutter/plugins/googlemobileads/AdMessageCodec.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ class AdMessageCodec extends StandardMessageCodec {
6868
private static final byte VALUE_AD_MANAGER_AD_VIEW_OPTIONS = (byte) 154;
6969
private static final byte VALUE_BANNER_PARAMETERS = (byte) 155;
7070
private static final byte VALUE_CUSTOM_PARAMETERS = (byte) 156;
71+
private static final byte VALUE_NATIVE_PARAMETERS = (byte) 157;
7172

7273
@NonNull Context context;
7374
@NonNull final FlutterAdSize.AdSizeFactory adSizeFactory;
@@ -259,6 +260,12 @@ protected void writeValue(ByteArrayOutputStream stream, Object value) {
259260
FlutterCustomParameters customParameters = (FlutterCustomParameters) value;
260261
writeValue(stream, customParameters.formatIds);
261262
writeValue(stream, customParameters.viewOptions);
263+
} else if (value instanceof FlutterNativeParameters) {
264+
stream.write(VALUE_NATIVE_PARAMETERS);
265+
FlutterNativeParameters nativeParameters = (FlutterNativeParameters) value;
266+
writeValue(stream, nativeParameters.factoryId);
267+
writeValue(stream, nativeParameters.nativeAdOptions);
268+
writeValue(stream, nativeParameters.viewOptions);
262269
} else {
263270
super.writeValue(stream, value);
264271
}
@@ -429,6 +436,11 @@ protected Object readValueOfType(byte type, ByteBuffer buffer) {
429436
return new FlutterCustomParameters(
430437
(List<String>) readValueOfType(buffer.get(), buffer),
431438
(Map<String, Object>) readValueOfType(buffer.get(), buffer));
439+
case VALUE_NATIVE_PARAMETERS:
440+
return new FlutterNativeParameters(
441+
(String) readValueOfType(buffer.get(), buffer),
442+
(FlutterNativeAdOptions) readValueOfType(buffer.get(), buffer),
443+
(Map<String, Object>) readValueOfType(buffer.get(), buffer));
432444
default:
433445
return super.readValueOfType(type, buffer);
434446
}

packages/google_mobile_ads/android/src/main/java/io/flutter/plugins/googlemobileads/FlutterAdLoader.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,8 @@ public void loadAdLoaderAd(
147147
@NonNull AdListener adListener,
148148
@NonNull AdRequest request,
149149
@Nullable FlutterAdLoaderAd.BannerParameters bannerParameters,
150-
@Nullable FlutterAdLoaderAd.CustomParameters customParameters) {
150+
@Nullable FlutterAdLoaderAd.CustomParameters customParameters,
151+
@Nullable FlutterAdLoaderAd.NativeParameters nativeParameters) {
151152
AdLoader.Builder builder = new AdLoader.Builder(context, adUnitId);
152153
if (bannerParameters != null) {
153154
builder = builder.forAdManagerAdView(bannerParameters.listener, bannerParameters.adSizes);
@@ -160,6 +161,12 @@ public void loadAdLoaderAd(
160161
builder = builder.forCustomFormatAd(formatId, customParameters.listener, null);
161162
}
162163
}
164+
if (nativeParameters != null) {
165+
builder = builder.forNativeAd(nativeParameters.listener);
166+
if (nativeParameters.nativeAdOptions != null) {
167+
builder = builder.withNativeAdOptions(nativeParameters.nativeAdOptions);
168+
}
169+
}
163170
builder.withAdListener(adListener).build().loadAd(request);
164171
}
165172

@@ -169,7 +176,8 @@ public void loadAdManagerAdLoaderAd(
169176
@NonNull AdListener adListener,
170177
@NonNull AdManagerAdRequest adManagerAdRequest,
171178
@Nullable FlutterAdLoaderAd.BannerParameters bannerParameters,
172-
@Nullable FlutterAdLoaderAd.CustomParameters customParameters) {
179+
@Nullable FlutterAdLoaderAd.CustomParameters customParameters,
180+
@Nullable FlutterAdLoaderAd.NativeParameters nativeParameters) {
173181
AdLoader.Builder builder = new AdLoader.Builder(context, adUnitId);
174182
if (bannerParameters != null) {
175183
builder = builder.forAdManagerAdView(bannerParameters.listener, bannerParameters.adSizes);
@@ -182,6 +190,12 @@ public void loadAdManagerAdLoaderAd(
182190
builder = builder.forCustomFormatAd(formatId, customParameters.listener, null);
183191
}
184192
}
193+
if (nativeParameters != null) {
194+
builder = builder.forNativeAd(nativeParameters.listener);
195+
if (nativeParameters.nativeAdOptions != null) {
196+
builder = builder.withNativeAdOptions(nativeParameters.nativeAdOptions);
197+
}
198+
}
185199
builder.withAdListener(adListener).build().loadAd(adManagerAdRequest);
186200
}
187201
}

packages/google_mobile_ads/android/src/main/java/io/flutter/plugins/googlemobileads/FlutterAdLoaderAd.java

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,24 @@
2424
import com.google.android.gms.ads.admanager.AdManagerAdView;
2525
import com.google.android.gms.ads.formats.AdManagerAdViewOptions;
2626
import com.google.android.gms.ads.formats.OnAdManagerAdViewLoadedListener;
27+
import com.google.android.gms.ads.nativead.NativeAd;
28+
import com.google.android.gms.ads.nativead.NativeAd.OnNativeAdLoadedListener;
29+
import com.google.android.gms.ads.nativead.NativeAdOptions;
2730
import com.google.android.gms.ads.nativead.NativeCustomFormatAd;
2831
import com.google.android.gms.ads.nativead.NativeCustomFormatAd.OnCustomFormatAdLoadedListener;
2932
import io.flutter.plugin.platform.PlatformView;
3033
import io.flutter.plugins.googlemobileads.GoogleMobileAdsPlugin.CustomAdFactory;
34+
import io.flutter.plugins.googlemobileads.GoogleMobileAdsPlugin.NativeAdFactory;
3135
import java.util.Map;
3236

3337
/**
3438
* A central wrapper for {@link AdManagerAdView}, {@link NativeCustomFormatAd} and {@link NativeAd}
3539
* instances served for a single {@link AdRequest} or {@link AdManagerAdRequest}
3640
*/
3741
class FlutterAdLoaderAd extends FlutterAd
38-
implements OnAdManagerAdViewLoadedListener, OnCustomFormatAdLoadedListener {
42+
implements OnAdManagerAdViewLoadedListener,
43+
OnCustomFormatAdLoadedListener,
44+
OnNativeAdLoadedListener {
3945
private static final String TAG = "FlutterAdLoaderAd";
4046

4147
@NonNull private final AdInstanceManager manager;
@@ -48,6 +54,7 @@ class FlutterAdLoaderAd extends FlutterAd
4854
@Nullable private View view;
4955
@Nullable protected BannerParameters bannerParameters;
5056
@Nullable protected CustomParameters customParameters;
57+
@Nullable protected NativeParameters nativeParameters;
5158

5259
static class Builder {
5360
@Nullable private AdInstanceManager manager;
@@ -59,6 +66,8 @@ static class Builder {
5966
@Nullable private FlutterBannerParameters bannerParameters;
6067
@Nullable private FlutterCustomParameters customParameters;
6168
@Nullable private Map<String, CustomAdFactory> customFactories;
69+
@Nullable private FlutterNativeParameters nativeParameters;
70+
@Nullable private Map<String, NativeAdFactory> nativeFactories;
6271

6372
public Builder setId(int id) {
6473
this.id = id;
@@ -106,6 +115,17 @@ public Builder withAvailableCustomFactories(
106115
return this;
107116
}
108117

118+
public Builder setNative(@Nullable FlutterNativeParameters nativeParameters) {
119+
this.nativeParameters = nativeParameters;
120+
return this;
121+
}
122+
123+
public Builder withAvailableNativeFactories(
124+
@NonNull Map<String, NativeAdFactory> nativeFactories) {
125+
this.nativeFactories = nativeFactories;
126+
return this;
127+
}
128+
109129
FlutterAdLoaderAd build() {
110130
if (manager == null) {
111131
throw new IllegalStateException("manager must be provided");
@@ -139,6 +159,12 @@ FlutterAdLoaderAd build() {
139159
new FlutterCustomFormatAdLoadedListener(adLoaderAd), customFactories);
140160
}
141161

162+
if (nativeParameters != null) {
163+
adLoaderAd.nativeParameters =
164+
nativeParameters.asNativeParameters(
165+
new FlutterNativeAdLoadedListener(adLoaderAd), nativeFactories);
166+
}
167+
142168
return adLoaderAd;
143169
}
144170
}
@@ -147,6 +173,7 @@ enum AdLoaderAdType {
147173
UNKNOWN,
148174
BANNER,
149175
CUSTOM,
176+
NATIVE,
150177
}
151178

152179
static class BannerParameters {
@@ -179,6 +206,24 @@ static class CustomParameters {
179206
}
180207
}
181208

209+
static class NativeParameters {
210+
@NonNull final OnNativeAdLoadedListener listener;
211+
@NonNull final NativeAdFactory factory;
212+
@Nullable final NativeAdOptions nativeAdOptions;
213+
@Nullable final Map<String, Object> viewOptions;
214+
215+
NativeParameters(
216+
@NonNull OnNativeAdLoadedListener listener,
217+
@NonNull NativeAdFactory factory,
218+
@Nullable NativeAdOptions nativeAdOptions,
219+
@Nullable Map<String, Object> viewOptions) {
220+
this.listener = listener;
221+
this.factory = factory;
222+
this.nativeAdOptions = nativeAdOptions;
223+
this.viewOptions = viewOptions;
224+
}
225+
}
226+
182227
protected FlutterAdLoaderAd(
183228
int adId,
184229
@NonNull AdInstanceManager manager,
@@ -220,7 +265,12 @@ void load() {
220265
// As of 20.0.0 of GMA, mockito is unable to mock AdLoader.
221266
if (request != null) {
222267
adLoader.loadAdLoaderAd(
223-
adUnitId, adListener, request.asAdRequest(adUnitId), bannerParameters, customParameters);
268+
adUnitId,
269+
adListener,
270+
request.asAdRequest(adUnitId),
271+
bannerParameters,
272+
customParameters,
273+
nativeParameters);
224274
return;
225275
}
226276

@@ -230,7 +280,8 @@ void load() {
230280
adListener,
231281
adManagerRequest.asAdManagerAdRequest(adUnitId),
232282
bannerParameters,
233-
customParameters);
283+
customParameters,
284+
nativeParameters);
234285
return;
235286
}
236287

@@ -281,6 +332,14 @@ public void onCustomFormatAdLoaded(@NonNull NativeCustomFormatAd ad) {
281332
manager.onAdLoaded(adId, null);
282333
}
283334

335+
@Override
336+
public void onNativeAdLoaded(@NonNull NativeAd ad) {
337+
view = nativeParameters.factory.createNativeAd(ad, nativeParameters.viewOptions);
338+
type = AdLoaderAdType.NATIVE;
339+
ad.setOnPaidEventListener(new FlutterPaidEventListener(manager, this));
340+
manager.onAdLoaded(adId, ad.getResponseInfo());
341+
}
342+
284343
@Override
285344
void dispose() {
286345
if (view == null) {

packages/google_mobile_ads/android/src/main/java/io/flutter/plugins/googlemobileads/FlutterCustomParameters.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ class FlutterCustomParameters {
2020

2121
FlutterAdLoaderAd.CustomParameters asCustomParameters(
2222
@NonNull OnCustomFormatAdLoadedListener listener,
23-
@NonNull Map<String, CustomAdFactory> availableFactories) {
23+
@NonNull Map<String, CustomAdFactory> registeredFactories) {
2424
Map<String, CustomAdFactory> factories = new HashMap<>();
2525
for (String formatId : formatIds) {
26-
factories.put(formatId, availableFactories.get(formatId));
26+
factories.put(formatId, registeredFactories.get(formatId));
2727
}
2828
return new FlutterAdLoaderAd.CustomParameters(listener, factories, viewOptions);
2929
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package io.flutter.plugins.googlemobileads;
16+
17+
import androidx.annotation.NonNull;
18+
import androidx.annotation.Nullable;
19+
import com.google.android.gms.ads.nativead.NativeAd.OnNativeAdLoadedListener;
20+
import com.google.android.gms.ads.nativead.NativeAdOptions;
21+
import io.flutter.plugins.googlemobileads.GoogleMobileAdsPlugin.NativeAdFactory;
22+
import java.util.Map;
23+
24+
class FlutterNativeParameters {
25+
@NonNull final String factoryId;
26+
@Nullable final FlutterNativeAdOptions nativeAdOptions;
27+
@Nullable final Map<String, Object> viewOptions;
28+
29+
FlutterNativeParameters(
30+
@NonNull String factoryId,
31+
@Nullable FlutterNativeAdOptions nativeAdOptions,
32+
@Nullable Map<String, Object> viewOptions) {
33+
this.factoryId = factoryId;
34+
this.nativeAdOptions = nativeAdOptions;
35+
this.viewOptions = viewOptions;
36+
}
37+
38+
FlutterAdLoaderAd.NativeParameters asNativeParameters(
39+
@NonNull OnNativeAdLoadedListener listener,
40+
@NonNull Map<String, NativeAdFactory> registeredFactories) {
41+
NativeAdOptions nativeAdOptions = null;
42+
if (this.nativeAdOptions != null) {
43+
nativeAdOptions = this.nativeAdOptions.asNativeAdOptions();
44+
}
45+
return new FlutterAdLoaderAd.NativeParameters(
46+
listener, registeredFactories.get(factoryId), nativeAdOptions, viewOptions);
47+
}
48+
}

packages/google_mobile_ads/android/src/main/java/io/flutter/plugins/googlemobileads/GoogleMobileAdsPlugin.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,17 @@ public void onAdInspectorClosed(@Nullable AdInspectorError adInspectorError) {
502502
}
503503
}
504504

505+
final FlutterNativeParameters nativeParameters =
506+
call.<FlutterNativeParameters>argument("native");
507+
if (nativeParameters != null) {
508+
if (nativeAdFactories.get(nativeParameters.factoryId) == null) {
509+
final String message =
510+
String.format("Can't find NativeAdFactory with id: %s", nativeParameters.factoryId);
511+
result.error("AdLoaderAdError", message, null);
512+
return;
513+
}
514+
}
515+
505516
final FlutterAdLoaderAd adLoaderAd =
506517
new FlutterAdLoaderAd.Builder()
507518
.setManager(instanceManager)
@@ -516,6 +527,8 @@ public void onAdInspectorClosed(@Nullable AdInspectorError adInspectorError) {
516527
.setBanner(call.<FlutterBannerParameters>argument("banner"))
517528
.setCustom(customParameters)
518529
.withAvailableCustomFactories(customAdFactories)
530+
.setNative(nativeParameters)
531+
.withAvailableNativeFactories(nativeAdFactories)
519532
.build();
520533
instanceManager.trackAd(adLoaderAd, call.<Integer>argument("adId"));
521534
adLoaderAd.load();

packages/google_mobile_ads/android/src/test/java/io/flutter/plugins/googlemobileads/AdMessageCodecTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,4 +576,27 @@ public void encodeCustomParameters() {
576576
assertEquals(result.viewOptions.size(), 1);
577577
assertEquals(result.viewOptions.get("key"), "value");
578578
}
579+
580+
@Test
581+
public void encodeNativeParameters() {
582+
final ByteBuffer data =
583+
codec.encodeMessage(
584+
new FlutterNativeParameters(
585+
"factory-id",
586+
new FlutterNativeAdOptions(1, 1, null, true, true, true),
587+
Collections.singletonMap("key", "value")));
588+
589+
final FlutterNativeParameters result =
590+
(FlutterNativeParameters) codec.decodeMessage((ByteBuffer) data.position(0));
591+
592+
assertEquals(result.factoryId, "factory-id");
593+
assertEquals(result.nativeAdOptions.adChoicesPlacement, Integer.valueOf(1));
594+
assertEquals(result.nativeAdOptions.mediaAspectRatio, Integer.valueOf(1));
595+
assertNull(result.nativeAdOptions.videoOptions);
596+
assertTrue(result.nativeAdOptions.requestCustomMuteThisAd);
597+
assertTrue(result.nativeAdOptions.shouldRequestMultipleImages);
598+
assertTrue(result.nativeAdOptions.shouldReturnUrlsForImageAssets);
599+
assertEquals(result.viewOptions.size(), 1);
600+
assertEquals(result.viewOptions.get("key"), "value");
601+
}
579602
}

0 commit comments

Comments
 (0)