Skip to content

Commit

Permalink
Native ad options (#311)
Browse files Browse the repository at this point in the history
* Support the native ad options API.
  • Loading branch information
jjliu15 authored Jul 30, 2021
1 parent fcbbd31 commit 9093c78
Show file tree
Hide file tree
Showing 27 changed files with 1,041 additions and 27 deletions.
4 changes: 4 additions & 0 deletions packages/google_mobile_ads/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.13.3

* Adds support for NativeAdOptions. More documentation also available for [Android](https://developers.google.com/admob/android/native/options) and [iOS](https://developers.google.com/admob/ios/native/options)

## 0.13.2+1

* Fixes [Issue #130](https://github.com/googleads/googleads-mobile-flutter/issues/130)
Expand Down
92 changes: 92 additions & 0 deletions packages/google_mobile_ads/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,52 @@ final NativeAdListener listener = NativeAdListener(
);
```

#### NativeAdOptions

`NativeAds` have an optional argument, `nativeAdOptions`, which can be used to set specific options on the native ad.

`shouldRequestMultipleImages`
<p>If set to `true`, the SDK will not load image asset content and native ad
image URLs can be used to fetch content. Defaults to false.</p>

`shouldRequestMultipleImages`
<p>
Some image assets will contain a series of images rather than just one. By setting this value to true,
your app indicates that it's prepared to display all the images for any assets that have more than one.
By setting it to false (the default) your app instructs the SDK to provide just the first image for any assets that contain a series.

If no `NativeadOptions` are passed in when initializing a `NativeAd`, the default value for each property will be used.
</p>

`adChoicesPlacement`
<p>
The [AdChoices overlay](https://developers.google.com/admob/android/native/advanced#adchoices_overlay) is set to the top right corner by default.
Apps can change which corner this overlay is rendered in by setting this property to one of the following:

* AdChoicesPlacement.topRightCorner
* AdChoicesPlacement.topLeftCorner
* AdChoicesPlacement.bottomRightCorner
* AdChoicesPlacement.bottomLeftCorner
</p>

`videoOptions`
<p>
Can be used to set video options for video assets returned as part of a native ad.
</p>

`mediaAspectRatio`
<p>
This sets the aspect ratio for image or video to be returned for the native ad.
Setting NativeMediaAspectRatio to one of the following constants will cause only ads with media of the specified aspect ratio to be returned:

* MediaAspectRatio.landscape
* MediaAspectRatio.portrait
* MediaAspectRatio.square
* MediaAspectRatio.any

If not set, ads with any aspect ratio will be returned.
</p>

### Load Native Ad

After a `NativeAd` is instantiated, `load()` must be called before it can be shown on the screen.
Expand Down Expand Up @@ -1339,6 +1385,52 @@ final NativeAdListener listener = NativeAdListener(
);
```

#### NativeAdOptions

`NativeAds` have an optional argument, `nativeAdOptions`, which can be used to set specific options on the native ad.

`shouldRequestMultipleImages`
<p>If set to `true`, the SDK will not load image asset content and native ad
image URLs can be used to fetch content. Defaults to false.</p>

`shouldRequestMultipleImages`
<p>
Some image assets will contain a series of images rather than just one. By setting this value to true,
your app indicates that it's prepared to display all the images for any assets that have more than one.
By setting it to false (the default) your app instructs the SDK to provide just the first image for any assets that contain a series.

If no `NativeadOptions` are passed in when initializing a `NativeAd`, the default value for each property will be used.
</p>

`adChoicesPlacement`
<p>
The [AdChoices overlay](https://developers.google.com/admob/android/native/advanced#adchoices_overlay) is set to the top right corner by default.
Apps can change which corner this overlay is rendered in by setting this property to one of the following:

* AdChoicesPlacement.topRightCorner
* AdChoicesPlacement.topLeftCorner
* AdChoicesPlacement.bottomRightCorner
* AdChoicesPlacement.bottomLeftCorner
</p>

`videoOptions`
<p>
Can be used to set video options for video assets returned as part of a native ad.
</p>

`mediaAspectRatio`
<p>
This sets the aspect ratio for image or video to be returned for the native ad.
Setting NativeMediaAspectRatio to one of the following constants will cause only ads with media of the specified aspect ratio to be returned:

* MediaAspectRatio.landscape
* MediaAspectRatio.portrait
* MediaAspectRatio.square
* MediaAspectRatio.any

If not set, ads with any aspect ratio will be returned.
</p>

### Load Native Ad

After a `NativeAd` is instantiated, `load()` must be called before it can be shown on the screen.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ class AdMessageCodec extends StandardMessageCodec {
private static final byte VALUE_ADAPTER_RESPONSE_INFO = (byte) 141;
static final byte VALUE_ANCHORED_ADAPTIVE_BANNER_AD_SIZE = (byte) 142;
static final byte VALUE_SMART_BANNER_AD_SIZE = (byte) 143;
static final byte VALUE_NATIVE_AD_OPTIONS = (byte) 144;
static final byte VALUE_VIDEO_OPTIONS = (byte) 145;

@NonNull final Context context;
@NonNull final FlutterAdSize.AdSizeFactory adSizeFactory;
Expand Down Expand Up @@ -141,6 +143,21 @@ protected void writeValue(ByteArrayOutputStream stream, Object value) {
FlutterServerSideVerificationOptions options = (FlutterServerSideVerificationOptions) value;
writeValue(stream, options.getUserId());
writeValue(stream, options.getCustomData());
} else if (value instanceof FlutterNativeAdOptions) {
stream.write(VALUE_NATIVE_AD_OPTIONS);
FlutterNativeAdOptions options = (FlutterNativeAdOptions) value;
writeValue(stream, options.adChoicesPlacement);
writeValue(stream, options.mediaAspectRatio);
writeValue(stream, options.videoOptions);
writeValue(stream, options.requestCustomMuteThisAd);
writeValue(stream, options.shouldRequestMultipleImages);
writeValue(stream, options.shouldReturnUrlsForImageAssets);
} else if (value instanceof FlutterVideoOptions) {
stream.write(VALUE_VIDEO_OPTIONS);
FlutterVideoOptions options = (FlutterVideoOptions) value;
writeValue(stream, options.clickToExpandRequested);
writeValue(stream, options.customControlsRequested);
writeValue(stream, options.startMuted);
} else {
super.writeValue(stream, value);
}
Expand Down Expand Up @@ -226,6 +243,19 @@ protected Object readValueOfType(byte type, ByteBuffer buffer) {
return new FlutterServerSideVerificationOptions(
(String) readValueOfType(buffer.get(), buffer),
(String) readValueOfType(buffer.get(), buffer));
case VALUE_NATIVE_AD_OPTIONS:
return new FlutterNativeAdOptions(
(Integer) readValueOfType(buffer.get(), buffer),
(Integer) readValueOfType(buffer.get(), buffer),
(FlutterVideoOptions) readValueOfType(buffer.get(), buffer),
(Boolean) readValueOfType(buffer.get(), buffer),
(Boolean) readValueOfType(buffer.get(), buffer),
(Boolean) readValueOfType(buffer.get(), buffer));
case VALUE_VIDEO_OPTIONS:
return new FlutterVideoOptions(
(Boolean) readValueOfType(buffer.get(), buffer),
(Boolean) readValueOfType(buffer.get(), buffer),
(Boolean) readValueOfType(buffer.get(), buffer));
default:
return super.readValueOfType(type, buffer);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@
/** Constants used in the plugin. */
public class Constants {
/** Version request agent. Should be bumped alongside plugin versions. */
public static final String REQUEST_AGENT_PREFIX_VERSIONED = "Flutter-GMA-0.13.2+1";
public static final String REQUEST_AGENT_PREFIX_VERSIONED = "Flutter-GMA-0.13.3";
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class FlutterNativeAd extends FlutterAd {
@Nullable private FlutterAdManagerAdRequest adManagerRequest;
@Nullable private Map<String, Object> customOptions;
@Nullable private NativeAdView nativeAdView;
@Nullable private FlutterNativeAdOptions nativeAdOptions;

static class Builder {
@Nullable private AdInstanceManager manager;
Expand All @@ -47,6 +48,7 @@ static class Builder {
@Nullable private FlutterAdManagerAdRequest adManagerRequest;
@Nullable private Map<String, Object> customOptions;
@Nullable private Integer id;
@Nullable private FlutterNativeAdOptions nativeAdOptions;

public Builder setAdFactory(@NonNull NativeAdFactory adFactory) {
this.adFactory = adFactory;
Expand Down Expand Up @@ -83,6 +85,11 @@ public Builder setAdManagerRequest(@NonNull FlutterAdManagerAdRequest request) {
return this;
}

public Builder setNativeAdOptions(@Nullable FlutterNativeAdOptions nativeAdOptions) {
this.nativeAdOptions = nativeAdOptions;
return this;
}

FlutterNativeAd build() {
if (manager == null) {
throw new IllegalStateException("AdInstanceManager cannot not be null.");
Expand All @@ -104,11 +111,19 @@ FlutterNativeAd build() {
adFactory,
adManagerRequest,
new FlutterAdLoader(),
customOptions);
customOptions,
nativeAdOptions);
} else {
nativeAd =
new FlutterNativeAd(
id, manager, adUnitId, adFactory, request, new FlutterAdLoader(), customOptions);
id,
manager,
adUnitId,
adFactory,
request,
new FlutterAdLoader(),
customOptions,
nativeAdOptions);
}
return nativeAd;
}
Expand All @@ -121,14 +136,16 @@ protected FlutterNativeAd(
@NonNull NativeAdFactory adFactory,
@NonNull FlutterAdRequest request,
@NonNull FlutterAdLoader flutterAdLoader,
@Nullable Map<String, Object> customOptions) {
@Nullable Map<String, Object> customOptions,
@Nullable FlutterNativeAdOptions nativeAdOptions) {
super(adId);
this.manager = manager;
this.adUnitId = adUnitId;
this.adFactory = adFactory;
this.request = request;
this.flutterAdLoader = flutterAdLoader;
this.customOptions = customOptions;
this.nativeAdOptions = nativeAdOptions;
}

protected FlutterNativeAd(
Expand All @@ -138,24 +155,28 @@ protected FlutterNativeAd(
@NonNull NativeAdFactory adFactory,
@NonNull FlutterAdManagerAdRequest adManagerRequest,
@NonNull FlutterAdLoader flutterAdLoader,
@Nullable Map<String, Object> customOptions) {
@Nullable Map<String, Object> customOptions,
@Nullable FlutterNativeAdOptions nativeAdOptions) {
super(adId);
this.manager = manager;
this.adUnitId = adUnitId;
this.adFactory = adFactory;
this.adManagerRequest = adManagerRequest;
this.flutterAdLoader = flutterAdLoader;
this.customOptions = customOptions;
this.nativeAdOptions = nativeAdOptions;
}

@Override
void load() {
final OnNativeAdLoadedListener loadedListener = new FlutterNativeAdLoadedListener(this);
final AdListener adListener = new FlutterNativeAdListener(adId, manager);
final NativeAdOptions options = new NativeAdOptions.Builder().build();

// Note we delegate loading the ad to FlutterAdLoader mainly for testing purposes.
// As of 20.0.0 of GMA, mockito is unable to mock AdLoader.
final NativeAdOptions options =
this.nativeAdOptions == null
? new NativeAdOptions.Builder().build()
: nativeAdOptions.asNativeAdOptions();
if (request != null) {
flutterAdLoader.loadNativeAd(
manager.activity, adUnitId, loadedListener, options, adListener, request.asAdRequest());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright 2021 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.c language governing permissions and
// limitations under the License.

package io.flutter.plugins.googlemobileads;

import androidx.annotation.Nullable;
import com.google.android.gms.ads.nativead.NativeAdOptions;

/** A wrapper for {@link com.google.android.gms.ads.nativead.NativeAdOptions}. */
class FlutterNativeAdOptions {

@Nullable final Integer adChoicesPlacement;
@Nullable final Integer mediaAspectRatio;
@Nullable final FlutterVideoOptions videoOptions;
@Nullable final Boolean requestCustomMuteThisAd;
@Nullable final Boolean shouldRequestMultipleImages;
@Nullable final Boolean shouldReturnUrlsForImageAssets;

FlutterNativeAdOptions(
@Nullable Integer adChoicesPlacement,
@Nullable Integer mediaAspectRatio,
@Nullable FlutterVideoOptions videoOptions,
@Nullable Boolean requestCustomMuteThisAd,
@Nullable Boolean shouldRequestMultipleImages,
@Nullable Boolean shouldReturnUrlsForImageAssets) {
this.adChoicesPlacement = adChoicesPlacement;
this.mediaAspectRatio = mediaAspectRatio;
this.videoOptions = videoOptions;
this.requestCustomMuteThisAd = requestCustomMuteThisAd;
this.shouldRequestMultipleImages = shouldRequestMultipleImages;
this.shouldReturnUrlsForImageAssets = shouldReturnUrlsForImageAssets;
}

NativeAdOptions asNativeAdOptions() {
NativeAdOptions.Builder builder = new NativeAdOptions.Builder();
if (adChoicesPlacement != null) {
builder.setAdChoicesPlacement(adChoicesPlacement);
}
if (mediaAspectRatio != null) {
builder.setMediaAspectRatio(mediaAspectRatio);
}
if (videoOptions != null) {
builder.setVideoOptions(videoOptions.asVideoOptions());
}
if (requestCustomMuteThisAd != null) {
builder.setRequestCustomMuteThisAd(requestCustomMuteThisAd);
}
if (shouldRequestMultipleImages != null) {
builder.setRequestMultipleImages(shouldRequestMultipleImages);
}
if (shouldReturnUrlsForImageAssets != null) {
builder.setReturnUrlsForImageAssets(shouldReturnUrlsForImageAssets);
}
return builder.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright 2021 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.c language governing permissions and
// limitations under the License.

package io.flutter.plugins.googlemobileads;

import androidx.annotation.Nullable;
import com.google.android.gms.ads.VideoOptions;

/** A wrapper for {@link com.google.android.gms.ads.VideoOptions}. */
class FlutterVideoOptions {
@Nullable final Boolean clickToExpandRequested;
@Nullable final Boolean customControlsRequested;
@Nullable final Boolean startMuted;

FlutterVideoOptions(
@Nullable Boolean clickToExpandRequested,
@Nullable Boolean customControlsRequested,
@Nullable Boolean startMuted) {
this.clickToExpandRequested = clickToExpandRequested;
this.customControlsRequested = customControlsRequested;
this.startMuted = startMuted;
}

VideoOptions asVideoOptions() {
VideoOptions.Builder builder = new VideoOptions.Builder();
if (clickToExpandRequested != null) {
builder.setClickToExpandRequested(clickToExpandRequested);
}
if (customControlsRequested != null) {
builder.setCustomControlsRequested(customControlsRequested);
}
if (startMuted != null) {
builder.setStartMuted(startMuted);
}
return builder.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull final Result result)
.setAdManagerRequest(call.<FlutterAdManagerAdRequest>argument("adManagerRequest"))
.setCustomOptions(call.<Map<String, Object>>argument("customOptions"))
.setId(call.<Integer>argument("adId"))
.setNativeAdOptions(call.<FlutterNativeAdOptions>argument("nativeAdOptions"))
.build();
instanceManager.trackAd(nativeAd, call.<Integer>argument("adId"));
nativeAd.load();
Expand Down
Loading

0 comments on commit 9093c78

Please sign in to comment.