diff --git a/packages/google_mobile_ads/CHANGELOG.md b/packages/google_mobile_ads/CHANGELOG.md index 51a412b01..911158e41 100644 --- a/packages/google_mobile_ads/CHANGELOG.md +++ b/packages/google_mobile_ads/CHANGELOG.md @@ -11,6 +11,13 @@ You can see the [fluid_example.dart](https://github.com/googleads/googleads-mobile-flutter/blob/master/packages/google_mobile_ads/example/lib/fluid_example.dart) for a reference of how to load and display a fluid ad. * Android - https://developers.google.com/ad-manager/mobile-ads-sdk/android/native/styles#fluid_size * iOS - https://developers.google.com/ad-manager/mobile-ads-sdk/ios/native/native-styles#fluid_size +* Adds `AdSize. getCurrentOrientationAnchoredAdaptiveBannerAdSize()` to support getting an `AnchoredAdaptiveBannerAdSize` in the current orientation. + * Previously the user had to specify an orientation (portrait / landscape) to create an AnchoredAdaptiveBannerAdSize. It has been made optional with this version. SDK will determine the current orientation of the device and return an appropriate AdSize. + * More information on anchored adaptive banners can be found here: + * [Admob android](https://developers.google.com/admob/android/banner/anchored-adaptive) + * [Admob iOS](https://developers.google.com/admob/ios/banner/anchored-adaptive) + * [Ad manager android](https://developers.google.com/ad-manager/mobile-ads-sdk/android/banner/anchored-adaptive) + * [Ad manager iOS](https://developers.google.com/ad-manager/mobile-ads-sdk/ios/banner/anchored-adaptive) * Adds support for inline adaptive banner ads. * Inline adaptive banner ads are meant to be used in scrollable content. They are of variable height and can be as tall as the device screen. They differ from Fluid ads in that they only resize once when the ad is loaded. diff --git a/packages/google_mobile_ads/android/build.gradle b/packages/google_mobile_ads/android/build.gradle index 98e857e2c..c80465c09 100644 --- a/packages/google_mobile_ads/android/build.gradle +++ b/packages/google_mobile_ads/android/build.gradle @@ -36,6 +36,7 @@ android { testImplementation 'org.hamcrest:hamcrest:2.2' testImplementation 'org.mockito:mockito-inline:3.9.0' testImplementation 'org.robolectric:robolectric:4.4' + testImplementation 'androidx.test:core:1.3.0' } testOptions { unitTests { @@ -67,4 +68,4 @@ afterEvaluate { } } } -} +} \ No newline at end of file diff --git a/packages/google_mobile_ads/android/src/main/java/io/flutter/plugins/googlemobileads/FlutterAdSize.java b/packages/google_mobile_ads/android/src/main/java/io/flutter/plugins/googlemobileads/FlutterAdSize.java index 26c9753b5..d1faf110b 100644 --- a/packages/google_mobile_ads/android/src/main/java/io/flutter/plugins/googlemobileads/FlutterAdSize.java +++ b/packages/google_mobile_ads/android/src/main/java/io/flutter/plugins/googlemobileads/FlutterAdSize.java @@ -35,6 +35,10 @@ AdSize getLandscapeAnchoredAdaptiveBannerAdSize(Context context, int width) { return AdSize.getLandscapeAnchoredAdaptiveBannerAdSize(context, width); } + AdSize getCurrentOrientationAnchoredAdaptiveBannerAdSize(Context context, int width) { + return AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(context, width); + } + AdSize getCurrentOrientationInlineAdaptiveBannerAdSize(Context context, int width) { return AdSize.getCurrentOrientationInlineAdaptiveBannerAdSize(context, width); } @@ -59,16 +63,17 @@ static class AnchoredAdaptiveBannerAdSize extends FlutterAdSize { private static AdSize getAdSize( @NonNull Context context, @NonNull AdSizeFactory factory, - @NonNull String orientation, + @Nullable String orientation, int width) { final AdSize adSize; - if (orientation.equals("portrait")) { + if (orientation == null) { + adSize = factory.getCurrentOrientationAnchoredAdaptiveBannerAdSize(context, width); + } else if (orientation.equals("portrait")) { adSize = factory.getPortraitAnchoredAdaptiveBannerAdSize(context, width); } else if (orientation.equals("landscape")) { adSize = factory.getLandscapeAnchoredAdaptiveBannerAdSize(context, width); } else { - throw new IllegalArgumentException( - "Orientation should be 'portrait' or 'landscape': " + orientation); + throw new IllegalArgumentException("Unexpected value for orientation: " + orientation); } return adSize; } @@ -76,7 +81,7 @@ private static AdSize getAdSize( AnchoredAdaptiveBannerAdSize( @NonNull Context context, @NonNull AdSizeFactory factory, - @NonNull String orientation, + @Nullable String orientation, int width) { super(getAdSize(context, factory, orientation, width)); this.orientation = orientation; diff --git a/packages/google_mobile_ads/android/src/main/java/io/flutter/plugins/googlemobileads/GoogleMobileAdsPlugin.java b/packages/google_mobile_ads/android/src/main/java/io/flutter/plugins/googlemobileads/GoogleMobileAdsPlugin.java index 45e45888d..0be3edb9a 100644 --- a/packages/google_mobile_ads/android/src/main/java/io/flutter/plugins/googlemobileads/GoogleMobileAdsPlugin.java +++ b/packages/google_mobile_ads/android/src/main/java/io/flutter/plugins/googlemobileads/GoogleMobileAdsPlugin.java @@ -428,7 +428,7 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull final Result result) case "AdSize#getAnchoredAdaptiveBannerAdSize": final FlutterAdSize.AnchoredAdaptiveBannerAdSize size = new FlutterAdSize.AnchoredAdaptiveBannerAdSize( - activityBinding.getActivity(), + appContext, new FlutterAdSize.AdSizeFactory(), call.argument("orientation"), call.argument("width")); diff --git a/packages/google_mobile_ads/android/src/test/java/io/flutter/plugins/googlemobileads/AdMessageCodecTest.java b/packages/google_mobile_ads/android/src/test/java/io/flutter/plugins/googlemobileads/AdMessageCodecTest.java index ed2652108..9017c776e 100644 --- a/packages/google_mobile_ads/android/src/test/java/io/flutter/plugins/googlemobileads/AdMessageCodecTest.java +++ b/packages/google_mobile_ads/android/src/test/java/io/flutter/plugins/googlemobileads/AdMessageCodecTest.java @@ -146,9 +146,33 @@ public void encodeAnchoredAdaptiveBannerAdSize() { .when(mockAdSizeFactory) .getPortraitAnchoredAdaptiveBannerAdSize(any(Context.class), anyInt()); - final AnchoredAdaptiveBannerAdSize adaptiveAdSize = + doReturn(mockAdSize) + .when(mockAdSizeFactory) + .getLandscapeAnchoredAdaptiveBannerAdSize(any(Context.class), anyInt()); + + doReturn(mockAdSize) + .when(mockAdSizeFactory) + .getCurrentOrientationAnchoredAdaptiveBannerAdSize(any(Context.class), anyInt()); + + final AnchoredAdaptiveBannerAdSize portraitAnchoredAdaptiveAdSize = new AnchoredAdaptiveBannerAdSize(mock(Context.class), mockAdSizeFactory, "portrait", 23); - final ByteBuffer data = codec.encodeMessage(adaptiveAdSize); + final ByteBuffer portraitData = codec.encodeMessage(portraitAnchoredAdaptiveAdSize); + + final AnchoredAdaptiveBannerAdSize portraitResult = + (AnchoredAdaptiveBannerAdSize) codec.decodeMessage((ByteBuffer) portraitData.position(0)); + assertEquals(portraitResult.size, mockAdSize); + + final AnchoredAdaptiveBannerAdSize landscapeAnchoredAdaptiveAdSize = + new AnchoredAdaptiveBannerAdSize(mock(Context.class), mockAdSizeFactory, "landscape", 34); + final ByteBuffer landscapeData = codec.encodeMessage(landscapeAnchoredAdaptiveAdSize); + + final AnchoredAdaptiveBannerAdSize landscapeResult = + (AnchoredAdaptiveBannerAdSize) codec.decodeMessage((ByteBuffer) landscapeData.position(0)); + assertEquals(landscapeResult.size, mockAdSize); + + final AnchoredAdaptiveBannerAdSize anchoredAdaptiveAdSize = + new AnchoredAdaptiveBannerAdSize(mock(Context.class), mockAdSizeFactory, null, 45); + final ByteBuffer data = codec.encodeMessage(anchoredAdaptiveAdSize); final AnchoredAdaptiveBannerAdSize result = (AnchoredAdaptiveBannerAdSize) codec.decodeMessage((ByteBuffer) data.position(0)); diff --git a/packages/google_mobile_ads/android/src/test/java/io/flutter/plugins/googlemobileads/GoogleMobileAdsTest.java b/packages/google_mobile_ads/android/src/test/java/io/flutter/plugins/googlemobileads/GoogleMobileAdsTest.java index 6d7d98e4f..536de30ad 100644 --- a/packages/google_mobile_ads/android/src/test/java/io/flutter/plugins/googlemobileads/GoogleMobileAdsTest.java +++ b/packages/google_mobile_ads/android/src/test/java/io/flutter/plugins/googlemobileads/GoogleMobileAdsTest.java @@ -30,6 +30,7 @@ import android.app.Activity; import android.content.Context; import android.os.Bundle; +import androidx.test.core.app.ApplicationProvider; import com.google.android.gms.ads.AdError; import com.google.android.gms.ads.AdSize; import com.google.android.gms.ads.AdView; @@ -46,6 +47,7 @@ import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.MethodChannel.Result; import io.flutter.plugin.common.StandardMethodCodec; +import io.flutter.plugin.platform.PlatformViewRegistry; import io.flutter.plugins.googlemobileads.FlutterAd.FlutterResponseInfo; import java.nio.ByteBuffer; import java.util.ArrayList; @@ -600,6 +602,61 @@ public void testGetVersionString() { } @Test + public void testGetAnchoredAdaptiveBannerAdSize() { + // Setup mocks + AdInstanceManager testManagerSpy = spy(testManager); + FlutterMobileAdsWrapper mockMobileAds = mock(FlutterMobileAdsWrapper.class); + GoogleMobileAdsPlugin plugin = + new GoogleMobileAdsPlugin(mockFlutterPluginBinding, testManagerSpy, mockMobileAds); + + BinaryMessenger mockBinaryMessenger = mock(BinaryMessenger.class); + FlutterPluginBinding mockActivityPluginBinding = mock(FlutterPluginBinding.class); + PlatformViewRegistry mockPlatformViewRegistry = mock(PlatformViewRegistry.class); + + Context context = ApplicationProvider.getApplicationContext(); + + doReturn(context).when(mockActivityPluginBinding).getApplicationContext(); + doReturn(mockBinaryMessenger).when(mockActivityPluginBinding).getBinaryMessenger(); + doReturn(mockPlatformViewRegistry).when(mockActivityPluginBinding).getPlatformViewRegistry(); + + plugin.onAttachedToEngine(mockActivityPluginBinding); + + // Test for portrait Banner AdSize. + HashMap arguments = new HashMap<>(); + arguments.put("orientation", "portrait"); + arguments.put("width", 23); + + AdSize adSize = AdSize.getPortraitAnchoredAdaptiveBannerAdSize(context, 23); + MethodCall methodCall = new MethodCall("AdSize#getAnchoredAdaptiveBannerAdSize", arguments); + Result result = mock(Result.class); + plugin.onMethodCall(methodCall, result); + + verify(result).success(adSize.getHeight()); + + // Test for landscape Banner AdSize. + arguments = new HashMap<>(); + arguments.put("orientation", "landscape"); + arguments.put("width", 23); + + adSize = AdSize.getLandscapeAnchoredAdaptiveBannerAdSize(context, 23); + methodCall = new MethodCall("AdSize#getAnchoredAdaptiveBannerAdSize", arguments); + result = mock(Result.class); + plugin.onMethodCall(methodCall, result); + + verify(result).success(adSize.getHeight()); + + // Test for current orientation (inferred) Banner AdSize. + arguments = new HashMap<>(); + arguments.put("width", 23); + + adSize = AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(context, 23); + methodCall = new MethodCall("AdSize#getAnchoredAdaptiveBannerAdSize", arguments); + result = mock(Result.class); + plugin.onMethodCall(methodCall, result); + + verify(result).success(adSize.getHeight()); + } + public void testGetAdSize_bannerAd() { // Setup mocks AdInstanceManager testManagerSpy = spy(testManager); diff --git a/packages/google_mobile_ads/example/lib/main.dart b/packages/google_mobile_ads/example/lib/main.dart index cf39f2649..4aaef2c51 100644 --- a/packages/google_mobile_ads/example/lib/main.dart +++ b/packages/google_mobile_ads/example/lib/main.dart @@ -156,10 +156,8 @@ class _MyAppState extends State { Future _createAnchoredBanner(BuildContext context) async { final AnchoredAdaptiveBannerAdSize? size = - await AdSize.getAnchoredAdaptiveBannerAdSize( - Orientation.portrait, - MediaQuery.of(context).size.width.truncate(), - ); + await AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize( + MediaQuery.of(context).size.width.truncate()); if (size == null) { print('Unable to get height of anchored banner.'); diff --git a/packages/google_mobile_ads/ios/Classes/FLTAd_Internal.h b/packages/google_mobile_ads/ios/Classes/FLTAd_Internal.h index 6b2a3a590..9bf9614f8 100644 --- a/packages/google_mobile_ads/ios/Classes/FLTAd_Internal.h +++ b/packages/google_mobile_ads/ios/Classes/FLTAd_Internal.h @@ -34,6 +34,7 @@ @interface FLTAdSizeFactory : NSObject - (GADAdSize)portraitAnchoredAdaptiveBannerAdSizeWithWidth:(NSNumber *_Nonnull)width; - (GADAdSize)landscapeAnchoredAdaptiveBannerAdSizeWithWidth:(NSNumber *_Nonnull)width; +- (GADAdSize)currentOrientationAnchoredAdaptiveBannerAdSizeWithWidth:(NSNumber *_Nonnull)width; - (GADAdSize)currentOrientationInlineAdaptiveBannerSizeWithWidth:(NSNumber *_Nonnull)width; - (GADAdSize)portraitOrientationInlineAdaptiveBannerSizeWithWidth:(NSNumber *_Nonnull)width; - (GADAdSize)landscapeInlineAdaptiveBannerAdSizeWithWidth:(NSNumber *_Nonnull)width; diff --git a/packages/google_mobile_ads/ios/Classes/FLTAd_Internal.m b/packages/google_mobile_ads/ios/Classes/FLTAd_Internal.m index d32fb391a..0ae3ce2f9 100644 --- a/packages/google_mobile_ads/ios/Classes/FLTAd_Internal.m +++ b/packages/google_mobile_ads/ios/Classes/FLTAd_Internal.m @@ -42,6 +42,10 @@ - (GADAdSize)landscapeAnchoredAdaptiveBannerAdSizeWithWidth:(NSNumber *_Nonnull) return GADLandscapeAnchoredAdaptiveBannerAdSizeWithWidth(width.doubleValue); } +- (GADAdSize)currentOrientationAnchoredAdaptiveBannerAdSizeWithWidth:(NSNumber *_Nonnull)width { + return GADCurrentOrientationAnchoredAdaptiveBannerAdSizeWithWidth(width.doubleValue); +} + - (GADAdSize)currentOrientationInlineAdaptiveBannerSizeWithWidth:(NSNumber *_Nonnull)width { return GADCurrentOrientationInlineAdaptiveBannerAdSizeWithWidth(width.floatValue); } @@ -61,15 +65,17 @@ - (GADAdSize)inlineAdaptiveBannerAdSizeWithWidthAndMaxHeight:(NSNumber *_Nonnull @implementation FLTAnchoredAdaptiveBannerSize - (instancetype _Nonnull)initWithFactory:(FLTAdSizeFactory *_Nonnull)factory - orientation:(NSString *_Nonnull)orientation + orientation:(NSString *)orientation width:(NSNumber *_Nonnull)width { GADAdSize size; - if ([orientation isEqualToString:@"portrait"]) { + if ([FLTAdUtil isNull:orientation]) { + size = [factory currentOrientationAnchoredAdaptiveBannerAdSizeWithWidth:width]; + } else if ([orientation isEqualToString:@"portrait"]) { size = [factory portraitAnchoredAdaptiveBannerAdSizeWithWidth:width]; } else if ([orientation isEqualToString:@"landscape"]) { size = [factory landscapeAnchoredAdaptiveBannerAdSizeWithWidth:width]; } else { - NSLog(@"AdaptiveBanner orientation should be 'portrait' or 'landscape': %@", orientation); + NSLog(@"Unexpected value for orientation: %@", orientation); return nil; } diff --git a/packages/google_mobile_ads/ios/Tests/FLTGoogleMobileAdsPluginMethodCallsTest.m b/packages/google_mobile_ads/ios/Tests/FLTGoogleMobileAdsPluginMethodCallsTest.m index 28263124e..c6832d326 100644 --- a/packages/google_mobile_ads/ios/Tests/FLTGoogleMobileAdsPluginMethodCallsTest.m +++ b/packages/google_mobile_ads/ios/Tests/FLTGoogleMobileAdsPluginMethodCallsTest.m @@ -323,6 +323,63 @@ - (void)testGetVersionString { XCTAssertEqual(returnedResult, [GADMobileAds.sharedInstance sdkVersion]); } +- (void)testGetAnchoredAdaptiveBannerAdSize { + FlutterMethodCall *methodCall = + [FlutterMethodCall methodCallWithMethodName:@"AdSize#getAnchoredAdaptiveBannerAdSize" + arguments:@{ + @"orientation" : @"portrait", + @"width" : @23, + }]; + + __block bool resultInvoked = false; + __block id _Nullable returnedResult; + FlutterResult result = ^(id _Nullable result) { + resultInvoked = true; + returnedResult = result; + }; + + [_fltGoogleMobileAdsPlugin handleMethodCall:methodCall result:result]; + + XCTAssertTrue(resultInvoked); + XCTAssertEqual([returnedResult doubleValue], + GADPortraitAnchoredAdaptiveBannerAdSizeWithWidth(23).size.height); + + methodCall = [FlutterMethodCall methodCallWithMethodName:@"AdSize#getAnchoredAdaptiveBannerAdSize" + arguments:@{ + @"orientation" : @"landscape", + @"width" : @34, + }]; + + resultInvoked = false; + result = ^(id _Nullable result) { + resultInvoked = true; + returnedResult = result; + }; + + [_fltGoogleMobileAdsPlugin handleMethodCall:methodCall result:result]; + + XCTAssertTrue(resultInvoked); + XCTAssertEqual([returnedResult doubleValue], + GADLandscapeAnchoredAdaptiveBannerAdSizeWithWidth(34).size.height); + + methodCall = [FlutterMethodCall methodCallWithMethodName:@"AdSize#getAnchoredAdaptiveBannerAdSize" + arguments:@{ + @"width" : @45, + }]; + + resultInvoked = false; + result = ^(id _Nullable result) { + resultInvoked = true; + returnedResult = result; + }; + + [_fltGoogleMobileAdsPlugin handleMethodCall:methodCall result:result]; + + XCTAssertTrue(resultInvoked); + XCTAssertEqual([returnedResult doubleValue], + GADCurrentOrientationAnchoredAdaptiveBannerAdSizeWithWidth(45).size.height); +} + - (void)testGetAdSize_bannerAd { // Method calls to load a banner ad. FlutterMethodCall *loadAdMethodCall = diff --git a/packages/google_mobile_ads/ios/Tests/FLTGoogleMobileAdsReaderWriterTest.m b/packages/google_mobile_ads/ios/Tests/FLTGoogleMobileAdsReaderWriterTest.m index 596ba1f60..b003f6276 100644 --- a/packages/google_mobile_ads/ios/Tests/FLTGoogleMobileAdsReaderWriterTest.m +++ b/packages/google_mobile_ads/ios/Tests/FLTGoogleMobileAdsReaderWriterTest.m @@ -132,8 +132,8 @@ - (void)testEncodeDecodeInlineAdaptiveBannerAdSize_withMaxHeight { XCTAssertEqualObjects(decodedSize.orientation, inlineAdaptiveBannerSize.orientation); } -- (void)testEncodeDecodeAnchoredAdaptiveBannerAdSize { - GADAdSize testAdSize = GADAdSizeFromCGSize(CGSizeMake(0, 0)); +- (void)testEncodeDecodeAnchoredAdaptiveBannerAdSize_portraitOrientation { + GADAdSize testAdSize = GADAdSizeFromCGSize(CGSizeMake(23, 34)); FLTAdSizeFactory *factory = OCMClassMock([FLTAdSizeFactory class]); OCMStub([factory portraitAnchoredAdaptiveBannerAdSizeWithWidth:@(23)]).andReturn(testAdSize); @@ -148,6 +148,37 @@ - (void)testEncodeDecodeAnchoredAdaptiveBannerAdSize { XCTAssertEqual(decodedSize.size.size.width, testAdSize.size.width); } +- (void)testEncodeDecodeAnchoredAdaptiveBannerAdSize_landscapeOrientation { + GADAdSize testAdSize = GADAdSizeFromCGSize(CGSizeMake(34, 45)); + + FLTAdSizeFactory *factory = OCMClassMock([FLTAdSizeFactory class]); + OCMStub([factory landscapeAnchoredAdaptiveBannerAdSizeWithWidth:@(34)]).andReturn(testAdSize); + + FLTAnchoredAdaptiveBannerSize *size = + [[FLTAnchoredAdaptiveBannerSize alloc] initWithFactory:factory + orientation:@"landscape" + width:@(34)]; + NSData *encodedMessage = [_messageCodec encode:size]; + + FLTAnchoredAdaptiveBannerSize *decodedSize = [_messageCodec decode:encodedMessage]; + XCTAssertEqual(decodedSize.size.size.width, testAdSize.size.width); +} + +- (void)testEncodeDecodeAnchoredAdaptiveBannerAdSize_currentOrientation { + GADAdSize testAdSize = GADAdSizeFromCGSize(CGSizeMake(45, 56)); + + FLTAdSizeFactory *factory = OCMClassMock([FLTAdSizeFactory class]); + OCMStub([factory currentOrientationAnchoredAdaptiveBannerAdSizeWithWidth:@(45)]) + .andReturn(testAdSize); + + FLTAnchoredAdaptiveBannerSize *size = + [[FLTAnchoredAdaptiveBannerSize alloc] initWithFactory:factory orientation:NULL width:@(45)]; + NSData *encodedMessage = [_messageCodec encode:size]; + + FLTAnchoredAdaptiveBannerSize *decodedSize = [_messageCodec decode:encodedMessage]; + XCTAssertEqual(decodedSize.size.size.width, testAdSize.size.width); +} + - (void)testEncodeDecodeSmartBannerAdSize { FLTSmartBannerSize *size = [[FLTSmartBannerSize alloc] initWithOrientation:@"landscape"]; @@ -440,4 +471,8 @@ - (GADAdSize)portraitAnchoredAdaptiveBannerAdSizeWithWidth:(NSNumber *)width { - (GADAdSize)landscapeAnchoredAdaptiveBannerAdSizeWithWidth:(NSNumber *)width { return GADAdSizeFromCGSize(CGSizeMake(width.doubleValue, 0)); } + +- (GADAdSize)currentOrientationAnchoredAdaptiveBannerAdSizeWithWidth:(NSNumber *)width { + return GADAdSizeFromCGSize(CGSizeMake(width.doubleValue, 0)); +} @end diff --git a/packages/google_mobile_ads/lib/src/ad_containers.dart b/packages/google_mobile_ads/lib/src/ad_containers.dart index 24a0e0b22..094f7f188 100644 --- a/packages/google_mobile_ads/lib/src/ad_containers.dart +++ b/packages/google_mobile_ads/lib/src/ad_containers.dart @@ -295,7 +295,7 @@ class AnchoredAdaptiveBannerAdSize extends AdSize { }) : super(width: width, height: height); /// Orientation of the device used by the SDK to automatically find the correct height. - final Orientation orientation; + final Orientation? orientation; } /// Ad units that render screen-width banner ads on any screen size across different devices in either [Orientation]. @@ -400,7 +400,7 @@ class AdSize { ) async { final num? height = await instanceManager.channel.invokeMethod( 'AdSize#getAnchoredAdaptiveBannerAdSize', - { + { 'orientation': describeEnum(orientation), 'width': width, }, @@ -414,6 +414,29 @@ class AdSize { ); } + /// Returns an AdSize with the given width and a Google-optimized height to create a banner ad. + /// + /// The size returned will have an aspect ratio similar to AdSize, suitable for anchoring near the top or bottom of your app. + /// The height will never be larger than 15% of the device's current orientation height and never smaller than 50px. + /// This function always returns the same height for any width / device combination. + /// For more details, visit: https://developers.google.com/android/reference/com/google/android/gms/ads/AdSize#getCurrentOrientationAnchoredAdaptiveBannerAdSize(android.content.Context,%20int) + static Future + getCurrentOrientationAnchoredAdaptiveBannerAdSize(int width) async { + final num? height = await instanceManager.channel.invokeMethod( + 'AdSize#getAnchoredAdaptiveBannerAdSize', + { + 'width': width, + }, + ); + + if (height == null) return null; + return AnchoredAdaptiveBannerAdSize( + null, + width: width, + height: height.truncate(), + ); + } + /// Gets an AdSize with the given width and height that is always 0. /// /// This ad size allows Google servers to choose an optimal ad size with a diff --git a/packages/google_mobile_ads/lib/src/ad_instance_manager.dart b/packages/google_mobile_ads/lib/src/ad_instance_manager.dart index 8f3d66b7c..7ef88c708 100644 --- a/packages/google_mobile_ads/lib/src/ad_instance_manager.dart +++ b/packages/google_mobile_ads/lib/src/ad_instance_manager.dart @@ -828,14 +828,18 @@ class AdMessageCodec extends StandardMessageCodec { } case _valueAnchoredAdaptiveBannerAdSize: - final String orientationStr = + final String? orientationStr = readValueOfType(buffer.getUint8(), buffer); final num width = readValueOfType(buffer.getUint8(), buffer); - return AnchoredAdaptiveBannerAdSize( - Orientation.values.firstWhere( + Orientation? orientation; + if (orientationStr != null) { + orientation = Orientation.values.firstWhere( (Orientation orientation) => describeEnum(orientation) == orientationStr, - ), + ); + } + return AnchoredAdaptiveBannerAdSize( + orientation, width: width.truncate(), height: -1, // Unused value ); @@ -999,7 +1003,11 @@ class AdMessageCodec extends StandardMessageCodec { writeValue(buffer, value.orientationValue); } else if (value is AnchoredAdaptiveBannerAdSize) { buffer.putUint8(_valueAnchoredAdaptiveBannerAdSize); - writeValue(buffer, describeEnum(value.orientation)); + var orientationValue; + if (value.orientation != null) { + orientationValue = describeEnum(value.orientation as Orientation); + } + writeValue(buffer, orientationValue); writeValue(buffer, value.width); } else if (value is SmartBannerAdSize) { buffer.putUint8(_valueSmartBannerAdSize); diff --git a/packages/google_mobile_ads/test/ad_containers_test.dart b/packages/google_mobile_ads/test/ad_containers_test.dart index 30efa69ee..c38cceda5 100644 --- a/packages/google_mobile_ads/test/ad_containers_test.dart +++ b/packages/google_mobile_ads/test/ad_containers_test.dart @@ -1423,13 +1423,32 @@ void main() { }); test('encode/decode $AnchoredAdaptiveBannerAdSize', () async { - final ByteData byteData = codec.encodeMessage( - AnchoredAdaptiveBannerAdSize(Orientation.landscape, + final ByteData byteDataPortrait = codec.encodeMessage( + AnchoredAdaptiveBannerAdSize(Orientation.portrait, width: 23, height: 34))!; + final AnchoredAdaptiveBannerAdSize resultPortrait = + codec.decodeMessage(byteDataPortrait); + expect(resultPortrait.orientation, Orientation.portrait); + expect(resultPortrait.width, 23); + expect(resultPortrait.height, -1); + + final ByteData byteDataLandscape = codec.encodeMessage( + AnchoredAdaptiveBannerAdSize(Orientation.landscape, + width: 34, height: 23))!; + + final AnchoredAdaptiveBannerAdSize resultLandscape = + codec.decodeMessage(byteDataLandscape); + expect(resultLandscape.orientation, Orientation.landscape); + expect(resultLandscape.width, 34); + expect(resultLandscape.height, -1); + + final ByteData byteData = codec.encodeMessage( + AnchoredAdaptiveBannerAdSize(null, width: 45, height: 34))!; + final AnchoredAdaptiveBannerAdSize result = codec.decodeMessage(byteData); - expect(result.orientation, Orientation.landscape); - expect(result.width, 23); + expect(result.orientation, null); + expect(result.width, 45); expect(result.height, -1); }); diff --git a/packages/google_mobile_ads/test/mobile_ads_test.dart b/packages/google_mobile_ads/test/mobile_ads_test.dart index cb833b5b3..bef8e3369 100644 --- a/packages/google_mobile_ads/test/mobile_ads_test.dart +++ b/packages/google_mobile_ads/test/mobile_ads_test.dart @@ -16,6 +16,7 @@ import 'package:google_mobile_ads/google_mobile_ads.dart'; import 'package:google_mobile_ads/src/ad_instance_manager.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { @@ -54,6 +55,8 @@ void main() { return null; case 'MobileAds#getVersionString': return Future.value('Test-SDK-Version'); + case 'AdSize#getAnchoredAdaptiveBannerAdSize': + return null; default: assert(false); return null; @@ -279,5 +282,34 @@ void main() { isMethodCall('MobileAds#getVersionString', arguments: null) ]); }); + + test('$AdSize.getAnchoredAdaptiveBannerAdSize', () async { + await AdSize.getAnchoredAdaptiveBannerAdSize(Orientation.portrait, 23); + + expect(log, [ + isMethodCall('AdSize#getAnchoredAdaptiveBannerAdSize', + arguments: {'orientation': 'portrait', 'width': 23}) + ]); + + await AdSize.getAnchoredAdaptiveBannerAdSize(Orientation.landscape, 34); + + expect(log, [ + isMethodCall('AdSize#getAnchoredAdaptiveBannerAdSize', + arguments: {'orientation': 'portrait', 'width': 23}), + isMethodCall('AdSize#getAnchoredAdaptiveBannerAdSize', + arguments: {'orientation': 'landscape', 'width': 34}) + ]); + + await AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(45); + + expect(log, [ + isMethodCall('AdSize#getAnchoredAdaptiveBannerAdSize', + arguments: {'orientation': 'portrait', 'width': 23}), + isMethodCall('AdSize#getAnchoredAdaptiveBannerAdSize', + arguments: {'orientation': 'landscape', 'width': 34}), + isMethodCall('AdSize#getAnchoredAdaptiveBannerAdSize', + arguments: {'width': 45}) + ]); + }); }); }