From 3c7ff7512cdb0485b3f6f3ca5ea98afff5c862be Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Wed, 3 Sep 2025 13:37:37 -0400 Subject: [PATCH 1/3] native implementations of ad cue points --- .../AdsManagerProxyApi.kt | 2 +- .../InteractiveMediaAdsLibrary.g.kt | 43 ++++---------- .../AdsManagerProxyApiTest.kt | 4 +- .../ios/RunnerTests/AdsManagerTests.swift | 15 +++++ .../AdsManagerProxyAPIDelegate.swift | 8 +++ .../InteractiveMediaAdsLibrary.g.swift | 10 +++- .../android/android_ad_display_container.dart | 2 +- .../src/android/interactive_media_ads.g.dart | 56 ++++++------------- .../lib/src/ios/interactive_media_ads.g.dart | 19 ++++++- .../interactive_media_ads_android.dart | 10 ++-- .../pigeons/interactive_media_ads_ios.dart | 6 ++ 11 files changed, 91 insertions(+), 84 deletions(-) diff --git a/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/AdsManagerProxyApi.kt b/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/AdsManagerProxyApi.kt index 11d7250c88c..4d04776b275 100644 --- a/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/AdsManagerProxyApi.kt +++ b/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/AdsManagerProxyApi.kt @@ -26,7 +26,7 @@ class AdsManagerProxyApi(override val pigeonRegistrar: ProxyApiRegistrar) : pigeon_instance.start() } - override fun getAdCuePoints(pigeon_instance: AdsManager): List { + override fun adCuePoints(pigeon_instance: AdsManager): List { return pigeon_instance.adCuePoints.map { it.toDouble() } } diff --git a/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsLibrary.g.kt b/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsLibrary.g.kt index f0028be7e34..9f212b5997e 100644 --- a/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsLibrary.g.kt +++ b/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsLibrary.g.kt @@ -2074,6 +2074,15 @@ abstract class PigeonApiContentProgressProvider( abstract class PigeonApiAdsManager( open val pigeonRegistrar: InteractiveMediaAdsLibraryPigeonProxyApiRegistrar ) { + /** + * List of content time offsets in seconds at which ad breaks are scheduled. + * + * The list will be empty if no ad breaks are scheduled. + */ + abstract fun adCuePoints( + pigeon_instance: com.google.ads.interactivemedia.v3.api.AdsManager + ): List + /** Discards current ad break and resumes content. */ abstract fun discardAdBreak(pigeon_instance: com.google.ads.interactivemedia.v3.api.AdsManager) @@ -2083,15 +2092,6 @@ abstract class PigeonApiAdsManager( /** Starts playing the ads. */ abstract fun start(pigeon_instance: com.google.ads.interactivemedia.v3.api.AdsManager) - /** - * List of content time offsets in seconds at which ad breaks are scheduled. - * - * The list will be empty if no ad breaks are scheduled. - */ - abstract fun getAdCuePoints( - pigeon_instance: com.google.ads.interactivemedia.v3.api.AdsManager - ): List - /** Resumes the current ad. */ abstract fun resume(pigeon_instance: com.google.ads.interactivemedia.v3.api.AdsManager) @@ -2171,28 +2171,6 @@ abstract class PigeonApiAdsManager( channel.setMessageHandler(null) } } - run { - val channel = - BasicMessageChannel( - binaryMessenger, - "dev.flutter.pigeon.interactive_media_ads.AdsManager.getAdCuePoints", - codec) - if (api != null) { - channel.setMessageHandler { message, reply -> - val args = message as List - val pigeon_instanceArg = args[0] as com.google.ads.interactivemedia.v3.api.AdsManager - val wrapped: List = - try { - listOf(api.getAdCuePoints(pigeon_instanceArg)) - } catch (exception: Throwable) { - InteractiveMediaAdsLibraryPigeonUtils.wrapError(exception) - } - reply.reply(wrapped) - } - } else { - channel.setMessageHandler(null) - } - } run { val channel = BasicMessageChannel( @@ -2255,11 +2233,12 @@ abstract class PigeonApiAdsManager( } else { val pigeon_identifierArg = pigeonRegistrar.instanceManager.addHostCreatedInstance(pigeon_instanceArg) + val adCuePointsArg = adCuePoints(pigeon_instanceArg) val binaryMessenger = pigeonRegistrar.binaryMessenger val codec = pigeonRegistrar.codec val channelName = "dev.flutter.pigeon.interactive_media_ads.AdsManager.pigeon_newInstance" val channel = BasicMessageChannel(binaryMessenger, channelName, codec) - channel.send(listOf(pigeon_identifierArg)) { + channel.send(listOf(pigeon_identifierArg, adCuePointsArg)) { if (it is List<*>) { if (it.size > 1) { callback( diff --git a/packages/interactive_media_ads/android/src/test/kotlin/dev/flutter/packages/interactive_media_ads/AdsManagerProxyApiTest.kt b/packages/interactive_media_ads/android/src/test/kotlin/dev/flutter/packages/interactive_media_ads/AdsManagerProxyApiTest.kt index e394e4761b8..37becf42139 100644 --- a/packages/interactive_media_ads/android/src/test/kotlin/dev/flutter/packages/interactive_media_ads/AdsManagerProxyApiTest.kt +++ b/packages/interactive_media_ads/android/src/test/kotlin/dev/flutter/packages/interactive_media_ads/AdsManagerProxyApiTest.kt @@ -43,14 +43,14 @@ class AdsManagerProxyApiTest { } @Test - fun getAdCuePoints() { + fun adCuePoints() { val api = TestProxyApiRegistrar().getPigeonApiAdsManager() val instance = mock() val value = listOf(1.0) whenever(instance.adCuePoints).thenReturn(listOf(1.0f)) - assertEquals(value, api.getAdCuePoints(instance)) + assertEquals(value, api.adCuePoints(instance)) } @Test diff --git a/packages/interactive_media_ads/example/ios/RunnerTests/AdsManagerTests.swift b/packages/interactive_media_ads/example/ios/RunnerTests/AdsManagerTests.swift index 48c232dc751..3e4b645a24e 100644 --- a/packages/interactive_media_ads/example/ios/RunnerTests/AdsManagerTests.swift +++ b/packages/interactive_media_ads/example/ios/RunnerTests/AdsManagerTests.swift @@ -101,6 +101,17 @@ final class AdsManagerTests: XCTestCase { XCTAssertTrue(instance.destroyCalled) } + + func testAdCuePoints() { + let registrar = TestProxyApiRegistrar() + let api = registrar.apiDelegate.pigeonApiIMAAdsManager(registrar) + + let instance = TestAdsManager.customInit() + + let value = try? api.pigeonDelegate.adCuePoints(pigeonApi: api, pigeonInstance: instance) + + XCTAssertEqual(value, [2.2, 3.3]) + } } class TestAdsManager: IMAAdsManager { @@ -146,4 +157,8 @@ class TestAdsManager: IMAAdsManager { override func destroy() { destroyCalled = true } + + override var adCuePoints: [Any] { + return [2.2, 3.3] + } } diff --git a/packages/interactive_media_ads/ios/interactive_media_ads/Sources/interactive_media_ads/AdsManagerProxyAPIDelegate.swift b/packages/interactive_media_ads/ios/interactive_media_ads/Sources/interactive_media_ads/AdsManagerProxyAPIDelegate.swift index 3916362e4cb..bec38a5bf7f 100644 --- a/packages/interactive_media_ads/ios/interactive_media_ads/Sources/interactive_media_ads/AdsManagerProxyAPIDelegate.swift +++ b/packages/interactive_media_ads/ios/interactive_media_ads/Sources/interactive_media_ads/AdsManagerProxyAPIDelegate.swift @@ -47,4 +47,12 @@ class AdsManagerProxyAPIDelegate: PigeonApiDelegateIMAAdsManager { func destroy(pigeonApi: PigeonApiIMAAdsManager, pigeonInstance: IMAAdsManager) throws { pigeonInstance.destroy() } + + func adCuePoints(pigeonApi: PigeonApiIMAAdsManager, pigeonInstance: IMAAdsManager) throws + -> [Double] + { + return pigeonInstance.adCuePoints.map { cuePoint -> Double in + return (cuePoint as! NSNumber).doubleValue + } + } } diff --git a/packages/interactive_media_ads/ios/interactive_media_ads/Sources/interactive_media_ads/InteractiveMediaAdsLibrary.g.swift b/packages/interactive_media_ads/ios/interactive_media_ads/Sources/interactive_media_ads/InteractiveMediaAdsLibrary.g.swift index 8faa555dafe..eadd38eb9cd 100644 --- a/packages/interactive_media_ads/ios/interactive_media_ads/Sources/interactive_media_ads/InteractiveMediaAdsLibrary.g.swift +++ b/packages/interactive_media_ads/ios/interactive_media_ads/Sources/interactive_media_ads/InteractiveMediaAdsLibrary.g.swift @@ -3157,6 +3157,12 @@ final class PigeonApiIMAAdError: PigeonApiProtocolIMAAdError { } } protocol PigeonApiDelegateIMAAdsManager { + /// List of content time offsets at which ad breaks are scheduled. + /// + /// List of double values in seconds. Empty list for single ads or if no ad + /// breaks are scheduled. + func adCuePoints(pigeonApi: PigeonApiIMAAdsManager, pigeonInstance: IMAAdsManager) throws + -> [Double] /// The `IMAAdsManagerDelegate` to notify with events during ad playback. func setDelegate( pigeonApi: PigeonApiIMAAdsManager, pigeonInstance: IMAAdsManager, @@ -3365,13 +3371,15 @@ final class PigeonApiIMAAdsManager: PigeonApiProtocolIMAAdsManager { } else { let pigeonIdentifierArg = pigeonRegistrar.instanceManager.addHostCreatedInstance( pigeonInstance as AnyObject) + let adCuePointsArg = try! pigeonDelegate.adCuePoints( + pigeonApi: self, pigeonInstance: pigeonInstance) let binaryMessenger = pigeonRegistrar.binaryMessenger let codec = pigeonRegistrar.codec let channelName: String = "dev.flutter.pigeon.interactive_media_ads.IMAAdsManager.pigeon_newInstance" let channel = FlutterBasicMessageChannel( name: channelName, binaryMessenger: binaryMessenger, codec: codec) - channel.sendMessage([pigeonIdentifierArg] as [Any?]) { response in + channel.sendMessage([pigeonIdentifierArg, adCuePointsArg] as [Any?]) { response in guard let listResponse = response as? [Any?] else { completion(.failure(createConnectionError(withChannelName: channelName))) return diff --git a/packages/interactive_media_ads/lib/src/android/android_ad_display_container.dart b/packages/interactive_media_ads/lib/src/android/android_ad_display_container.dart index 377f260fcb4..e7364415ac6 100644 --- a/packages/interactive_media_ads/lib/src/android/android_ad_display_container.dart +++ b/packages/interactive_media_ads/lib/src/android/android_ad_display_container.dart @@ -151,7 +151,7 @@ base class AndroidAdDisplayContainer extends PlatformAdDisplayContainer { } // Resets the state to before an ad is loaded and releases references to all - // ads and allbacks. + // ads and callbacks. void _release() { _resetStateForNextAd(); _loadedAdMediaInfoQueue.clear(); diff --git a/packages/interactive_media_ads/lib/src/android/interactive_media_ads.g.dart b/packages/interactive_media_ads/lib/src/android/interactive_media_ads.g.dart index 340fbc796be..4d5ea7220ca 100644 --- a/packages/interactive_media_ads/lib/src/android/interactive_media_ads.g.dart +++ b/packages/interactive_media_ads/lib/src/android/interactive_media_ads.g.dart @@ -2250,16 +2250,22 @@ class AdsManager extends BaseManager { AdsManager.pigeon_detached({ super.pigeon_binaryMessenger, super.pigeon_instanceManager, + required this.adCuePoints, }) : super.pigeon_detached(); late final _PigeonInternalProxyApiBaseCodec _pigeonVar_codecAdsManager = _PigeonInternalProxyApiBaseCodec(pigeon_instanceManager); + /// List of content time offsets in seconds at which ad breaks are scheduled. + /// + /// The list will be empty if no ad breaks are scheduled. + final List adCuePoints; + static void pigeon_setUpMessageHandlers({ bool pigeon_clearHandlers = false, BinaryMessenger? pigeon_binaryMessenger, PigeonInstanceManager? pigeon_instanceManager, - AdsManager Function()? pigeon_newInstance, + AdsManager Function(List adCuePoints)? pigeon_newInstance, }) { final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = _PigeonInternalProxyApiBaseCodec( @@ -2287,13 +2293,20 @@ class AdsManager extends BaseManager { arg_pigeon_instanceIdentifier != null, 'Argument for dev.flutter.pigeon.interactive_media_ads.AdsManager.pigeon_newInstance was null, expected non-null int.', ); + final List? arg_adCuePoints = + (args[1] as List?)?.cast(); + assert( + arg_adCuePoints != null, + 'Argument for dev.flutter.pigeon.interactive_media_ads.AdsManager.pigeon_newInstance was null, expected non-null List.', + ); try { (pigeon_instanceManager ?? PigeonInstanceManager.instance) .addHostCreatedInstance( - pigeon_newInstance?.call() ?? + pigeon_newInstance?.call(arg_adCuePoints!) ?? AdsManager.pigeon_detached( pigeon_binaryMessenger: pigeon_binaryMessenger, pigeon_instanceManager: pigeon_instanceManager, + adCuePoints: arg_adCuePoints!, ), arg_pigeon_instanceIdentifier!, ); @@ -2403,44 +2416,6 @@ class AdsManager extends BaseManager { } } - /// List of content time offsets in seconds at which ad breaks are scheduled. - /// - /// The list will be empty if no ad breaks are scheduled. - Future> getAdCuePoints() async { - final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = - _pigeonVar_codecAdsManager; - final BinaryMessenger? pigeonVar_binaryMessenger = pigeon_binaryMessenger; - const String pigeonVar_channelName = - 'dev.flutter.pigeon.interactive_media_ads.AdsManager.getAdCuePoints'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send( - [this], - ); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else if (pigeonVar_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (pigeonVar_replyList[0] as List?)!.cast(); - } - } - /// Resumes the current ad. Future resume() async { final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = @@ -2511,6 +2486,7 @@ class AdsManager extends BaseManager { return AdsManager.pigeon_detached( pigeon_binaryMessenger: pigeon_binaryMessenger, pigeon_instanceManager: pigeon_instanceManager, + adCuePoints: adCuePoints, ); } } diff --git a/packages/interactive_media_ads/lib/src/ios/interactive_media_ads.g.dart b/packages/interactive_media_ads/lib/src/ios/interactive_media_ads.g.dart index d12b99fd5ff..ecbe7388392 100644 --- a/packages/interactive_media_ads/lib/src/ios/interactive_media_ads.g.dart +++ b/packages/interactive_media_ads/lib/src/ios/interactive_media_ads.g.dart @@ -3855,16 +3855,23 @@ class IMAAdsManager extends NSObject { IMAAdsManager.pigeon_detached({ super.pigeon_binaryMessenger, super.pigeon_instanceManager, + required this.adCuePoints, }) : super.pigeon_detached(); late final _PigeonInternalProxyApiBaseCodec _pigeonVar_codecIMAAdsManager = _PigeonInternalProxyApiBaseCodec(pigeon_instanceManager); + /// List of content time offsets at which ad breaks are scheduled. + /// + /// List of double values in seconds. Empty list for single ads or if no ad + /// breaks are scheduled. + final List adCuePoints; + static void pigeon_setUpMessageHandlers({ bool pigeon_clearHandlers = false, BinaryMessenger? pigeon_binaryMessenger, PigeonInstanceManager? pigeon_instanceManager, - IMAAdsManager Function()? pigeon_newInstance, + IMAAdsManager Function(List adCuePoints)? pigeon_newInstance, }) { final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = _PigeonInternalProxyApiBaseCodec( @@ -3892,13 +3899,20 @@ class IMAAdsManager extends NSObject { arg_pigeon_instanceIdentifier != null, 'Argument for dev.flutter.pigeon.interactive_media_ads.IMAAdsManager.pigeon_newInstance was null, expected non-null int.', ); + final List? arg_adCuePoints = + (args[1] as List?)?.cast(); + assert( + arg_adCuePoints != null, + 'Argument for dev.flutter.pigeon.interactive_media_ads.IMAAdsManager.pigeon_newInstance was null, expected non-null List.', + ); try { (pigeon_instanceManager ?? PigeonInstanceManager.instance) .addHostCreatedInstance( - pigeon_newInstance?.call() ?? + pigeon_newInstance?.call(arg_adCuePoints!) ?? IMAAdsManager.pigeon_detached( pigeon_binaryMessenger: pigeon_binaryMessenger, pigeon_instanceManager: pigeon_instanceManager, + adCuePoints: arg_adCuePoints!, ), arg_pigeon_instanceIdentifier!, ); @@ -4169,6 +4183,7 @@ class IMAAdsManager extends NSObject { return IMAAdsManager.pigeon_detached( pigeon_binaryMessenger: pigeon_binaryMessenger, pigeon_instanceManager: pigeon_instanceManager, + adCuePoints: adCuePoints, ); } } diff --git a/packages/interactive_media_ads/pigeons/interactive_media_ads_android.dart b/packages/interactive_media_ads/pigeons/interactive_media_ads_android.dart index 0671728ac42..ad678e0e58d 100644 --- a/packages/interactive_media_ads/pigeons/interactive_media_ads_android.dart +++ b/packages/interactive_media_ads/pigeons/interactive_media_ads_android.dart @@ -448,6 +448,11 @@ abstract class ContentProgressProvider { ), ) abstract class AdsManager extends BaseManager { + /// List of content time offsets in seconds at which ad breaks are scheduled. + /// + /// The list will be empty if no ad breaks are scheduled. + late List adCuePoints; + /// Discards current ad break and resumes content. void discardAdBreak(); @@ -457,11 +462,6 @@ abstract class AdsManager extends BaseManager { /// Starts playing the ads. void start(); - /// List of content time offsets in seconds at which ad breaks are scheduled. - /// - /// The list will be empty if no ad breaks are scheduled. - List getAdCuePoints(); - /// Resumes the current ad. void resume(); diff --git a/packages/interactive_media_ads/pigeons/interactive_media_ads_ios.dart b/packages/interactive_media_ads/pigeons/interactive_media_ads_ios.dart index de697aa9fcf..803c8f22486 100644 --- a/packages/interactive_media_ads/pigeons/interactive_media_ads_ios.dart +++ b/packages/interactive_media_ads/pigeons/interactive_media_ads_ios.dart @@ -630,6 +630,12 @@ abstract class IMAAdError extends NSObject { /// See https://developers.google.com/interactive-media-ads/docs/sdks/ios/client-side/reference/Classes/IMAAdsManager.html. @ProxyApi() abstract class IMAAdsManager extends NSObject { + /// List of content time offsets at which ad breaks are scheduled. + /// + /// List of double values in seconds. Empty list for single ads or if no ad + /// breaks are scheduled. + late List adCuePoints; + /// The `IMAAdsManagerDelegate` to notify with events during ad playback. void setDelegate(IMAAdsManagerDelegate? delegate); From 61bbf06059c9298e3b03c6c79559d03469f3d881 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Wed, 3 Sep 2025 14:09:59 -0400 Subject: [PATCH 2/3] add platform implementations --- .../example/lib/video_ad_example_screen.dart | 1 + .../lib/src/ads_loader.dart | 5 +++++ .../lib/src/android/android_ads_manager.dart | 11 +++++++++- .../lib/src/ios/ios_ads_manager.dart | 12 ++++++++++- .../platform_ads_manager.dart | 7 ++++++- .../test/ads_manager_test.dart | 9 +++++++++ .../test/android/ads_loader_test.mocks.dart | 20 +++++++++---------- .../test/android/ads_manager_test.dart | 10 ++++++++++ .../test/android/ads_manager_test.mocks.dart | 20 +++++++++---------- .../test/ios/ads_loader_test.mocks.dart | 9 +++++++++ .../test/ios/ads_manager_delegate_tests.dart | 4 ++++ .../test/ios/ads_manager_test.dart | 10 ++++++++++ .../test/ios/ads_manager_test.mocks.dart | 9 +++++++++ .../test/test_stubs.dart | 1 + 14 files changed, 103 insertions(+), 25 deletions(-) diff --git a/packages/interactive_media_ads/example/lib/video_ad_example_screen.dart b/packages/interactive_media_ads/example/lib/video_ad_example_screen.dart index 99a4ad1b998..5b5092b7708 100644 --- a/packages/interactive_media_ads/example/lib/video_ad_example_screen.dart +++ b/packages/interactive_media_ads/example/lib/video_ad_example_screen.dart @@ -70,6 +70,7 @@ class _VideoAdExampleScreenState extends State _adsLoader = AdsLoader( container: container, onAdsLoaded: (OnAdsLoadedData data) { + debugPrint('OnAdsLoaded: (cuePoints: ${data.manager.adCuePoints})'); final AdsManager manager = data.manager; _adsManager = data.manager; diff --git a/packages/interactive_media_ads/lib/src/ads_loader.dart b/packages/interactive_media_ads/lib/src/ads_loader.dart index 6050b8c195a..57495c24d71 100644 --- a/packages/interactive_media_ads/lib/src/ads_loader.dart +++ b/packages/interactive_media_ads/lib/src/ads_loader.dart @@ -151,6 +151,11 @@ class AdsManager { /// Implementation of [PlatformAdsManager] for the current platform. final PlatformAdsManager platform; + /// List of content time offsets at which ad breaks are scheduled. + /// + /// The list will be empty for single ads or if no ad breaks are scheduled. + List get adCuePoints => platform.adCuePoints; + /// Initializes the ad experience using default rendering settings. Future init({AdsRenderingSettings? settings}) { return platform.init(settings: settings?.platform); diff --git a/packages/interactive_media_ads/lib/src/android/android_ads_manager.dart b/packages/interactive_media_ads/lib/src/android/android_ads_manager.dart index b9fab053aba..c92ae68f0aa 100644 --- a/packages/interactive_media_ads/lib/src/android/android_ads_manager.dart +++ b/packages/interactive_media_ads/lib/src/android/android_ads_manager.dart @@ -18,7 +18,16 @@ class AndroidAdsManager extends PlatformAdsManager { @internal AndroidAdsManager(ima.AdsManager manager, {InteractiveMediaAdsProxy? proxy}) : _manager = manager, - _proxy = proxy ?? const InteractiveMediaAdsProxy(); + _proxy = proxy ?? const InteractiveMediaAdsProxy(), + super( + adCuePoints: List.unmodifiable( + manager.adCuePoints.map((double seconds) { + return Duration( + milliseconds: (seconds * Duration.millisecondsPerSecond).round(), + ); + }), + ), + ); final ima.AdsManager _manager; final InteractiveMediaAdsProxy _proxy; diff --git a/packages/interactive_media_ads/lib/src/ios/ios_ads_manager.dart b/packages/interactive_media_ads/lib/src/ios/ios_ads_manager.dart index e94f2dd46be..b44356e5e58 100644 --- a/packages/interactive_media_ads/lib/src/ios/ios_ads_manager.dart +++ b/packages/interactive_media_ads/lib/src/ios/ios_ads_manager.dart @@ -15,7 +15,17 @@ import 'ios_ads_rendering_settings.dart'; class IOSAdsManager extends PlatformAdsManager { /// Constructs an [IOSAdsManager]. @internal - IOSAdsManager(IMAAdsManager manager) : _manager = manager; + IOSAdsManager(IMAAdsManager manager) + : _manager = manager, + super( + adCuePoints: List.unmodifiable( + manager.adCuePoints.map((double seconds) { + return Duration( + milliseconds: (seconds * Duration.millisecondsPerSecond).round(), + ); + }), + ), + ); final IMAAdsManager _manager; diff --git a/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_manager.dart b/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_manager.dart index ea73ff7268e..21273ed0dde 100644 --- a/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_manager.dart +++ b/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_manager.dart @@ -15,7 +15,12 @@ base class AdsManagerStartParams {} abstract class PlatformAdsManager { /// Creates a [PlatformAdsManager]. @protected - PlatformAdsManager(); + PlatformAdsManager({required this.adCuePoints}); + + /// List of content time offsets at which ad breaks are scheduled. + /// + /// The list will be empty if no ad breaks are scheduled. + final List adCuePoints; /// Initializes the ad experience using default rendering settings. Future init({PlatformAdsRenderingSettings? settings}); diff --git a/packages/interactive_media_ads/test/ads_manager_test.dart b/packages/interactive_media_ads/test/ads_manager_test.dart index 9e9ba25dc32..4bb647cc129 100644 --- a/packages/interactive_media_ads/test/ads_manager_test.dart +++ b/packages/interactive_media_ads/test/ads_manager_test.dart @@ -102,6 +102,15 @@ void main() { final AdsManager manager = createAdsManager(platformManager); await manager.destroy(); }); + + test('adCuePoints', () async { + final TestAdsManager platformManager = TestAdsManager( + adCuePoints: const [Duration(seconds: 5)], + ); + + final AdsManager manager = createAdsManager(platformManager); + expect(platformManager.adCuePoints, manager.adCuePoints); + }); } AdsManager createAdsManager(PlatformAdsManager platformManager) { diff --git a/packages/interactive_media_ads/test/android/ads_loader_test.mocks.dart b/packages/interactive_media_ads/test/android/ads_loader_test.mocks.dart index eb56f04dcaa..da94b7f609a 100644 --- a/packages/interactive_media_ads/test/android/ads_loader_test.mocks.dart +++ b/packages/interactive_media_ads/test/android/ads_loader_test.mocks.dart @@ -433,6 +433,15 @@ class MockAdsLoadedListener extends _i1.Mock implements _i2.AdsLoadedListener { /// /// See the documentation for Mockito's code generation for more information. class MockAdsManager extends _i1.Mock implements _i2.AdsManager { + @override + List get adCuePoints => + (super.noSuchMethod( + Invocation.getter(#adCuePoints), + returnValue: [], + returnValueForMissingStub: [], + ) + as List); + @override _i2.PigeonInstanceManager get pigeon_instanceManager => (super.noSuchMethod( @@ -475,17 +484,6 @@ class MockAdsManager extends _i1.Mock implements _i2.AdsManager { ) as _i5.Future); - @override - _i5.Future> getAdCuePoints() => - (super.noSuchMethod( - Invocation.method(#getAdCuePoints, []), - returnValue: _i5.Future>.value([]), - returnValueForMissingStub: _i5.Future>.value( - [], - ), - ) - as _i5.Future>); - @override _i5.Future resume() => (super.noSuchMethod( diff --git a/packages/interactive_media_ads/test/android/ads_manager_test.dart b/packages/interactive_media_ads/test/android/ads_manager_test.dart index 9dc926c20f9..add60677a99 100644 --- a/packages/interactive_media_ads/test/android/ads_manager_test.dart +++ b/packages/interactive_media_ads/test/android/ads_manager_test.dart @@ -197,5 +197,15 @@ void main() { when(mockErrorEvent.error).thenReturn(mockError); onAdErrorCallback(MockAdErrorListener(), mockErrorEvent); }); + + test('adCuePoints', () { + final MockAdsManager mockAdsManager = MockAdsManager(); + + final List cuePoints = [1.0]; + when(mockAdsManager.adCuePoints).thenReturn(cuePoints); + final AndroidAdsManager adsManager = AndroidAdsManager(mockAdsManager); + + expect(adsManager.adCuePoints, [const Duration(seconds: 1)]); + }); }); } diff --git a/packages/interactive_media_ads/test/android/ads_manager_test.mocks.dart b/packages/interactive_media_ads/test/android/ads_manager_test.mocks.dart index 92af7ae343e..8fd6d262667 100644 --- a/packages/interactive_media_ads/test/android/ads_manager_test.mocks.dart +++ b/packages/interactive_media_ads/test/android/ads_manager_test.mocks.dart @@ -365,6 +365,15 @@ class MockAdEventListener extends _i1.Mock implements _i2.AdEventListener { /// /// See the documentation for Mockito's code generation for more information. class MockAdsManager extends _i1.Mock implements _i2.AdsManager { + @override + List get adCuePoints => + (super.noSuchMethod( + Invocation.getter(#adCuePoints), + returnValue: [], + returnValueForMissingStub: [], + ) + as List); + @override _i2.PigeonInstanceManager get pigeon_instanceManager => (super.noSuchMethod( @@ -407,17 +416,6 @@ class MockAdsManager extends _i1.Mock implements _i2.AdsManager { ) as _i4.Future); - @override - _i4.Future> getAdCuePoints() => - (super.noSuchMethod( - Invocation.method(#getAdCuePoints, []), - returnValue: _i4.Future>.value([]), - returnValueForMissingStub: _i4.Future>.value( - [], - ), - ) - as _i4.Future>); - @override _i4.Future resume() => (super.noSuchMethod( diff --git a/packages/interactive_media_ads/test/ios/ads_loader_test.mocks.dart b/packages/interactive_media_ads/test/ios/ads_loader_test.mocks.dart index a7d336d0a86..0788ef05487 100644 --- a/packages/interactive_media_ads/test/ios/ads_loader_test.mocks.dart +++ b/packages/interactive_media_ads/test/ios/ads_loader_test.mocks.dart @@ -321,6 +321,15 @@ class MockIMAAdsLoaderDelegate extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockIMAAdsManager extends _i1.Mock implements _i2.IMAAdsManager { + @override + List get adCuePoints => + (super.noSuchMethod( + Invocation.getter(#adCuePoints), + returnValue: [], + returnValueForMissingStub: [], + ) + as List); + @override _i2.PigeonInstanceManager get pigeon_instanceManager => (super.noSuchMethod( diff --git a/packages/interactive_media_ads/test/ios/ads_manager_delegate_tests.dart b/packages/interactive_media_ads/test/ios/ads_manager_delegate_tests.dart index d2df945123e..f1b78ad22ff 100644 --- a/packages/interactive_media_ads/test/ios/ads_manager_delegate_tests.dart +++ b/packages/interactive_media_ads/test/ios/ads_manager_delegate_tests.dart @@ -72,6 +72,7 @@ void main() { delegate, ima.IMAAdsManager.pigeon_detached( pigeon_instanceManager: instanceManager, + adCuePoints: const [], ), ima.IMAAdEvent.pigeon_detached( type: ima.AdEventType.allAdsCompleted, @@ -138,6 +139,7 @@ void main() { delegate, ima.IMAAdsManager.pigeon_detached( pigeon_instanceManager: instanceManager, + adCuePoints: const [], ), ); }); @@ -198,6 +200,7 @@ void main() { delegate, ima.IMAAdsManager.pigeon_detached( pigeon_instanceManager: instanceManager, + adCuePoints: const [], ), ); }); @@ -264,6 +267,7 @@ void main() { delegate, ima.IMAAdsManager.pigeon_detached( pigeon_instanceManager: instanceManager, + adCuePoints: const [], ), ima.IMAAdError.pigeon_detached( type: ima.AdErrorType.loadingFailed, diff --git a/packages/interactive_media_ads/test/ios/ads_manager_test.dart b/packages/interactive_media_ads/test/ios/ads_manager_test.dart index 1d2c9f83759..ef271bc909a 100644 --- a/packages/interactive_media_ads/test/ios/ads_manager_test.dart +++ b/packages/interactive_media_ads/test/ios/ads_manager_test.dart @@ -150,5 +150,15 @@ void main() { verify(mockAdsManager.setDelegate(delegate)); }); + + test('adCuePoints', () { + final MockIMAAdsManager mockAdsManager = MockIMAAdsManager(); + + final List cuePoints = [1.0]; + when(mockAdsManager.adCuePoints).thenReturn(cuePoints); + final IOSAdsManager adsManager = IOSAdsManager(mockAdsManager); + + expect(adsManager.adCuePoints, [const Duration(seconds: 1)]); + }); }); } diff --git a/packages/interactive_media_ads/test/ios/ads_manager_test.mocks.dart b/packages/interactive_media_ads/test/ios/ads_manager_test.mocks.dart index 6d1b4b9a03e..6e10afb706a 100644 --- a/packages/interactive_media_ads/test/ios/ads_manager_test.mocks.dart +++ b/packages/interactive_media_ads/test/ios/ads_manager_test.mocks.dart @@ -44,6 +44,15 @@ class _FakeIMAAdsRenderingSettings_2 extends _i1.SmartFake /// /// See the documentation for Mockito's code generation for more information. class MockIMAAdsManager extends _i1.Mock implements _i2.IMAAdsManager { + @override + List get adCuePoints => + (super.noSuchMethod( + Invocation.getter(#adCuePoints), + returnValue: [], + returnValueForMissingStub: [], + ) + as List); + @override _i2.PigeonInstanceManager get pigeon_instanceManager => (super.noSuchMethod( diff --git a/packages/interactive_media_ads/test/test_stubs.dart b/packages/interactive_media_ads/test/test_stubs.dart index 9ec7eecdd6b..57f190595cc 100644 --- a/packages/interactive_media_ads/test/test_stubs.dart +++ b/packages/interactive_media_ads/test/test_stubs.dart @@ -147,6 +147,7 @@ class TestAdsManager extends PlatformAdsManager { this.onPause, this.onResume, this.onSkip, + super.adCuePoints = const [], }); Future Function({PlatformAdsRenderingSettings? settings})? onInit; From 9cddbfa1af09c9cb43f9e41604cb6f364009442c Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Wed, 3 Sep 2025 14:25:41 -0400 Subject: [PATCH 3/3] support for adcuepoints --- packages/interactive_media_ads/CHANGELOG.md | 5 +++++ .../packages/interactive_media_ads/AdsRequestProxyApi.kt | 2 +- .../interactive_media_ads/AdsRequestProxyAPIDelegate.swift | 2 +- packages/interactive_media_ads/pubspec.yaml | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/interactive_media_ads/CHANGELOG.md b/packages/interactive_media_ads/CHANGELOG.md index 5c84cf244ec..456b0b506d4 100644 --- a/packages/interactive_media_ads/CHANGELOG.md +++ b/packages/interactive_media_ads/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.2.7 + +* Adds support to retrieve content time offsets at which ad breaks are scheduled. See + `AdsManager.adCuePoints` + ## 0.2.6+7 * Updates Android `PlatformAdDisplayContainer` implementation to support preloading ads. diff --git a/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/AdsRequestProxyApi.kt b/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/AdsRequestProxyApi.kt index 5a239da78ba..7aaee7c7c02 100644 --- a/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/AdsRequestProxyApi.kt +++ b/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/AdsRequestProxyApi.kt @@ -21,7 +21,7 @@ class AdsRequestProxyApi(override val pigeonRegistrar: ProxyApiRegistrar) : * * This must match the version in pubspec.yaml. */ - const val pluginVersion = "0.2.6+7" + const val pluginVersion = "0.2.7" } override fun setAdTagUrl(pigeon_instance: AdsRequest, adTagUrl: String) { diff --git a/packages/interactive_media_ads/ios/interactive_media_ads/Sources/interactive_media_ads/AdsRequestProxyAPIDelegate.swift b/packages/interactive_media_ads/ios/interactive_media_ads/Sources/interactive_media_ads/AdsRequestProxyAPIDelegate.swift index 802c3851508..3e8180f3263 100644 --- a/packages/interactive_media_ads/ios/interactive_media_ads/Sources/interactive_media_ads/AdsRequestProxyAPIDelegate.swift +++ b/packages/interactive_media_ads/ios/interactive_media_ads/Sources/interactive_media_ads/AdsRequestProxyAPIDelegate.swift @@ -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.6+7" + static let pluginVersion = "0.2.7" func pigeonDefaultConstructor( pigeonApi: PigeonApiIMAAdsRequest, adTagUrl: String, adDisplayContainer: IMAAdDisplayContainer, diff --git a/packages/interactive_media_ads/pubspec.yaml b/packages/interactive_media_ads/pubspec.yaml index 17bb2c2c94f..385101a9c01 100644 --- a/packages/interactive_media_ads/pubspec.yaml +++ b/packages/interactive_media_ads/pubspec.yaml @@ -2,7 +2,7 @@ name: interactive_media_ads description: A Flutter plugin for using the Interactive Media Ads SDKs on Android and iOS. repository: https://github.com/flutter/packages/tree/main/packages/interactive_media_ads issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+interactive_media_ads%22 -version: 0.2.6+7 # This must match the version in +version: 0.2.7 # This must match the version in # `android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/AdsRequestProxyApi.kt` and # `ios/interactive_media_ads/Sources/interactive_media_ads/AdsRequestProxyAPIDelegate.swift`