Skip to content

Commit 870c12c

Browse files
committed
Initial AdLoaderAd type
Add the `AdLoaderAd` component, without the specific configuration items for each of the handled ad types. The component implementation is known as: * `AdLoaderAd` (Flutter) * `FlutterAdLoaderAd` (Android) * `FLTAdLoaderAd` (iOS)
1 parent 03d6552 commit 870c12c

15 files changed

Lines changed: 830 additions & 0 deletions

File tree

packages/google_mobile_ads/android/src/main/java/io/flutter/plugins/googlemobileads/FlutterAdListener.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,14 @@ public void onAdLoaded() {
9494
}
9595
}
9696

97+
/** Listener for adloader ads. */
98+
class FlutterAdLoaderAdListener extends FlutterAdListener {
99+
100+
FlutterAdLoaderAdListener(int adId, AdInstanceManager manager) {
101+
super(adId, manager);
102+
}
103+
}
104+
97105
/** {@link OnNativeAdLoadedListener} for native ads. */
98106
class FlutterNativeAdLoadedListener implements OnNativeAdLoadedListener {
99107

packages/google_mobile_ads/android/src/main/java/io/flutter/plugins/googlemobileads/FlutterAdLoader.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,4 +139,21 @@ public void loadAdManagerNativeAd(
139139
.build()
140140
.loadAd(adManagerAdRequest);
141141
}
142+
143+
/** Load an ad loader ad. */
144+
public void loadAdLoaderAd(
145+
@NonNull String adUnitId, @NonNull AdListener adListener, @NonNull AdRequest request) {
146+
new AdLoader.Builder(context, adUnitId).withAdListener(adListener).build().loadAd(request);
147+
}
148+
149+
/** Load an ad manager ad loader ad. */
150+
public void loadAdManagerAdLoaderAd(
151+
@NonNull String adUnitId,
152+
@NonNull AdListener adListener,
153+
@NonNull AdManagerAdRequest adManagerAdRequest) {
154+
new AdLoader.Builder(context, adUnitId)
155+
.withAdListener(adListener)
156+
.build()
157+
.loadAd(adManagerAdRequest);
158+
}
142159
}
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package io.flutter.plugins.googlemobileads;
16+
17+
import android.util.Log;
18+
import androidx.annotation.NonNull;
19+
import androidx.annotation.Nullable;
20+
import com.google.android.gms.ads.AdListener;
21+
22+
/**
23+
* A central wrapper for {@link AdManagerAdView}, {@link NativeCustomFormatAd} and {@link NativeAd}
24+
* instances served for a single {@link AdRequest} or {@link AdManagerAdRequest}
25+
*/
26+
class FlutterAdLoaderAd extends FlutterAd {
27+
private static final String TAG = "FlutterAdLoaderAd";
28+
29+
@NonNull private final AdInstanceManager manager;
30+
@NonNull private final String adUnitId;
31+
@NonNull private final FlutterAdLoader adLoader;
32+
@Nullable private FlutterAdRequest request;
33+
@Nullable private FlutterAdManagerAdRequest adManagerRequest;
34+
35+
static class Builder {
36+
@Nullable private AdInstanceManager manager;
37+
@Nullable private String adUnitId;
38+
@Nullable private FlutterAdRequest request;
39+
@Nullable private FlutterAdManagerAdRequest adManagerRequest;
40+
@Nullable private Integer id;
41+
@Nullable private FlutterAdLoader adLoader;
42+
43+
public Builder setId(int id) {
44+
this.id = id;
45+
return this;
46+
}
47+
48+
public Builder setManager(@NonNull AdInstanceManager manager) {
49+
this.manager = manager;
50+
return this;
51+
}
52+
53+
public Builder setAdUnitId(@NonNull String adUnitId) {
54+
this.adUnitId = adUnitId;
55+
return this;
56+
}
57+
58+
public Builder setRequest(@NonNull FlutterAdRequest request) {
59+
this.request = request;
60+
return this;
61+
}
62+
63+
public Builder setAdManagerRequest(@NonNull FlutterAdManagerAdRequest adManagerRequest) {
64+
this.adManagerRequest = adManagerRequest;
65+
return this;
66+
}
67+
68+
public Builder setFlutterAdLoader(@NonNull FlutterAdLoader adLoader) {
69+
this.adLoader = adLoader;
70+
return this;
71+
}
72+
73+
FlutterAdLoaderAd build() {
74+
if (manager == null) {
75+
throw new IllegalStateException("manager must be provided");
76+
}
77+
78+
if (adUnitId == null) {
79+
throw new IllegalStateException("adUnitId must be provided");
80+
}
81+
82+
if (request == null && adManagerRequest == null) {
83+
throw new IllegalStateException("Either request or adManagerRequest must be provided");
84+
}
85+
86+
final FlutterAdLoaderAd adLoaderAd;
87+
88+
if (request == null) {
89+
adLoaderAd = new FlutterAdLoaderAd(id, manager, adUnitId, adManagerRequest, adLoader);
90+
} else {
91+
adLoaderAd = new FlutterAdLoaderAd(id, manager, adUnitId, request, adLoader);
92+
}
93+
return adLoaderAd;
94+
}
95+
}
96+
97+
protected FlutterAdLoaderAd(
98+
int adId,
99+
@NonNull AdInstanceManager manager,
100+
@NonNull String adUnitId,
101+
@NonNull FlutterAdRequest request,
102+
@NonNull FlutterAdLoader adLoader) {
103+
super(adId);
104+
this.manager = manager;
105+
this.adUnitId = adUnitId;
106+
this.request = request;
107+
this.adLoader = adLoader;
108+
}
109+
110+
protected FlutterAdLoaderAd(
111+
int adId,
112+
@NonNull AdInstanceManager manager,
113+
@NonNull String adUnitId,
114+
@NonNull FlutterAdManagerAdRequest adManagerRequest,
115+
@NonNull FlutterAdLoader adLoader) {
116+
super(adId);
117+
this.manager = manager;
118+
this.adUnitId = adUnitId;
119+
this.adManagerRequest = adManagerRequest;
120+
this.adLoader = adLoader;
121+
}
122+
123+
@Override
124+
void load() {
125+
final AdListener adListener = new FlutterAdLoaderAdListener(adId, manager);
126+
// Note we delegate loading the ad to FlutterAdLoader mainly for testing purposes.
127+
// As of 20.0.0 of GMA, mockito is unable to mock AdLoader.
128+
if (request != null) {
129+
adLoader.loadAdLoaderAd(adUnitId, adListener, request.asAdRequest(adUnitId));
130+
return;
131+
}
132+
133+
if (adManagerRequest != null) {
134+
adLoader.loadAdManagerAdLoaderAd(
135+
adUnitId, adListener, adManagerRequest.asAdManagerAdRequest(adUnitId));
136+
return;
137+
}
138+
139+
Log.e(TAG, "A null or invalid ad request was provided.");
140+
}
141+
142+
@Override
143+
void dispose() {}
144+
}

packages/google_mobile_ads/android/src/main/java/io/flutter/plugins/googlemobileads/GoogleMobileAdsPlugin.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,20 @@ public void onAdInspectorClosed(@Nullable AdInspectorError adInspectorError) {
419419
nativeAd.load();
420420
result.success(null);
421421
break;
422+
case "loadAdLoaderAd":
423+
final FlutterAdLoaderAd adLoaderAd =
424+
new FlutterAdLoaderAd.Builder()
425+
.setManager(instanceManager)
426+
.setAdUnitId(call.<String>argument("adUnitId"))
427+
.setRequest(call.<FlutterAdRequest>argument("request"))
428+
.setAdManagerRequest(call.<FlutterAdManagerAdRequest>argument("adManagerRequest"))
429+
.setId(call.<Integer>argument("adId"))
430+
.setFlutterAdLoader(new FlutterAdLoader(context))
431+
.build();
432+
instanceManager.trackAd(adLoaderAd, call.<Integer>argument("adId"));
433+
adLoaderAd.load();
434+
result.success(null);
435+
break;
422436
case "loadInterstitialAd":
423437
final FlutterInterstitialAd interstitial =
424438
new FlutterInterstitialAd(
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package io.flutter.plugins.googlemobileads;
16+
17+
import static org.mockito.ArgumentMatchers.any;
18+
import static org.mockito.ArgumentMatchers.anyString;
19+
import static org.mockito.ArgumentMatchers.eq;
20+
import static org.mockito.Mockito.doAnswer;
21+
import static org.mockito.Mockito.mock;
22+
import static org.mockito.Mockito.spy;
23+
import static org.mockito.Mockito.verify;
24+
import static org.mockito.Mockito.when;
25+
26+
import android.app.Activity;
27+
import com.google.android.gms.ads.AdListener;
28+
import com.google.android.gms.ads.AdRequest;
29+
import com.google.android.gms.ads.LoadAdError;
30+
import com.google.android.gms.ads.admanager.AdManagerAdRequest;
31+
import io.flutter.plugin.common.MethodChannel;
32+
import io.flutter.plugins.googlemobileads.FlutterAd.FlutterLoadAdError;
33+
import org.junit.Before;
34+
import org.junit.Test;
35+
import org.junit.runner.RunWith;
36+
import org.mockito.invocation.InvocationOnMock;
37+
import org.mockito.stubbing.Answer;
38+
import org.robolectric.RobolectricTestRunner;
39+
40+
/** Tests for {@link FlutterAdLoaderAd} */
41+
@RunWith(RobolectricTestRunner.class)
42+
public class FlutterAdLoaderAdTest {
43+
44+
private AdInstanceManager testManager;
45+
private final FlutterAdRequest request = new FlutterAdRequest.Builder().build();
46+
47+
@Before
48+
public void setup() {
49+
testManager = spy(new AdInstanceManager(mock(MethodChannel.class)));
50+
when(testManager.getActivity()).thenReturn(mock(Activity.class));
51+
}
52+
53+
@Test
54+
public void loadAdLoaderAdWithAdManagerAdRequest() {
55+
final FlutterAdManagerAdRequest mockFlutterRequest = mock(FlutterAdManagerAdRequest.class);
56+
final AdManagerAdRequest mockRequest = mock(AdManagerAdRequest.class);
57+
when(mockFlutterRequest.asAdManagerAdRequest(anyString())).thenReturn(mockRequest);
58+
FlutterAdLoader mockLoader = mock(FlutterAdLoader.class);
59+
final FlutterAdLoaderAd adLoaderAd =
60+
new FlutterAdLoaderAd(1, testManager, "testId", mockFlutterRequest, mockLoader);
61+
62+
final LoadAdError mockLoadAdError = mock(LoadAdError.class);
63+
when(mockLoadAdError.getCode()).thenReturn(1);
64+
when(mockLoadAdError.getDomain()).thenReturn("2");
65+
when(mockLoadAdError.getMessage()).thenReturn("3");
66+
67+
doAnswer(
68+
new Answer() {
69+
@Override
70+
public Object answer(InvocationOnMock invocation) {
71+
AdListener listener = invocation.getArgument(1);
72+
listener.onAdClicked();
73+
listener.onAdClosed();
74+
listener.onAdFailedToLoad(mockLoadAdError);
75+
listener.onAdImpression();
76+
listener.onAdOpened();
77+
return null;
78+
}
79+
})
80+
.when(mockLoader)
81+
.loadAdManagerAdLoaderAd(eq("testId"), any(AdListener.class), eq(mockRequest));
82+
83+
adLoaderAd.load();
84+
85+
verify(mockLoader)
86+
.loadAdManagerAdLoaderAd(eq("testId"), any(AdListener.class), eq(mockRequest));
87+
88+
verify(testManager).onAdClicked(eq(1));
89+
verify(testManager).onAdClosed(eq(1));
90+
FlutterLoadAdError expectedError = new FlutterLoadAdError(mockLoadAdError);
91+
verify(testManager).onAdFailedToLoad(eq(1), eq(expectedError));
92+
verify(testManager).onAdImpression(eq(1));
93+
verify(testManager).onAdOpened(eq(1));
94+
}
95+
96+
@Test
97+
public void loadAdLoaderAdWithAdRequest() {
98+
final FlutterAdRequest mockFlutterRequest = mock(FlutterAdRequest.class);
99+
final AdRequest mockRequest = mock(AdRequest.class);
100+
when(mockFlutterRequest.asAdRequest(anyString())).thenReturn(mockRequest);
101+
FlutterAdLoader mockLoader = mock(FlutterAdLoader.class);
102+
final FlutterAdLoaderAd adLoaderAd =
103+
new FlutterAdLoaderAd(1, testManager, "testId", mockFlutterRequest, mockLoader);
104+
105+
final LoadAdError mockLoadAdError = mock(LoadAdError.class);
106+
when(mockLoadAdError.getCode()).thenReturn(1);
107+
when(mockLoadAdError.getDomain()).thenReturn("2");
108+
when(mockLoadAdError.getMessage()).thenReturn("3");
109+
110+
doAnswer(
111+
new Answer() {
112+
@Override
113+
public Object answer(InvocationOnMock invocation) {
114+
AdListener listener = invocation.getArgument(1);
115+
listener.onAdClicked();
116+
listener.onAdClosed();
117+
listener.onAdFailedToLoad(mockLoadAdError);
118+
listener.onAdImpression();
119+
listener.onAdOpened();
120+
return null;
121+
}
122+
})
123+
.when(mockLoader)
124+
.loadAdLoaderAd(eq("testId"), any(AdListener.class), eq(mockRequest));
125+
126+
adLoaderAd.load();
127+
128+
verify(mockLoader).loadAdLoaderAd(eq("testId"), any(AdListener.class), eq(mockRequest));
129+
130+
verify(testManager).onAdClicked(eq(1));
131+
verify(testManager).onAdClosed(eq(1));
132+
FlutterLoadAdError expectedError = new FlutterLoadAdError(mockLoadAdError);
133+
verify(testManager).onAdFailedToLoad(eq(1), eq(expectedError));
134+
verify(testManager).onAdImpression(eq(1));
135+
verify(testManager).onAdOpened(eq(1));
136+
}
137+
138+
@Test(expected = IllegalStateException.class)
139+
public void adLoaderAdBuilderNullManager() {
140+
new FlutterAdLoaderAd.Builder()
141+
.setManager(null)
142+
.setAdUnitId("testId")
143+
.setRequest(request)
144+
.build();
145+
}
146+
147+
@Test(expected = IllegalStateException.class)
148+
public void adLoaderAdBuilderNullAdUnitId() {
149+
new FlutterAdLoaderAd.Builder()
150+
.setManager(testManager)
151+
.setAdUnitId(null)
152+
.setRequest(request)
153+
.build();
154+
}
155+
156+
@Test(expected = IllegalStateException.class)
157+
public void adLoaderAdBuilderNullRequest() {
158+
new FlutterAdLoaderAd.Builder()
159+
.setManager(testManager)
160+
.setAdUnitId("testId")
161+
.setRequest(null)
162+
.build();
163+
}
164+
}

packages/google_mobile_ads/ios/Classes/FLTAdInstanceManager_Internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#import <GoogleMobileAds/GoogleMobileAds.h>
1919

2020
@protocol FLTAd;
21+
@class FLTAdLoaderAd;
2122
@class FLTBannerAd;
2223
@class FLTNativeAd;
2324
@class FLTRewardedAd;

packages/google_mobile_ads/ios/Classes/FLTAd_Internal.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,16 @@
307307
- (GADAdLoader *_Nonnull)adLoader;
308308
@end
309309

310+
@interface FLTAdLoaderAd
311+
: FLTBaseAd <FLTAd, FlutterPlatformView, GADAdLoaderDelegate>
312+
@property(readonly, nonnull) GADAdLoader *adLoader;
313+
- (nonnull instancetype)initWithAdUnitId:(nonnull NSString *)adUnitId
314+
request:(nonnull FLTAdRequest *)request
315+
rootViewController:
316+
(nonnull UIViewController *)rootViewController
317+
adId:(nonnull NSNumber *)adId;
318+
@end
319+
310320
@interface FLTRewardItem : NSObject
311321
@property(readonly) NSNumber *_Nonnull amount;
312322
@property(readonly) NSString *_Nonnull type;

0 commit comments

Comments
 (0)