Skip to content

Commit d24b32f

Browse files
[interactive_media_ads] Adds support for companion ads (#9260)
Fixes flutter/flutter#153446 Adds `CompanionAdSlot` and `AdDisplayContainer(companionAds)`. ## Pre-Review Checklist [^1]: Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling.
1 parent 0b322a2 commit d24b32f

File tree

57 files changed

+4595
-1361
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+4595
-1361
lines changed

packages/interactive_media_ads/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.2.4
2+
3+
* Adds support for companion ads. See `CompanionAdSlot` and `AdDisplayContainer(companionAds)`.
4+
15
## 0.2.3+12
26

37
* Fixes appending request agent to ad tags that contain a query.

packages/interactive_media_ads/README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ If building on Android, add the user permissions required by the IMA SDK for req
5555
Add the import statements for the `interactive_media_ads` and [video_player][7]. Both plugins should
5656
already be added to your `pubspec.yaml`.
5757

58-
<?code-excerpt "example/lib/main.dart (imports)"?>
58+
<?code-excerpt "example/lib/readme_example.dart (imports)"?>
5959
```dart
6060
import 'package:interactive_media_ads/interactive_media_ads.dart';
6161
import 'package:video_player/video_player.dart';
@@ -66,7 +66,7 @@ import 'package:video_player/video_player.dart';
6666
Create a new [StatefulWidget](https://api.flutter.dev/flutter/widgets/StatefulWidget-class.html)
6767
that handles displaying Ads and playing content.
6868

69-
<?code-excerpt "example/lib/main.dart (example_widget)"?>
69+
<?code-excerpt "example/lib/readme_example.dart (example_widget)"?>
7070
```dart
7171
/// Example widget displaying an Ad before a video.
7272
class AdExampleWidget extends StatefulWidget {
@@ -120,7 +120,7 @@ Instantiate the [AdDisplayContainer][3] for playing Ads and the
120120
[VideoPlayerController](https://pub.dev/documentation/video_player/latest/video_player/VideoPlayerController-class.html)
121121
for playing content.
122122

123-
<?code-excerpt "example/lib/main.dart (ad_and_content_players)"?>
123+
<?code-excerpt "example/lib/readme_example.dart (ad_and_content_players)"?>
124124
```dart
125125
late final AdDisplayContainer _adDisplayContainer = AdDisplayContainer(
126126
onContainerAdded: (AdDisplayContainer container) {
@@ -194,7 +194,7 @@ void initState() {
194194

195195
Return a `Widget` that contains the ad player and the content player.
196196

197-
<?code-excerpt "example/lib/main.dart (widget_build)"?>
197+
<?code-excerpt "example/lib/readme_example.dart (widget_build)"?>
198198
```dart
199199
@override
200200
Widget build(BuildContext context) {
@@ -244,7 +244,7 @@ Widget build(BuildContext context) {
244244

245245
Handle requesting ads and add event listeners to handle when content should be displayed or hidden.
246246

247-
<?code-excerpt "example/lib/main.dart (request_ads)"?>
247+
<?code-excerpt "example/lib/readme_example.dart (request_ads)"?>
248248
```dart
249249
Future<void> _requestAds(AdDisplayContainer container) {
250250
return _adsLoader.requestAds(AdsRequest(
@@ -292,7 +292,7 @@ Future<void> _pauseContent() {
292292

293293
Dispose the content player and destroy the [AdsManager][6].
294294

295-
<?code-excerpt "example/lib/main.dart (dispose)"?>
295+
<?code-excerpt "example/lib/readme_example.dart (dispose)"?>
296296
```dart
297297
@override
298298
void dispose() {

packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/AdsRequestProxyApi.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class AdsRequestProxyApi(override val pigeonRegistrar: ProxyApiRegistrar) :
2121
*
2222
* This must match the version in pubspec.yaml.
2323
*/
24-
const val pluginVersion = "0.2.3+12"
24+
const val pluginVersion = "0.2.4"
2525
}
2626

2727
override fun setAdTagUrl(pigeon_instance: AdsRequest, adTagUrl: String) {

packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/BaseDisplayContainerProxyApi.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,21 @@
44

55
package dev.flutter.packages.interactive_media_ads
66

7+
import com.google.ads.interactivemedia.v3.api.BaseDisplayContainer
8+
import com.google.ads.interactivemedia.v3.api.CompanionAdSlot
9+
710
/**
811
* ProxyApi implementation for [com.google.ads.interactivemedia.v3.api.BaseDisplayContainer].
912
*
1013
* <p>This class may handle instantiating native object instances that are attached to a Dart
1114
* instance or handle method calls on the associated native class or an instance of that class.
1215
*/
1316
class BaseDisplayContainerProxyApi(override val pigeonRegistrar: ProxyApiRegistrar) :
14-
PigeonApiBaseDisplayContainer(pigeonRegistrar)
17+
PigeonApiBaseDisplayContainer(pigeonRegistrar) {
18+
override fun setCompanionSlots(
19+
pigeon_instance: BaseDisplayContainer,
20+
companionSlots: List<CompanionAdSlot>?
21+
) {
22+
return pigeon_instance.setCompanionSlots(companionSlots)
23+
}
24+
}

packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/CompanionAdSlotProxyApi.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,8 @@ class CompanionAdSlotProxyApi(override val pigeonRegistrar: ProxyApiRegistrar) :
4747
override fun setSize(pigeon_instance: CompanionAdSlot, width: Long, height: Long) {
4848
pigeon_instance.setSize(width.toInt(), height.toInt())
4949
}
50+
51+
override fun setFluidSize(pigeon_instance: CompanionAdSlot) {
52+
pigeon_instance.setSize(CompanionAdSlot.FLUID_SIZE, CompanionAdSlot.FLUID_SIZE)
53+
}
5054
}

packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/ImaSdkFactoryProxyApi.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import com.google.ads.interactivemedia.v3.api.AdDisplayContainer
99
import com.google.ads.interactivemedia.v3.api.AdsLoader
1010
import com.google.ads.interactivemedia.v3.api.AdsRenderingSettings
1111
import com.google.ads.interactivemedia.v3.api.AdsRequest
12+
import com.google.ads.interactivemedia.v3.api.CompanionAdSlot
1213
import com.google.ads.interactivemedia.v3.api.ImaSdkFactory
1314
import com.google.ads.interactivemedia.v3.api.ImaSdkSettings
1415
import com.google.ads.interactivemedia.v3.api.player.VideoAdPlayer
@@ -32,6 +33,10 @@ class ImaSdkFactoryProxyApi(override val pigeonRegistrar: ProxyApiRegistrar) :
3233
return ImaSdkFactory.createAdDisplayContainer(container, player)
3334
}
3435

36+
override fun createCompanionAdSlot(pigeon_instance: ImaSdkFactory): CompanionAdSlot {
37+
return pigeon_instance.createCompanionAdSlot()
38+
}
39+
3540
override fun createImaSdkSettings(pigeon_instance: ImaSdkFactory): ImaSdkSettings {
3641
return pigeon_instance.createImaSdkSettings()
3742
}

packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsLibrary.g.kt

Lines changed: 111 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright 2013 The Flutter Authors. All rights reserved.
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
4-
// Autogenerated from Pigeon (v25.3.1), do not edit directly.
4+
// Autogenerated from Pigeon (v25.3.2), do not edit directly.
55
// See also: https://pub.dev/packages/pigeon
66
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")
77

@@ -387,9 +387,7 @@ abstract class InteractiveMediaAdsLibraryPigeonProxyApiRegistrar(
387387
* An implementation of [PigeonApiBaseDisplayContainer] used to add a new Dart instance of
388388
* `BaseDisplayContainer` to the Dart `InstanceManager`.
389389
*/
390-
open fun getPigeonApiBaseDisplayContainer(): PigeonApiBaseDisplayContainer {
391-
return PigeonApiBaseDisplayContainer(this)
392-
}
390+
abstract fun getPigeonApiBaseDisplayContainer(): PigeonApiBaseDisplayContainer
393391

394392
/**
395393
* An implementation of [PigeonApiAdDisplayContainer] used to add a new Dart instance of
@@ -592,6 +590,8 @@ abstract class InteractiveMediaAdsLibraryPigeonProxyApiRegistrar(
592590
fun setUp() {
593591
InteractiveMediaAdsLibraryPigeonInstanceManagerApi.setUpMessageHandlers(
594592
binaryMessenger, instanceManager)
593+
PigeonApiBaseDisplayContainer.setUpMessageHandlers(
594+
binaryMessenger, getPigeonApiBaseDisplayContainer())
595595
PigeonApiAdsLoader.setUpMessageHandlers(binaryMessenger, getPigeonApiAdsLoader())
596596
PigeonApiAdsRequest.setUpMessageHandlers(binaryMessenger, getPigeonApiAdsRequest())
597597
PigeonApiContentProgressProvider.setUpMessageHandlers(
@@ -621,6 +621,7 @@ abstract class InteractiveMediaAdsLibraryPigeonProxyApiRegistrar(
621621

622622
fun tearDown() {
623623
InteractiveMediaAdsLibraryPigeonInstanceManagerApi.setUpMessageHandlers(binaryMessenger, null)
624+
PigeonApiBaseDisplayContainer.setUpMessageHandlers(binaryMessenger, null)
624625
PigeonApiAdsLoader.setUpMessageHandlers(binaryMessenger, null)
625626
PigeonApiAdsRequest.setUpMessageHandlers(binaryMessenger, null)
626627
PigeonApiContentProgressProvider.setUpMessageHandlers(binaryMessenger, null)
@@ -1004,9 +1005,55 @@ private open class InteractiveMediaAdsLibraryPigeonCodec : StandardMessageCodec(
10041005
* https://developers.google.com/interactive-media-ads/docs/sdks/android/client-side/api/reference/com/google/ads/interactivemedia/v3/api/BaseDisplayContainer.html.
10051006
*/
10061007
@Suppress("UNCHECKED_CAST")
1007-
open class PigeonApiBaseDisplayContainer(
1008+
abstract class PigeonApiBaseDisplayContainer(
10081009
open val pigeonRegistrar: InteractiveMediaAdsLibraryPigeonProxyApiRegistrar
10091010
) {
1011+
/**
1012+
* Sets slots for displaying companions.
1013+
*
1014+
* Passing null will reset the container to having no companion slots.
1015+
*/
1016+
abstract fun setCompanionSlots(
1017+
pigeon_instance: com.google.ads.interactivemedia.v3.api.BaseDisplayContainer,
1018+
companionSlots: List<com.google.ads.interactivemedia.v3.api.CompanionAdSlot>?
1019+
)
1020+
1021+
companion object {
1022+
@Suppress("LocalVariableName")
1023+
fun setUpMessageHandlers(
1024+
binaryMessenger: BinaryMessenger,
1025+
api: PigeonApiBaseDisplayContainer?
1026+
) {
1027+
val codec = api?.pigeonRegistrar?.codec ?: InteractiveMediaAdsLibraryPigeonCodec()
1028+
run {
1029+
val channel =
1030+
BasicMessageChannel<Any?>(
1031+
binaryMessenger,
1032+
"dev.flutter.pigeon.interactive_media_ads.BaseDisplayContainer.setCompanionSlots",
1033+
codec)
1034+
if (api != null) {
1035+
channel.setMessageHandler { message, reply ->
1036+
val args = message as List<Any?>
1037+
val pigeon_instanceArg =
1038+
args[0] as com.google.ads.interactivemedia.v3.api.BaseDisplayContainer
1039+
val companionSlotsArg =
1040+
args[1] as List<com.google.ads.interactivemedia.v3.api.CompanionAdSlot>?
1041+
val wrapped: List<Any?> =
1042+
try {
1043+
api.setCompanionSlots(pigeon_instanceArg, companionSlotsArg)
1044+
listOf(null)
1045+
} catch (exception: Throwable) {
1046+
InteractiveMediaAdsLibraryPigeonUtils.wrapError(exception)
1047+
}
1048+
reply.reply(wrapped)
1049+
}
1050+
} else {
1051+
channel.setMessageHandler(null)
1052+
}
1053+
}
1054+
}
1055+
}
1056+
10101057
@Suppress("LocalVariableName", "FunctionName")
10111058
/** Creates a Dart instance of BaseDisplayContainer and attaches it to [pigeon_instanceArg]. */
10121059
fun pigeon_newInstance(
@@ -2267,6 +2314,11 @@ abstract class PigeonApiImaSdkFactory(
22672314
player: com.google.ads.interactivemedia.v3.api.player.VideoAdPlayer
22682315
): com.google.ads.interactivemedia.v3.api.AdDisplayContainer
22692316

2317+
/** Creates a CompanionAdSlot for the SDK to fill with companion ads. */
2318+
abstract fun createCompanionAdSlot(
2319+
pigeon_instance: com.google.ads.interactivemedia.v3.api.ImaSdkFactory
2320+
): com.google.ads.interactivemedia.v3.api.CompanionAdSlot
2321+
22702322
/** Creates an `ImaSdkSettings` object for configuring the IMA SDK. */
22712323
abstract fun createImaSdkSettings(
22722324
pigeon_instance: com.google.ads.interactivemedia.v3.api.ImaSdkFactory
@@ -2343,6 +2395,28 @@ abstract class PigeonApiImaSdkFactory(
23432395
channel.setMessageHandler(null)
23442396
}
23452397
}
2398+
run {
2399+
val channel =
2400+
BasicMessageChannel<Any?>(
2401+
binaryMessenger,
2402+
"dev.flutter.pigeon.interactive_media_ads.ImaSdkFactory.createCompanionAdSlot",
2403+
codec)
2404+
if (api != null) {
2405+
channel.setMessageHandler { message, reply ->
2406+
val args = message as List<Any?>
2407+
val pigeon_instanceArg = args[0] as com.google.ads.interactivemedia.v3.api.ImaSdkFactory
2408+
val wrapped: List<Any?> =
2409+
try {
2410+
listOf(api.createCompanionAdSlot(pigeon_instanceArg))
2411+
} catch (exception: Throwable) {
2412+
InteractiveMediaAdsLibraryPigeonUtils.wrapError(exception)
2413+
}
2414+
reply.reply(wrapped)
2415+
}
2416+
} else {
2417+
channel.setMessageHandler(null)
2418+
}
2419+
}
23462420
run {
23472421
val channel =
23482422
BasicMessageChannel<Any?>(
@@ -5561,6 +5635,14 @@ abstract class PigeonApiCompanionAdSlot(
55615635
height: Long
55625636
)
55635637

5638+
/**
5639+
* Sets the size of the slot as fluid.
5640+
*
5641+
* This is a convenience method that sets both parameters of [setSize] to
5642+
* [CompanionAdSlot.FLUID_SIZE](https://developers.google.com/interactive-media-ads/docs/sdks/android/client-side/api/reference/com/google/ads/interactivemedia/v3/api/CompanionAdSlot#FLUID_SIZE()).
5643+
*/
5644+
abstract fun setFluidSize(pigeon_instance: com.google.ads.interactivemedia.v3.api.CompanionAdSlot)
5645+
55645646
companion object {
55655647
@Suppress("LocalVariableName")
55665648
fun setUpMessageHandlers(binaryMessenger: BinaryMessenger, api: PigeonApiCompanionAdSlot?) {
@@ -5760,6 +5842,30 @@ abstract class PigeonApiCompanionAdSlot(
57605842
channel.setMessageHandler(null)
57615843
}
57625844
}
5845+
run {
5846+
val channel =
5847+
BasicMessageChannel<Any?>(
5848+
binaryMessenger,
5849+
"dev.flutter.pigeon.interactive_media_ads.CompanionAdSlot.setFluidSize",
5850+
codec)
5851+
if (api != null) {
5852+
channel.setMessageHandler { message, reply ->
5853+
val args = message as List<Any?>
5854+
val pigeon_instanceArg =
5855+
args[0] as com.google.ads.interactivemedia.v3.api.CompanionAdSlot
5856+
val wrapped: List<Any?> =
5857+
try {
5858+
api.setFluidSize(pigeon_instanceArg)
5859+
listOf(null)
5860+
} catch (exception: Throwable) {
5861+
InteractiveMediaAdsLibraryPigeonUtils.wrapError(exception)
5862+
}
5863+
reply.reply(wrapped)
5864+
}
5865+
} else {
5866+
channel.setMessageHandler(null)
5867+
}
5868+
}
57635869
}
57645870
}
57655871

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
package dev.flutter.packages.interactive_media_ads
6+
7+
import com.google.ads.interactivemedia.v3.api.BaseDisplayContainer
8+
import com.google.ads.interactivemedia.v3.api.CompanionAdSlot
9+
import kotlin.test.Test
10+
import org.mockito.kotlin.mock
11+
import org.mockito.kotlin.verify
12+
13+
class BaseDisplayContainerProxyApiTest {
14+
@Test
15+
fun setCompanionSlots() {
16+
val api = TestProxyApiRegistrar().getPigeonApiBaseDisplayContainer()
17+
18+
val instance = mock<BaseDisplayContainer>()
19+
val companionSlots = listOf(mock<CompanionAdSlot>())
20+
api.setCompanionSlots(instance, companionSlots)
21+
22+
verify(instance).setCompanionSlots(companionSlots)
23+
}
24+
}

packages/interactive_media_ads/android/src/test/kotlin/dev/flutter/packages/interactive_media_ads/CompanionAdSlotProxyApiTest.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,14 @@ class CompanionAdSlotProxyApiTest {
101101

102102
verify(instance).setSize(width.toInt(), height.toInt())
103103
}
104+
105+
@Test
106+
fun setFluidSize() {
107+
val api = TestProxyApiRegistrar().getPigeonApiCompanionAdSlot()
108+
109+
val instance = mock<CompanionAdSlot>()
110+
api.setFluidSize(instance)
111+
112+
verify(instance).setSize(CompanionAdSlot.FLUID_SIZE, CompanionAdSlot.FLUID_SIZE)
113+
}
104114
}

packages/interactive_media_ads/android/src/test/kotlin/dev/flutter/packages/interactive_media_ads/ImaSdkFactoryProxyApiTest.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import com.google.ads.interactivemedia.v3.api.AdDisplayContainer
88
import com.google.ads.interactivemedia.v3.api.AdsLoader
99
import com.google.ads.interactivemedia.v3.api.AdsRenderingSettings
1010
import com.google.ads.interactivemedia.v3.api.AdsRequest
11+
import com.google.ads.interactivemedia.v3.api.CompanionAdSlot
1112
import com.google.ads.interactivemedia.v3.api.ImaSdkFactory
1213
import com.google.ads.interactivemedia.v3.api.ImaSdkSettings
1314
import kotlin.test.Test
@@ -42,6 +43,17 @@ class ImaSdkFactoryProxyApiTest {
4243
assertEquals(mockAdsLoader, api.createAdsLoader(instance, mockSettings, mockContainer))
4344
}
4445

46+
@Test
47+
fun createCompanionAdSlot() {
48+
val api = TestProxyApiRegistrar().getPigeonApiImaSdkFactory()
49+
50+
val instance = mock<ImaSdkFactory>()
51+
val mockAdSlot = mock<CompanionAdSlot>()
52+
whenever(instance.createCompanionAdSlot()).thenReturn(mockAdSlot)
53+
54+
assertEquals(mockAdSlot, api.createCompanionAdSlot(instance))
55+
}
56+
4557
@Test
4658
fun createAdsRequest() {
4759
val api = TestProxyApiRegistrar().getPigeonApiImaSdkFactory()

0 commit comments

Comments
 (0)