Skip to content

[interactive_media_ads] Adds support to define parameters that control the rendering of ads #8057

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 29 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
fc00df8
ad android method and add platform class
bparrishMines Nov 12, 2024
efc63c0
update platform class
bparrishMines Nov 12, 2024
ca5c47d
add app facing portion
bparrishMines Nov 12, 2024
70be676
android implementation
bparrishMines Nov 12, 2024
d0e0de9
ios implementation
bparrishMines Nov 12, 2024
c10c14f
fix android test
bparrishMines Nov 12, 2024
7f9a563
update app facing test
bparrishMines Nov 12, 2024
5b3e3ae
ios test
bparrishMines Nov 12, 2024
bae565f
version bump
bparrishMines Nov 12, 2024
e5abb39
Merge branch 'main' of github.com:flutter/packages into ima_adsrendering
bparrishMines Nov 12, 2024
3071bf5
fix up changelog
bparrishMines Nov 12, 2024
bf2a118
Merge branch 'main' of github.com:flutter/packages into ima_adsrendering
bparrishMines Nov 12, 2024
aa443bc
create new android and ios implementations
bparrishMines Nov 13, 2024
832d86e
Merge branch 'main' of github.com:flutter/packages into ima_adsrendering
bparrishMines Nov 13, 2024
efa6b2d
fix tests
bparrishMines Nov 13, 2024
63eabc3
fix lint and add comment
bparrishMines Nov 13, 2024
b69a9bd
add enable preloading to example
bparrishMines Nov 13, 2024
e7aaf61
update excerpts
bparrishMines Nov 13, 2024
4c8bc86
Merge branch 'main' of github.com:flutter/packages into ima_adsrendering
bparrishMines Nov 13, 2024
300a608
obey contract
bparrishMines Nov 14, 2024
b2abcd1
Merge branch 'main' of github.com:flutter/packages into ima_adsrendering
bparrishMines Nov 14, 2024
3b79dd0
use duration
bparrishMines Nov 14, 2024
e80a923
review comments
bparrishMines Nov 14, 2024
1e58518
Merge branch 'main' of github.com:flutter/packages into ima_adsrendering
bparrishMines Nov 14, 2024
9d85bf8
Merge branch 'main' of github.com:flutter/packages into ima_adsrendering
bparrishMines Nov 16, 2024
d9b81e6
fix lints
bparrishMines Nov 16, 2024
35e05c5
fix excerpts
bparrishMines Nov 16, 2024
aea49fe
add null clarification and doc fix
bparrishMines Nov 19, 2024
4b4bf9c
Merge branch 'main' of github.com:flutter/packages into ima_adsrendering
bparrishMines Nov 19, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/interactive_media_ads/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.2.3

* Adds parameters to control the rendering of ads. See `AdsManager.init`.

## 0.2.2+15

* Adds remaining methods for internal wrapper of the Android native `BaseManager`.
Expand Down
2 changes: 1 addition & 1 deletion packages/interactive_media_ads/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ late final AdDisplayContainer _adDisplayContainer = AdDisplayContainer(
},
));

manager.init();
manager.init(settings: AdsRenderingSettings(enablePreloading: true));
},
onAdsLoadError: (AdsLoadErrorData data) {
debugPrint('OnAdsLoadError: ${data.error.message}');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class AdsRequestProxyApi(override val pigeonRegistrar: ProxyApiRegistrar) :
*
* This must match the version in pubspec.yaml.
*/
const val pluginVersion = "0.2.2+15"
const val pluginVersion = "0.2.3"
}

override fun setAdTagUrl(pigeon_instance: AdsRequest, adTagUrl: String) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package dev.flutter.packages.interactive_media_ads
import android.view.ViewGroup
import com.google.ads.interactivemedia.v3.api.AdDisplayContainer
import com.google.ads.interactivemedia.v3.api.AdsLoader
import com.google.ads.interactivemedia.v3.api.AdsRenderingSettings
import com.google.ads.interactivemedia.v3.api.AdsRequest
import com.google.ads.interactivemedia.v3.api.ImaSdkFactory
import com.google.ads.interactivemedia.v3.api.ImaSdkSettings
Expand Down Expand Up @@ -46,4 +47,8 @@ class ImaSdkFactoryProxyApi(override val pigeonRegistrar: ProxyApiRegistrar) :
override fun createAdsRequest(pigeon_instance: ImaSdkFactory): AdsRequest {
return pigeon_instance.createAdsRequest()
}

override fun createAdsRenderingSettings(pigeon_instance: ImaSdkFactory): AdsRenderingSettings {
return pigeon_instance.createAdsRenderingSettings()
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Autogenerated from Pigeon (v22.5.0), do not edit directly.
// Autogenerated from Pigeon (v22.6.0), do not edit directly.
// See also: https://pub.dev/packages/pigeon
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass", "SyntheticAccessor")

Expand Down Expand Up @@ -2236,6 +2236,14 @@ abstract class PigeonApiImaSdkFactory(
pigeon_instance: com.google.ads.interactivemedia.v3.api.ImaSdkFactory
): com.google.ads.interactivemedia.v3.api.AdsRequest

/**
* Creates an `AdsRenderingSettings` object to give the AdsManager parameters that control the
* rendering of ads.
*/
abstract fun createAdsRenderingSettings(
pigeon_instance: com.google.ads.interactivemedia.v3.api.ImaSdkFactory
): com.google.ads.interactivemedia.v3.api.AdsRenderingSettings

companion object {
@Suppress("LocalVariableName")
fun setUpMessageHandlers(binaryMessenger: BinaryMessenger, api: PigeonApiImaSdkFactory?) {
Expand Down Expand Up @@ -2355,6 +2363,28 @@ abstract class PigeonApiImaSdkFactory(
channel.setMessageHandler(null)
}
}
run {
val channel =
BasicMessageChannel<Any?>(
binaryMessenger,
"dev.flutter.pigeon.interactive_media_ads.ImaSdkFactory.createAdsRenderingSettings",
codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val pigeon_instanceArg = args[0] as com.google.ads.interactivemedia.v3.api.ImaSdkFactory
val wrapped: List<Any?> =
try {
listOf(api.createAdsRenderingSettings(pigeon_instanceArg))
} catch (exception: Throwable) {
wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package dev.flutter.packages.interactive_media_ads

import com.google.ads.interactivemedia.v3.api.AdDisplayContainer
import com.google.ads.interactivemedia.v3.api.AdsLoader
import com.google.ads.interactivemedia.v3.api.AdsRenderingSettings
import com.google.ads.interactivemedia.v3.api.AdsRequest
import com.google.ads.interactivemedia.v3.api.ImaSdkFactory
import com.google.ads.interactivemedia.v3.api.ImaSdkSettings
Expand Down Expand Up @@ -51,4 +52,15 @@ class ImaSdkFactoryProxyApiTest {

assertEquals(mockRequest, api.createAdsRequest(instance))
}

@Test
fun createAdsRenderingSettings() {
val api = TestProxyApiRegistrar().getPigeonApiImaSdkFactory()

val instance = mock<ImaSdkFactory>()
val mockSettings = mock<AdsRenderingSettings>()
whenever(instance.createAdsRenderingSettings()).thenReturn(mockSettings)

assertEquals(mockSettings, api.createAdsRenderingSettings(instance))
}
}
2 changes: 1 addition & 1 deletion packages/interactive_media_ads/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ class _AdExampleWidgetState extends State<AdExampleWidget>
},
));

manager.init();
manager.init(settings: AdsRenderingSettings(enablePreloading: true));
},
onAdsLoadError: (AdsLoadErrorData data) {
debugPrint('OnAdsLoadError: ${data.error.message}');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class AdsRequestProxyAPIDelegate: PigeonApiDelegateIMAAdsRequest {
/// The current version of the `interactive_media_ads` plugin.
///
/// This must match the version in pubspec.yaml.
static let pluginVersion = "0.2.2+15"
static let pluginVersion = "0.2.3"

func pigeonDefaultConstructor(
pigeonApi: PigeonApiIMAAdsRequest, adTagUrl: String, adDisplayContainer: IMAAdDisplayContainer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
export 'src/ad_display_container.dart';
export 'src/ads_loader.dart';
export 'src/ads_manager_delegate.dart';
export 'src/ads_rendering_settings.dart';
export 'src/ads_request.dart';
export 'src/android/android_interactive_media_ads.dart'
show AndroidInteractiveMediaAds;
Expand All @@ -18,4 +19,5 @@ export 'src/platform_interface/platform_interface.dart'
AdErrorType,
AdEvent,
AdEventType,
AdUIElement,
AdsLoadErrorData;
5 changes: 3 additions & 2 deletions packages/interactive_media_ads/lib/src/ads_loader.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:flutter/foundation.dart';

import 'ad_display_container.dart';
import 'ads_manager_delegate.dart';
import 'ads_rendering_settings.dart';
import 'ads_request.dart';
import 'platform_interface/platform_interface.dart';

Expand Down Expand Up @@ -145,8 +146,8 @@ class AdsManager {
final PlatformAdsManager platform;

/// Initializes the ad experience using default rendering settings.
Future<void> init() {
return platform.init(AdsManagerInitParams());
Future<void> init({AdsRenderingSettings? settings}) {
return platform.init(settings: settings?.platform);
}

/// Starts playing the ads.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'ads_loader.dart';
import 'platform_interface/platform_interface.dart';

/// Defines parameters that control the rendering of ads.
class AdsRenderingSettings {
/// Creates an [AdsRenderingSettings].
AdsRenderingSettings({
int? bitrate,
bool? enablePreloading,
Duration loadVideoTimeout = const Duration(seconds: 8),
List<String>? mimeTypes,
Duration? playAdsAfterTime,
Set<AdUIElement>? uiElements,
}) : this.fromPlatform(PlatformAdsRenderingSettings(
PlatformAdsRenderingSettingsCreationParams(
bitrate: bitrate,
enablePreloading: enablePreloading,
loadVideoTimeout: loadVideoTimeout,
mimeTypes: mimeTypes,
playAdsAfterTime: playAdsAfterTime,
uiElements: uiElements,
),
));

/// Constructs an [AdsRenderingSettings] from a specific platform
/// implementation.
AdsRenderingSettings.fromPlatform(this.platform);

/// Implementation of [PlatformAdsRenderingSettings] for the current platform.
final PlatformAdsRenderingSettings platform;

/// Maximum recommended bitrate.
///
/// The value is in kbit/s.
///
/// The SDK will select media which has a bitrate below the specified max or
/// the closest bitrate if there is no media with a lower bitrate found.
///
/// If null, the bitrate will be selected by the SDK, using the currently
/// detected network speed (cellular or Wi-Fi).
int? get bitrate => platform.params.bitrate;

/// If set, the SDK will instruct the player to load the creative in response
/// to [AdsManager.init].
///
/// This allows the player to preload the ad at any point before
/// [AdsManager.start].
///
/// If null, the platform will decide the default value.
bool? get enablePreloading => platform.params.enablePreloading;

/// Specifies a non-default amount of time to wait for media to load before
/// timing out.
///
/// This only applies to the IMA client-side SDK..
Duration get loadVideoTimeout => platform.params.loadVideoTimeout;

/// The SDK will prioritize the media with MIME type on the list.
///
/// This only refers to the mime types of videos to be selected for linear
/// ads.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're keeping nullability, this needs a comment explaining that null means platform default.

List<String>? get mimeTypes => platform.params.mimeTypes;

/// For VMAP and ad rules playlists, only play ad breaks scheduled after this
/// time.
///
/// This setting is strictly after the specified time. For example, setting
/// `playAdsAfterTime` to 15s will ignore an ad break scheduled to play at
/// 15s.
Duration? get playAdsAfterTime => platform.params.playAdsAfterTime;

/// Sets the ad UI elements to be rendered by the IMA SDK.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Sets the" -> "The" (since it's a getter)

///
/// Some modifications to the uiElements list may have no effect for specific
/// ads.
Set<AdUIElement>? get uiElements => platform.params.uiElements;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment about nullability.

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'dart:async';
import 'package:meta/meta.dart';

import '../platform_interface/platform_interface.dart';
import 'android_ads_rendering_settings.dart';
import 'enum_converter_utils.dart';
import 'interactive_media_ads.g.dart' as ima;
import 'interactive_media_ads_proxy.dart';
Expand All @@ -32,8 +33,15 @@ class AndroidAdsManager extends PlatformAdsManager {
}

@override
Future<void> init(AdsManagerInitParams params) {
return _manager.init(null);
Future<void> init({PlatformAdsRenderingSettings? settings}) async {
ima.AdsRenderingSettings? nativeSettings;
if (settings != null) {
nativeSettings = settings is AndroidAdsRenderingSettings
? await settings.nativeSettings
: await AndroidAdsRenderingSettings(settings.params).nativeSettings;
}

await _manager.init(nativeSettings);
}

@override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';

import 'package:meta/meta.dart';

import '../platform_interface/platform_interface.dart';
import 'interactive_media_ads.g.dart' as ima;
import 'interactive_media_ads_proxy.dart';

/// Android implementation of [PlatformAdsRenderingSettingsCreationParams].
final class AndroidAdsRenderingSettingsCreationParams
extends PlatformAdsRenderingSettingsCreationParams {
/// Constructs an [AndroidAdsRenderingSettingsCreationParams].
const AndroidAdsRenderingSettingsCreationParams({
super.bitrate,
super.enablePreloading,
super.loadVideoTimeout,
super.mimeTypes,
super.playAdsAfterTime,
super.uiElements,
this.enableCustomTabs = false,
@visibleForTesting InteractiveMediaAdsProxy? proxy,
}) : _proxy = proxy ?? const InteractiveMediaAdsProxy(),
super();

/// Creates a [AndroidAdsRenderingSettingsCreationParams] from an instance of
/// [PlatformAdsRenderingSettingsCreationParams].
factory AndroidAdsRenderingSettingsCreationParams.fromPlatformAdsRenderingSettingsCreationParams(
PlatformAdsRenderingSettingsCreationParams params, {
bool enableCustomTabs = false,
}) {
return AndroidAdsRenderingSettingsCreationParams(
bitrate: params.bitrate,
enablePreloading: params.enablePreloading,
loadVideoTimeout: params.loadVideoTimeout,
mimeTypes: params.mimeTypes,
playAdsAfterTime: params.playAdsAfterTime,
uiElements: params.uiElements,
enableCustomTabs: enableCustomTabs,
);
}

final InteractiveMediaAdsProxy _proxy;

/// Notifies the SDK whether to launch the click-through URL using Custom Tabs
/// feature.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does null mean for this?

Copy link
Contributor Author

@bparrishMines bparrishMines Nov 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My thought process was that null essentially meant that the native value will not be set with any value. But since the documentation states that false is the default, it is probably safe to always set it and make the default on the Dart side false.

I was actually considering what a better design for setting optional native parameters was. Is it better to always set a value if the default is stated or provide an option to not set it? (e.g. null).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the internal implementation level, either is fine, we should just document what null means when we provide the option.

final bool enableCustomTabs;
}

/// Android implementation of [PlatformAdsRenderingSettings].
base class AndroidAdsRenderingSettings extends PlatformAdsRenderingSettings {
/// Constructs an [AndroidAdsRenderingSettings].
AndroidAdsRenderingSettings(super.params) : super.implementation() {
final Completer<ima.AdsRenderingSettings> nativeSettingsCompleter =
Completer<ima.AdsRenderingSettings>();
nativeSettings = nativeSettingsCompleter.future;

_androidParams._proxy
.instanceImaSdkFactory()
.createAdsRenderingSettings()
.then((ima.AdsRenderingSettings nativeSettings) async {
await Future.wait(<Future<void>>[
if (_androidParams.bitrate != null)
nativeSettings.setBitrateKbps(params.bitrate!),
if (_androidParams.enablePreloading != null)
nativeSettings.setEnablePreloading(_androidParams.enablePreloading!),
nativeSettings.setLoadVideoTimeout(
_androidParams.loadVideoTimeout.inMilliseconds,
),
if (_androidParams.mimeTypes != null)
nativeSettings.setMimeTypes(_androidParams.mimeTypes!),
if (_androidParams.playAdsAfterTime != null)
nativeSettings.setPlayAdsAfterTime(
_androidParams.playAdsAfterTime!.inMicroseconds /
Duration.microsecondsPerSecond,
),
if (_androidParams.uiElements != null)
nativeSettings.setUiElements(
_androidParams.uiElements!.map(
(AdUIElement element) {
return switch (element) {
AdUIElement.adAttribution => ima.UiElement.adAttribution,
AdUIElement.countdown => ima.UiElement.countdown,
};
},
).toList(),
),
nativeSettings.setEnableCustomTabs(_androidParams.enableCustomTabs)
]);

nativeSettingsCompleter.complete(nativeSettings);
});
}

/// The native Android AdsRenderingSettings.
///
/// The instantiation of the native AdsRenderingSettings is asynchronous, so
/// this provides access to the value after it is created and all params have
/// been set.
@internal
late final Future<ima.AdsRenderingSettings> nativeSettings;

late final AndroidAdsRenderingSettingsCreationParams _androidParams =
params is AndroidAdsRenderingSettingsCreationParams
? params as AndroidAdsRenderingSettingsCreationParams
: AndroidAdsRenderingSettingsCreationParams
.fromPlatformAdsRenderingSettingsCreationParams(
params,
);
}
Loading