blob: 3a2d97fa93d1ea7bbee8e5123f365f9656745700 [file] [log] [blame]
// Copyright 2017, the Flutter project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#import "FLTMobileAd.h"
#import "FLTRequestFactory.h"
#import "FirebaseAdMobPlugin.h"
static NSMutableDictionary *allAds = nil;
static NSDictionary *statusToString = nil;
@implementation FLTMobileAd
NSNumber *_mobileAdId;
FlutterMethodChannel *_channel;
FLTMobileAdStatus _status;
double _anchorOffset;
int _anchorType;
+ (void)initialize {
if (allAds == nil) {
allAds = [[NSMutableDictionary alloc] init];
}
_anchorType = 0;
_anchorOffset = 0;
if (statusToString == nil) {
statusToString = @{
@(CREATED) : @"CREATED",
@(LOADING) : @"LOADING",
@(FAILED) : @"FAILED",
@(PENDING) : @"PENDING",
@(LOADED) : @"LOADED"
};
}
}
+ (void)configureWithAppId:(NSString *)appId {
[GADMobileAds configureWithApplicationID:appId];
}
+ (FLTMobileAd *)getAdForId:(NSNumber *)mobileAdId {
return allAds[mobileAdId];
}
+ (UIViewController *)rootViewController {
return [UIApplication sharedApplication].delegate.window.rootViewController;
}
- (instancetype)initWithId:(NSNumber *)mobileAdId channel:(FlutterMethodChannel *)channel {
self = [super init];
if (self) {
_mobileAdId = mobileAdId;
_channel = channel;
_status = CREATED;
_anchorOffset = 0;
_anchorType = 0;
allAds[mobileAdId] = self;
}
return self;
}
- (FLTMobileAdStatus)status {
return _status;
}
- (void)loadWithAdUnitId:(NSString *)adUnitId targetingInfo:(NSDictionary *)targetingInfo {
// Implemented by the Banner and Interstitial subclasses
}
- (void)showAtOffset:(double)anchorOffset fromAnchor:(int)anchorType {
_anchorType = anchorType;
_anchorOffset = anchorOffset;
if (_anchorType == 0) {
_anchorOffset = -_anchorOffset;
}
[self show];
}
- (void)show {
// Implemented by the Banner and Interstitial subclasses
}
- (void)dispose {
[allAds removeObjectForKey:_mobileAdId];
}
- (NSDictionary *)argumentsMap {
return @{@"id" : _mobileAdId};
}
- (NSString *)description {
NSString *statusString = (NSString *)statusToString[[NSNumber numberWithInt:_status]];
return [NSString
stringWithFormat:@"%@ %@ mobileAdId:%@", super.description, statusString, _mobileAdId];
}
@end
@implementation FLTBannerAd
GADBannerView *_banner;
GADAdSize _adSize;
+ (instancetype)withId:(NSNumber *)mobileAdId
adSize:(GADAdSize)adSize
channel:(FlutterMethodChannel *)channel {
FLTMobileAd *ad = [FLTMobileAd getAdForId:mobileAdId];
return ad != nil ? (FLTBannerAd *)ad
: [[FLTBannerAd alloc] initWithId:mobileAdId adSize:adSize channel:channel];
}
- (instancetype)initWithId:mobileAdId
adSize:(GADAdSize)adSize
channel:(FlutterMethodChannel *)channel {
self = [super initWithId:mobileAdId channel:channel];
if (self) {
_adSize = adSize;
return self;
}
return nil;
}
- (void)loadWithAdUnitId:(NSString *)adUnitId targetingInfo:(NSDictionary *)targetingInfo {
if (_status != CREATED) return;
_status = LOADING;
_banner = [[GADBannerView alloc] initWithAdSize:_adSize];
_banner.delegate = self;
_banner.adUnitID = adUnitId;
_banner.rootViewController = [FLTMobileAd rootViewController];
FLTRequestFactory *factory = [[FLTRequestFactory alloc] initWithTargetingInfo:targetingInfo];
[_banner loadRequest:[factory createRequest]];
}
- (void)show {
if (_status == LOADING) {
_status = PENDING;
return;
}
if (_status != LOADED) return;
_banner.translatesAutoresizingMaskIntoConstraints = NO;
UIView *screen = [FLTMobileAd rootViewController].view;
[screen addSubview:_banner];
#if defined(__IPHONE_11_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0)
if (@available(ios 11.0, *)) {
UILayoutGuide *guide = screen.safeAreaLayoutGuide;
[NSLayoutConstraint activateConstraints:@[
[_banner.centerXAnchor constraintEqualToAnchor:guide.centerXAnchor],
[_banner.bottomAnchor
constraintEqualToAnchor:_anchorType == 0 ? guide.bottomAnchor : guide.topAnchor
constant:_anchorOffset]
]];
} else {
[self placeBannerPreIos11];
}
#else
[self placeBannerPreIos11];
#endif
}
- (void)placeBannerPreIos11 {
UIView *screen = [FLTMobileAd rootViewController].view;
CGFloat x = screen.frame.size.width / 2 - _banner.frame.size.width / 2;
CGFloat y;
if (_anchorType == 0) {
y = screen.frame.size.height - _banner.frame.size.height + _anchorOffset;
} else {
y = _anchorOffset;
}
_banner.frame = (CGRect){{x, y}, _banner.frame.size};
[screen addSubview:_banner];
}
- (void)adViewDidReceiveAd:(GADBannerView *)adView {
bool statusWasPending = _status == PENDING;
_status = LOADED;
[_channel invokeMethod:@"onAdLoaded" arguments:[self argumentsMap]];
if (statusWasPending) [self show];
}
- (void)adView:(GADBannerView *)adView didFailToReceiveAdWithError:(GADRequestError *)error {
FLTLogWarning(@"adView:didFailToReceiveAdWithError: %@ (MobileAd %@)",
[error localizedDescription], self);
[_channel invokeMethod:@"onAdFailedToLoad" arguments:[self argumentsMap]];
}
- (void)adViewWillPresentScreen:(GADBannerView *)adView {
[_channel invokeMethod:@"onAdClicked" arguments:[self argumentsMap]];
}
- (void)adViewWillDismissScreen:(GADBannerView *)adView {
[_channel invokeMethod:@"onAdImpression" arguments:[self argumentsMap]];
}
- (void)adViewDidDismissScreen:(GADBannerView *)adView {
[_channel invokeMethod:@"onAdClosed" arguments:[self argumentsMap]];
}
- (void)adViewWillLeaveApplication:(GADBannerView *)adView {
[_channel invokeMethod:@"onAdLeftApplication" arguments:[self argumentsMap]];
}
- (void)dispose {
if (_banner.superview) [_banner removeFromSuperview];
_banner = nil;
[super dispose];
}
- (NSString *)description {
return [NSString stringWithFormat:@"%@ for: %@", super.description, _banner];
}
@end
@implementation FLTInterstitialAd
GADInterstitial *_interstitial;
+ (instancetype)withId:(NSNumber *)mobileAdId channel:(FlutterMethodChannel *)channel {
FLTMobileAd *ad = [FLTMobileAd getAdForId:mobileAdId];
return ad != nil ? (FLTInterstitialAd *)ad
: [[FLTInterstitialAd alloc] initWithId:mobileAdId channel:channel];
}
- (void)loadWithAdUnitId:(NSString *)adUnitId targetingInfo:(NSDictionary *)targetingInfo {
if (_status != CREATED) return;
_status = LOADING;
_interstitial = [[GADInterstitial alloc] initWithAdUnitID:adUnitId];
_interstitial.delegate = self;
FLTRequestFactory *factory = [[FLTRequestFactory alloc] initWithTargetingInfo:targetingInfo];
[_interstitial loadRequest:[factory createRequest]];
}
- (void)show {
if (_status == LOADING) {
_status = PENDING;
return;
}
if (_status != LOADED) return;
[_interstitial presentFromRootViewController:[FLTMobileAd rootViewController]];
}
- (void)interstitialDidReceiveAd:(GADInterstitial *)ad {
bool statusWasPending = _status == PENDING;
_status = LOADED;
[_channel invokeMethod:@"onAdLoaded" arguments:[self argumentsMap]];
if (statusWasPending) [self show];
}
- (void)interstitial:(GADInterstitial *)ad didFailToReceiveAdWithError:(GADRequestError *)error {
FLTLogWarning(@"interstitial:didFailToReceiveAdWithError: %@ (MobileAd %@)",
[error localizedDescription], self);
[_channel invokeMethod:@"onAdFailedToLoad" arguments:[self argumentsMap]];
}
- (void)interstitialWillPresentScreen:(GADInterstitial *)ad {
[_channel invokeMethod:@"onAdClicked" arguments:[self argumentsMap]];
}
- (void)interstitialWillDismissScreen:(GADInterstitial *)ad {
[_channel invokeMethod:@"onAdImpression" arguments:[self argumentsMap]];
}
- (void)interstitialDidDismissScreen:(GADInterstitial *)ad {
[_channel invokeMethod:@"onAdClosed" arguments:[self argumentsMap]];
}
- (void)interstitialWillLeaveApplication:(GADInterstitial *)ad {
[_channel invokeMethod:@"onAdLeftApplication" arguments:[self argumentsMap]];
}
- (void)dispose {
// It is not possible to hide/remove/destroy an AdMob interstitial Ad.
_interstitial = nil;
[super dispose];
}
- (NSString *)description {
return [NSString stringWithFormat:@"%@ for: %@", super.description, _interstitial];
}
@end