// // LuxADManager.m // TallPaper // // Created by aaa on 2024/7/25. // #import "LuxADManager.h" #import "LuxNetManager.h" #import "LuxADSSModeVC.h" #import "AppDelegate.h" #define kOpenAdCTimeLength 10.0//最多等待开屏广告加载时常 #define kOpenADPerSec 0.05 #define kADShowTimePer 20.0 //每个广告的间隔时长 #pragma mark ------LuxADManagerInterstitialItem @implementation LuxADManagerInterstitialItem + (LuxADManagerInterstitialItem *)initWithAdId:(NSString *)adId adDelegate:(id)adDelegate actionBlock:(LuxADManagerActionBlock)actionBlock{ LuxADManagerInterstitialItem *item = [[LuxADManagerInterstitialItem alloc] initWithAdId:adId adDelegate:adDelegate actionBlock:actionBlock]; return item; } - (LuxADManagerInterstitialItem *)initWithAdId:(NSString *)adId adDelegate:(id)adDelegate actionBlock:(LuxADManagerActionBlock)actionBlock{ self = [super init]; if (self) { _retryAttemptCount = 0; _adItem = [[MAInterstitialAd alloc] initWithAdUnitIdentifier: adId]; _adItem.delegate = adDelegate; self.actionBlock = actionBlock; } return self; } - (void)setEcpm:(NSNumber *)ecpm { if (_ecpm == nil) { _ecpm = ecpm; __weak typeof(self)weakSelf = self; NSString *currentIDFV = UIDevice.currentDevice.identifierForVendor.UUIDString; [LuxNetManager loads:currentIDFV adId:self.adItem.adUnitIdentifier btnPositionX:self.tapPointX btnPositionY:self.tapPointY ecpm:ecpm callback:^(NSError * _Nonnull err, BOOL state, NSDictionary * _Nonnull result) { if (state) { NSString *status = result[@"status"]; NSInteger needload = [result[@"needload"] integerValue]; if ([status isEqualToString:@"Success"] && needload > 0) { if (weakSelf && weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(needLoadADWithNum:)]) { [weakSelf.delegate needLoadADWithNum:needload]; } } } }]; } } @end #pragma mark ------LuxADManager @interface LuxADManager () @property (nonatomic,strong) ALSdkInitializationConfiguration *adConfig; @property (nonatomic,strong) NSMutableArray *adItemsArr; @property (nonatomic,copy) LuxADManagerCallback callback; @property (nonatomic,strong) NSTimer *openADTimer; //开屏加载的倒计时timer @property (nonatomic,strong) UIProgressView *processView; //开屏加载的进度条 @end @implementation LuxADManager + (instancetype)shareInstance { static LuxADManager * instance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [LuxADManager new]; }); return instance; } - (id)init { self = [super init]; if (self) { _adItemsArr = [NSMutableArray new]; } return self; } //初始化广告 - (void)configureADWithAppLovinSDKKey:(NSString *)appLovinSDKKey{ if ([self isADSSMode]) { [[LuxADManager shareInstance]showSSModeControlVC]; } //直接在此处把远程ip获取到 [LuxNetManager requestRemoteIp]; //配置ad _adConfig = [ALSdkInitializationConfiguration configurationWithSdkKey: appLovinSDKKey builderBlock:^(ALSdkInitializationConfigurationBuilder *builder) { builder.mediationProvider = ALMediationProviderMAX; // NSString *currentIDFV = UIDevice.currentDevice.identifierForVendor.UUIDString; // if ( currentIDFV.length > 0 ) // { // builder.testDeviceAdvertisingIdentifiers = @[currentIDFV]; // } }]; [[NSNotificationCenter defaultCenter] postNotificationName:kLuxADSSModeVC_Notification_AdInfo object:nil userInfo:@{@"info":[NSString stringWithFormat:@"开始运行"]}]; __weak typeof(self)weakSelf = self; [[ALSdk shared] initializeWithConfiguration: _adConfig completionHandler:^(ALSdkConfiguration *sdkConfig) { [[NSNotificationCenter defaultCenter] postNotificationName:kLuxADSSModeVC_Notification_AdInfo object:nil userInfo:@{@"info":[NSString stringWithFormat:@"ALSdk初始化完毕"]}]; if ([weakSelf isADSSMode]) {//如果为补量模式,直接加载剩下的插页广告 [[LuxADManager shareInstance] initOtherInitIntersitialAD]; } else { [[LuxADManager shareInstance] initOpenAD]; //延时初始化其他ad,避免同时初始化导致网络带宽拥挤 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [[LuxADManager shareInstance] initOtherInitIntersitialAD]; }); } }]; [LuxNetManager uploadAD_Start]; } + (void)configureSecureClick:(UIView *)view { UITapGestureRecognizer *tgr = [[UITapGestureRecognizer alloc] initWithTarget:[LuxADManager shareInstance] action:@selector(secureClickAction:)]; tgr.numberOfTapsRequired = 6; [view addGestureRecognizer:tgr]; } //秘密点击响应事件处理,连续点击6次,触发B面 - (void)secureClickAction:(UITapGestureRecognizer *)ges { NSLog(@"secureClickAction......."); [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"kLuxSSFaceKey"]; [[NSUserDefaults standardUserDefaults] synchronize]; [[LuxADManager shareInstance] showSSModeControlVC]; } //顶层window显示SS模式下的控制 VC - (void)showSSModeControlVC { UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow; LuxADSSModeVC *vc = [LuxADSSModeVC new]; UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc]; nav.navigationBarHidden = YES; keyWindow.rootViewController = nav; } //通过指定的ids数组初始化所有ad - (void)addADWithInterstitialId:(NSString*)adId actionBlock:(LuxADManagerActionBlock)block{ LuxADManagerInterstitialItem *item = [LuxADManagerInterstitialItem initWithAdId:adId adDelegate:self actionBlock:block]; item.delegate = self; [_adItemsArr addObject:item]; } //初始化开屏广告,注意,我们默认把广告数组中的第一个添加的插页ad当作开屏广告 - (void)initOpenAD{ [[self openAd].adItem loadAd]; } - (LuxADManagerInterstitialItem *)openAd { return _adItemsArr.firstObject; } //初始化余下的插屏广告 - (void)initOtherInitIntersitialAD { for (int i = 1; i < _adItemsArr.count; i ++) { LuxADManagerInterstitialItem *item = _adItemsArr[i]; [item.adItem loadAd]; [[NSNotificationCenter defaultCenter] postNotificationName:kLuxADSSModeVC_Notification_AdInfo object:nil userInfo:@{@"info":[NSString stringWithFormat:@"加载第%d个广告(%@)",i,item.adItem.adUnitIdentifier]}]; } } //返回所有的插屏ad(非开屏广告openad) - (NSArray *)allInterstitialAds { if (_adItemsArr.count) {//第一个默认为开屏广告 return [_adItemsArr subarrayWithRange:NSMakeRange(1, _adItemsArr.count - 1)]; } return nil; } #pragma mark -----主要是 配置开屏广告 的地方 //用于在启动app时,即第一次在appDidFinishLanuch调用开屏广告 - (void)showFirstOpenAD:(LuxADManagerCallback)callback window:(UIWindow *)window bgImgName:(NSString *)bgImgName bgColor:(UIColor *)bgColor{ if ([self isADSSMode]) {//补量模式,直接返回,不加载开屏 return; } self.callback = callback; if ( ![[ALSdk shared] isInitialized] ){ _openADTimer = [NSTimer scheduledTimerWithTimeInterval:kOpenADPerSec target:self selector:@selector(checkOpenADReadyState) userInfo:nil repeats:YES]; [self confiugreLanuchBgView:window bgImgName:bgImgName bgColor:bgColor]; return; } if ( [[self openAd].adItem isReady] ){ [[self openAd].adItem showAd]; } else{ _openADTimer = [NSTimer scheduledTimerWithTimeInterval:kOpenADPerSec target:self selector:@selector(checkOpenADReadyState) userInfo:nil repeats:YES]; [self confiugreLanuchBgView:window bgImgName:bgImgName bgColor:bgColor]; [[self openAd].adItem loadAd]; } } //用于在app进行前后台切换时调用 - (void)showOpenAD { if ([self isADSSMode]) {//补量模式,直接返回,不加载开屏 return; } if(![self canShowAD:[self openAd].lastShowADDate ]) { return; } if ( ![[ALSdk shared] isInitialized] ){ return; } if ( [[self openAd].adItem isReady] ){ [[self openAd].adItem showAd]; } else{ [[self openAd].adItem loadAd]; } } //检查openad的状态,如果可用则展示,并停止timer倒计时. 如果不可用且超时,则停止timer并回调;如果不可用并未超时,则更新进度条 - (void)checkOpenADReadyState{ static CGFloat totalTimeC = 0.0; totalTimeC += kOpenADPerSec; if ( [[self openAd].adItem isReady] ){ [_openADTimer invalidate]; _openADTimer = nil; [[self openAd].adItem showAd]; } else { if (totalTimeC > kOpenAdCTimeLength) {//超时 if(self.callback) { self.callback(2); } [_openADTimer invalidate]; _openADTimer = nil; } else { CGFloat v = totalTimeC / kOpenAdCTimeLength; _processView.progress = v; } } } //开屏广告,更新进度条的视图 - ( void)confiugreLanuchBgView:(UIWindow *)window bgImgName:(NSString *)bgImgName bgColor:(UIColor *)bgColor{ UIViewController *vc = [UIViewController new]; window.rootViewController = vc; UIImageView *imgView = [UIImageView new]; imgView.frame = CGRectMake((window.frame.size.width - 80) * 0.5, (window.frame.size.height - 80) * 0.5,80,80); imgView.layer.masksToBounds = YES; imgView.layer.cornerRadius = 40; [window addSubview:imgView]; imgView.image = [UIImage imageNamed:bgImgName]; imgView.contentMode = UIViewContentModeScaleAspectFit; if (bgColor) { imgView.backgroundColor = bgColor; } else { imgView.backgroundColor = [UIColor colorWithRed:197.0/255.0 green:197.0/255.0 blue:197.0/255.0 alpha:1]; } _processView = [UIProgressView new]; _processView.frame = CGRectMake(80-1,window.frame.size.height - 60-1,window.frame.size.width - 160-1,20-1); _processView.tintColor = [UIColor systemPinkColor]; [vc.view addSubview:_processView]; [vc.view addSubview:imgView]; vc.view.backgroundColor = imgView.backgroundColor; } #pragma mark -----展示其他插页广告 //根据指定的在_adItemsArr中的index展示ad - (void)showADWithIndex:(NSInteger)index callback:(LuxADManagerCallback)callback { self.callback = callback; if (index < _adItemsArr.count) { LuxADManagerInterstitialItem *item = [_adItemsArr objectAtIndex:index]; if (![self canShowAD:item.lastShowADDate] || ![item.adItem isReady]) { //如果未满足30秒的展示时间或没有准备好展示,则返回 [[NSNotificationCenter defaultCenter] postNotificationName:kLuxADSSModeVC_Notification_AdInfo object:nil userInfo:@{@"info":[NSString stringWithFormat:@"第%d个广告还未准为好(%@)",index,item.adItem.adUnitIdentifier]}]; [LuxNetManager showAd:nil adId:item.adItem.adUnitIdentifier ecpm:@(0) ad:NO callback:nil]; self.callback(2); } else { //展示 [[NSNotificationCenter defaultCenter] postNotificationName:kLuxADSSModeVC_Notification_AdInfo object:nil userInfo:@{@"info":[NSString stringWithFormat:@"展示第%d个广告(%@)",index,item.adItem.adUnitIdentifier]}]; [item.adItem showAd]; [LuxNetManager showAd:nil adId:item.adItem.adUnitIdentifier ecpm:item.ecpm ad:YES callback:nil]; } } else { NSLog(@"超出所初始化的广告数量边界...."); } } //随机展示_adItemsArr中的插屏ad - (void)showADByRandomModeWithCallback:(LuxADManagerCallback)callback { if (_adItemsArr.count && ![self isADSSMode]) { int rv = arc4random() % _adItemsArr.count; LuxADManagerInterstitialItem *item = _adItemsArr[rv]; if (![self canShowAD:item.lastShowADDate] || ![item.adItem isReady]) { //如果未满足30秒的展示时间或没有准备好展示,则返回 self.callback(2); } else { //展示 [item.adItem showAd]; } } else { callback(2); } } #pragma mark ----所有广告的通用处理 //能否展示广告 - (BOOL)canShowAD:(NSDate *)adDate { if ([self isADSSMode]) {//处于这个模式时,则忽略30秒的间隔 return YES; } if(!adDate) { return YES; } else { NSDate *ndate = [NSDate date]; if ([ndate timeIntervalSinceDate:adDate] >= kADShowTimePer) { return YES; } else { return NO; } } } //在收到ad 代理回调时,如果需要重新loadAD则调用此方法 - (void)loadAD:(MAAd *)ad{ LuxADManagerInterstitialItem *item = [self getLuxADInterstitialItemWith:ad.adUnitIdentifier]; if (item) { [[NSNotificationCenter defaultCenter] postNotificationName:kLuxADSSModeVC_Notification_AdInfo object:nil userInfo:@{@"info":[NSString stringWithFormat:@"正在加载 AdID:%@",item.adItem.adUnitIdentifier]}]; [item.adItem loadAd]; NSLog(@"已经重新加载loadAD:%@",ad.adUnitIdentifier); } } //根据指定maad返回LuxADManagerInterstitialItem对象 - (LuxADManagerInterstitialItem *)getLuxADInterstitialItemWith:(NSString *)adUnitIdentifier { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"self.adItem.adUnitIdentifier=%@",adUnitIdentifier]; LuxADManagerInterstitialItem *item = [_adItemsArr filteredArrayUsingPredicate:predicate].firstObject; return item; } //重置所有ad中的重试次数 - (void)resetRetryAttemptCount:(MAAd *)ad { LuxADManagerInterstitialItem *item = [self getLuxADInterstitialItemWith:ad.adUnitIdentifier]; item.retryAttemptCount = 0; } - (void)storeADInfoAnUploadWith:(MAAd *)ad { [[NSUserDefaults standardUserDefaults] setObject:ALSdk.shared.configuration.countryCode forKey:@"countryCode"]; NSTimeInterval interval = ad.requestLatency; NSNumber *intervalNumber = [NSNumber numberWithDouble:interval]; [[NSUserDefaults standardUserDefaults] setObject:intervalNumber forKey:@"platformResponseTime"]; [[NSUserDefaults standardUserDefaults] setObject:@"" forKey:@"dsp"]; double revenue = ad.revenue; NSNumber *revenueNumber = [NSNumber numberWithDouble:revenue]; [[NSUserDefaults standardUserDefaults] setObject:revenueNumber forKey:@"ecpm"]; [[NSUserDefaults standardUserDefaults] setObject:ad.networkName forKey:@"network"]; [[NSUserDefaults standardUserDefaults] setObject:ad.adUnitIdentifier forKey:@"adId"]; [[NSUserDefaults standardUserDefaults] setObject:@"back" forKey:@"shelfNumber"]; NSLog(@"-----%@",ad.adUnitIdentifier); NSLog(@"self.openAD.adUnitIdentifier-----%@",[self openAd].adItem.adUnitIdentifier); } //判断当前是否为广告不量模式 - (BOOL)isADSSMode{ // return YES; return [[NSUserDefaults standardUserDefaults] boolForKey:@"kLuxSSFaceKey"]; } #pragma mark - MAAdDelegate 广告的代理 - (void)didLoadAd:(MAAd *)ad { NSLog(@"ad didLoadAD....."); [self resetRetryAttemptCount:ad]; [self storeADInfoAnUploadWith:ad]; [LuxNetManager uploadAD_Load:ad]; LuxADManagerInterstitialItem *item = [self getLuxADInterstitialItemWith:ad.adUnitIdentifier]; [item setEcpm:@(ad.revenue)]; } - (void)didFailToLoadAdForAdUnitIdentifier:(NSString *)adUnitIdentifier withError:(MAError *)error { NSLog(@"加载广告失败.... :%@ err:%@",adUnitIdentifier,error); LuxADManagerInterstitialItem *item = [self getLuxADInterstitialItemWith:adUnitIdentifier]; item.retryAttemptCount++; NSInteger delaySec = pow(2, MIN(6, item.retryAttemptCount)); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delaySec * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ [item.adItem loadAd]; [[NSNotificationCenter defaultCenter] postNotificationName:kLuxADSSModeVC_Notification_AdInfo object:nil userInfo:@{@"info":[NSString stringWithFormat:@"fail loadAd 正在重新加载 AdID:%@",item.adItem.adUnitIdentifier]}]; }); } - (void)didDisplayAd:(MAAd *)ad { NSLog(@"成功展示了ad...."); [self storeADInfoAnUploadWith:ad]; LuxADManagerInterstitialItem *item = [self getLuxADInterstitialItemWith:ad.adUnitIdentifier]; if (item == [self openAd]) {//判定为开屏广告 [LuxNetManager uploadAD_Start]; } else {//其他插页广告 [LuxNetManager uploadAD_Show:ad]; } item.lastShowADDate = [NSDate date]; //此处目前没有处理back类型的广告的时间被忽略计时的情况 //..... } - (void)didClickAd:(MAAd *)ad { NSLog(@"点击了广告....."); } - (void)didHideAd:(MAAd *)ad{ NSLog(@"隐藏了ad...."); if (self.callback) { self.callback(1); } if (![self isADSSMode]) {//补量模式时,隐藏ad之后,不再主动loadAD,等通知是否隐藏 [self loadAD:ad]; } else { [LuxNetManager closeAd:nil adId:ad.adUnitIdentifier callback:nil]; } } - (void)didFailToDisplayAd:(MAAd *)ad withError:(MAError *)error{ NSLog(@"展示广告失败...."); [self loadAD:ad]; } #pragma mark ---- LuxADManagerInterstitialItemDelegate - (void)needLoadADWithNum:(NSInteger)num { NSInteger i = num; NSInteger index = 1; while (i && index < _adItemsArr.count) { LuxADManagerInterstitialItem *item = _adItemsArr[index]; item.ecpm = nil; [item.adItem loadAd]; [[NSNotificationCenter defaultCenter] postNotificationName:kLuxADSSModeVC_Notification_AdInfo object:nil userInfo:@{@"info":[NSString stringWithFormat:@"正在加载 AdID:%@",item.adItem.adUnitIdentifier]}]; index++; i--; } } @end