Skip to content

Commit

Permalink
Allow serving Native ads in AdLoaderAd
Browse files Browse the repository at this point in the history
Allow the `AdLoaderAd` instance to serve `Native` ads, which are
instances of:

  * `NativeAd` under Android, and
  * `GADNativeAd` under iOS
  • Loading branch information
msbit committed Mar 23, 2023
1 parent 7a58770 commit d7d6aca
Show file tree
Hide file tree
Showing 18 changed files with 640 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ class AdMessageCodec extends StandardMessageCodec {
private static final byte VALUE_AD_MANAGER_AD_VIEW_OPTIONS = (byte) 154;
private static final byte VALUE_BANNER_PARAMETERS = (byte) 155;
private static final byte VALUE_CUSTOM_PARAMETERS = (byte) 156;
private static final byte VALUE_NATIVE_PARAMETERS = (byte) 157;

@NonNull Context context;
@NonNull final FlutterAdSize.AdSizeFactory adSizeFactory;
Expand Down Expand Up @@ -259,6 +260,12 @@ protected void writeValue(ByteArrayOutputStream stream, Object value) {
FlutterCustomParameters customParameters = (FlutterCustomParameters) value;
writeValue(stream, customParameters.formatIds);
writeValue(stream, customParameters.viewOptions);
} else if (value instanceof FlutterNativeParameters) {
stream.write(VALUE_NATIVE_PARAMETERS);
FlutterNativeParameters nativeParameters = (FlutterNativeParameters) value;
writeValue(stream, nativeParameters.factoryId);
writeValue(stream, nativeParameters.nativeAdOptions);
writeValue(stream, nativeParameters.viewOptions);
} else {
super.writeValue(stream, value);
}
Expand Down Expand Up @@ -429,6 +436,11 @@ protected Object readValueOfType(byte type, ByteBuffer buffer) {
return new FlutterCustomParameters(
(List<String>) readValueOfType(buffer.get(), buffer),
(Map<String, Object>) readValueOfType(buffer.get(), buffer));
case VALUE_NATIVE_PARAMETERS:
return new FlutterNativeParameters(
(String) readValueOfType(buffer.get(), buffer),
(FlutterNativeAdOptions) readValueOfType(buffer.get(), buffer),
(Map<String, Object>) readValueOfType(buffer.get(), buffer));
default:
return super.readValueOfType(type, buffer);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@ public void loadAdLoaderAd(
@NonNull AdListener adListener,
@NonNull AdRequest request,
@Nullable FlutterAdLoaderAd.BannerParameters bannerParameters,
@Nullable FlutterAdLoaderAd.CustomParameters customParameters) {
@Nullable FlutterAdLoaderAd.CustomParameters customParameters,
@Nullable FlutterAdLoaderAd.NativeParameters nativeParameters) {
AdLoader.Builder builder = new AdLoader.Builder(context, adUnitId);
if (bannerParameters != null) {
builder = builder.forAdManagerAdView(bannerParameters.listener, bannerParameters.adSizes);
Expand All @@ -160,6 +161,12 @@ public void loadAdLoaderAd(
builder = builder.forCustomFormatAd(formatId, customParameters.listener, null);
}
}
if (nativeParameters != null) {
builder = builder.forNativeAd(nativeParameters.listener);
if (nativeParameters.nativeAdOptions != null) {
builder = builder.withNativeAdOptions(nativeParameters.nativeAdOptions);
}
}
builder.withAdListener(adListener).build().loadAd(request);
}

Expand All @@ -169,7 +176,8 @@ public void loadAdManagerAdLoaderAd(
@NonNull AdListener adListener,
@NonNull AdManagerAdRequest adManagerAdRequest,
@Nullable FlutterAdLoaderAd.BannerParameters bannerParameters,
@Nullable FlutterAdLoaderAd.CustomParameters customParameters) {
@Nullable FlutterAdLoaderAd.CustomParameters customParameters,
@Nullable FlutterAdLoaderAd.NativeParameters nativeParameters) {
AdLoader.Builder builder = new AdLoader.Builder(context, adUnitId);
if (bannerParameters != null) {
builder = builder.forAdManagerAdView(bannerParameters.listener, bannerParameters.adSizes);
Expand All @@ -182,6 +190,12 @@ public void loadAdManagerAdLoaderAd(
builder = builder.forCustomFormatAd(formatId, customParameters.listener, null);
}
}
if (nativeParameters != null) {
builder = builder.forNativeAd(nativeParameters.listener);
if (nativeParameters.nativeAdOptions != null) {
builder = builder.withNativeAdOptions(nativeParameters.nativeAdOptions);
}
}
builder.withAdListener(adListener).build().loadAd(adManagerAdRequest);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,24 @@
import com.google.android.gms.ads.admanager.AdManagerAdView;
import com.google.android.gms.ads.formats.AdManagerAdViewOptions;
import com.google.android.gms.ads.formats.OnAdManagerAdViewLoadedListener;
import com.google.android.gms.ads.nativead.NativeAd;
import com.google.android.gms.ads.nativead.NativeAd.OnNativeAdLoadedListener;
import com.google.android.gms.ads.nativead.NativeAdOptions;
import com.google.android.gms.ads.nativead.NativeCustomFormatAd;
import com.google.android.gms.ads.nativead.NativeCustomFormatAd.OnCustomFormatAdLoadedListener;
import io.flutter.plugin.platform.PlatformView;
import io.flutter.plugins.googlemobileads.GoogleMobileAdsPlugin.CustomAdFactory;
import io.flutter.plugins.googlemobileads.GoogleMobileAdsPlugin.NativeAdFactory;
import java.util.Map;

/**
* A central wrapper for {@link AdManagerAdView}, {@link NativeCustomFormatAd} and {@link NativeAd}
* instances served for a single {@link AdRequest} or {@link AdManagerAdRequest}
*/
class FlutterAdLoaderAd extends FlutterAd
implements OnAdManagerAdViewLoadedListener, OnCustomFormatAdLoadedListener {
implements OnAdManagerAdViewLoadedListener,
OnCustomFormatAdLoadedListener,
OnNativeAdLoadedListener {
private static final String TAG = "FlutterAdLoaderAd";

@NonNull private final AdInstanceManager manager;
Expand All @@ -47,6 +53,7 @@ class FlutterAdLoaderAd extends FlutterAd
@Nullable private View view;
@Nullable protected BannerParameters bannerParameters;
@Nullable protected CustomParameters customParameters;
@Nullable protected NativeParameters nativeParameters;

static class Builder {
@Nullable private AdInstanceManager manager;
Expand All @@ -58,6 +65,8 @@ static class Builder {
@Nullable private FlutterBannerParameters bannerParameters;
@Nullable private FlutterCustomParameters customParameters;
@Nullable private Map<String, CustomAdFactory> customFactories;
@Nullable private FlutterNativeParameters nativeParameters;
@Nullable private Map<String, NativeAdFactory> nativeFactories;

public Builder setId(int id) {
this.id = id;
Expand Down Expand Up @@ -105,6 +114,17 @@ public Builder withAvailableCustomFactories(
return this;
}

public Builder setNative(@Nullable FlutterNativeParameters nativeParameters) {
this.nativeParameters = nativeParameters;
return this;
}

public Builder withAvailableNativeFactories(
@NonNull Map<String, NativeAdFactory> nativeFactories) {
this.nativeFactories = nativeFactories;
return this;
}

FlutterAdLoaderAd build() {
if (manager == null) {
throw new IllegalStateException("manager must be provided");
Expand Down Expand Up @@ -138,6 +158,12 @@ FlutterAdLoaderAd build() {
new FlutterCustomFormatAdLoadedListener(adLoaderAd), customFactories);
}

if (nativeParameters != null) {
adLoaderAd.nativeParameters =
nativeParameters.asNativeParameters(
new FlutterNativeAdLoadedListener(adLoaderAd), nativeFactories);
}

return adLoaderAd;
}
}
Expand All @@ -146,6 +172,7 @@ enum AdLoaderAdType {
UNKNOWN,
BANNER,
CUSTOM,
NATIVE,
}

static class BannerParameters {
Expand Down Expand Up @@ -178,6 +205,24 @@ static class CustomParameters {
}
}

static class NativeParameters {
@NonNull final OnNativeAdLoadedListener listener;
@NonNull final NativeAdFactory factory;
@Nullable final NativeAdOptions nativeAdOptions;
@Nullable final Map<String, Object> viewOptions;

NativeParameters(
@NonNull OnNativeAdLoadedListener listener,
@NonNull NativeAdFactory factory,
@Nullable NativeAdOptions nativeAdOptions,
@Nullable Map<String, Object> viewOptions) {
this.listener = listener;
this.factory = factory;
this.nativeAdOptions = nativeAdOptions;
this.viewOptions = viewOptions;
}
}

protected FlutterAdLoaderAd(
int adId,
@NonNull AdInstanceManager manager,
Expand Down Expand Up @@ -217,7 +262,12 @@ void load() {
// As of 20.0.0 of GMA, mockito is unable to mock AdLoader.
if (request != null) {
adLoader.loadAdLoaderAd(
adUnitId, adListener, request.asAdRequest(adUnitId), bannerParameters, customParameters);
adUnitId,
adListener,
request.asAdRequest(adUnitId),
bannerParameters,
customParameters,
nativeParameters);
return;
}

Expand All @@ -227,7 +277,8 @@ void load() {
adListener,
adManagerRequest.asAdManagerAdRequest(adUnitId),
bannerParameters,
customParameters);
customParameters,
nativeParameters);
return;
}

Expand Down Expand Up @@ -273,6 +324,14 @@ public void onCustomFormatAdLoaded(@NonNull NativeCustomFormatAd ad) {
manager.onAdLoaded(adId, null);
}

@Override
public void onNativeAdLoaded(@NonNull NativeAd ad) {
view = nativeParameters.factory.createNativeAd(ad, nativeParameters.viewOptions);
type = AdLoaderAdType.NATIVE;
ad.setOnPaidEventListener(new FlutterPaidEventListener(manager, this));
manager.onAdLoaded(adId, ad.getResponseInfo());
}

@Override
void dispose() {
if (view == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ class FlutterCustomParameters {

FlutterAdLoaderAd.CustomParameters asCustomParameters(
@NonNull OnCustomFormatAdLoadedListener listener,
@NonNull Map<String, CustomAdFactory> availableFactories) {
@NonNull Map<String, CustomAdFactory> registeredFactories) {
Map<String, CustomAdFactory> factories = new HashMap<>();
for (String formatId : formatIds) {
factories.put(formatId, availableFactories.get(formatId));
factories.put(formatId, registeredFactories.get(formatId));
}
return new FlutterAdLoaderAd.CustomParameters(listener, factories, viewOptions);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package io.flutter.plugins.googlemobileads;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.gms.ads.nativead.NativeAd.OnNativeAdLoadedListener;
import com.google.android.gms.ads.nativead.NativeAdOptions;
import io.flutter.plugins.googlemobileads.GoogleMobileAdsPlugin.NativeAdFactory;
import java.util.Map;

class FlutterNativeParameters {
@NonNull final String factoryId;
@Nullable final FlutterNativeAdOptions nativeAdOptions;
@Nullable final Map<String, Object> viewOptions;

FlutterNativeParameters(
@NonNull String factoryId,
@Nullable FlutterNativeAdOptions nativeAdOptions,
@Nullable Map<String, Object> viewOptions) {
this.factoryId = factoryId;
this.nativeAdOptions = nativeAdOptions;
this.viewOptions = viewOptions;
}

FlutterAdLoaderAd.NativeParameters asNativeParameters(
@NonNull OnNativeAdLoadedListener listener,
@NonNull Map<String, NativeAdFactory> registeredFactories) {
NativeAdOptions nativeAdOptions = null;
if (this.nativeAdOptions != null) {
nativeAdOptions = this.nativeAdOptions.asNativeAdOptions();
}
return new FlutterAdLoaderAd.NativeParameters(
listener, registeredFactories.get(factoryId), nativeAdOptions, viewOptions);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,17 @@ public void onAdInspectorClosed(@Nullable AdInspectorError adInspectorError) {
}
}

final FlutterNativeParameters nativeParameters =
call.<FlutterNativeParameters>argument("native");
if (nativeParameters != null) {
if (nativeAdFactories.get(nativeParameters.factoryId) == null) {
final String message =
String.format("Can't find NativeAdFactory with id: %s", nativeParameters.factoryId);
result.error("AdLoaderAdError", message, null);
return;
}
}

final FlutterAdLoaderAd adLoaderAd =
new FlutterAdLoaderAd.Builder()
.setManager(instanceManager)
Expand All @@ -500,6 +511,8 @@ public void onAdInspectorClosed(@Nullable AdInspectorError adInspectorError) {
.setBanner(call.<FlutterBannerParameters>argument("banner"))
.setCustom(customParameters)
.withAvailableCustomFactories(customAdFactories)
.setNative(nativeParameters)
.withAvailableNativeFactories(nativeAdFactories)
.build();
instanceManager.trackAd(adLoaderAd, call.<Integer>argument("adId"));
adLoaderAd.load();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -576,4 +576,27 @@ public void encodeCustomParameters() {
assertEquals(result.viewOptions.size(), 1);
assertEquals(result.viewOptions.get("key"), "value");
}

@Test
public void encodeNativeParameters() {
final ByteBuffer data =
codec.encodeMessage(
new FlutterNativeParameters(
"factory-id",
new FlutterNativeAdOptions(1, 1, null, true, true, true),
Collections.singletonMap("key", "value")));

final FlutterNativeParameters result =
(FlutterNativeParameters) codec.decodeMessage((ByteBuffer) data.position(0));

assertEquals(result.factoryId, "factory-id");
assertEquals(result.nativeAdOptions.adChoicesPlacement, Integer.valueOf(1));
assertEquals(result.nativeAdOptions.mediaAspectRatio, Integer.valueOf(1));
assertNull(result.nativeAdOptions.videoOptions);
assertTrue(result.nativeAdOptions.requestCustomMuteThisAd);
assertTrue(result.nativeAdOptions.shouldRequestMultipleImages);
assertTrue(result.nativeAdOptions.shouldReturnUrlsForImageAssets);
assertEquals(result.viewOptions.size(), 1);
assertEquals(result.viewOptions.get("key"), "value");
}
}
Loading

0 comments on commit d7d6aca

Please sign in to comment.