MAUnityAdManager.m 86 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051
  1. //
  2. // MAUnityAdManager.m
  3. // AppLovin MAX Unity Plugin
  4. //
  5. #import "MAUnityAdManager.h"
  6. #define KEY_WINDOW [UIApplication sharedApplication].keyWindow
  7. #define DEVICE_SPECIFIC_ADVIEW_AD_FORMAT ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) ? MAAdFormat.leader : MAAdFormat.banner
  8. #define IS_VERTICAL_BANNER_POSITION(_POS) ( [@"center_left" isEqual: adViewPosition] || [@"center_right" isEqual: adViewPosition] )
  9. #define DEGREES_TO_RADIANS(angle) ((angle) / 180.0 * M_PI)
  10. #ifdef __cplusplus
  11. extern "C" {
  12. #endif
  13. extern bool max_unity_should_disable_all_logs(void); // Forward declaration
  14. // UnityAppController.mm
  15. UIViewController* UnityGetGLViewController(void);
  16. UIWindow* UnityGetMainWindow(void);
  17. // life cycle management
  18. int UnityIsPaused(void);
  19. void UnityPause(int pause);
  20. void max_unity_dispatch_on_main_thread(dispatch_block_t block)
  21. {
  22. if ( block )
  23. {
  24. if ( [NSThread isMainThread] )
  25. {
  26. block();
  27. }
  28. else
  29. {
  30. dispatch_async(dispatch_get_main_queue(), block);
  31. }
  32. }
  33. }
  34. #ifdef __cplusplus
  35. }
  36. #endif
  37. @interface MAUnityAdManager()<MAAdDelegate, MAAdViewAdDelegate, MARewardedAdDelegate, MAAdRevenueDelegate, MAAdReviewDelegate, MAAdExpirationDelegate>
  38. // Parent Fields
  39. @property (nonatomic, weak) ALSdk *sdk;
  40. // Fullscreen Ad Fields
  41. @property (nonatomic, strong) NSMutableDictionary<NSString *, MAInterstitialAd *> *interstitials;
  42. @property (nonatomic, strong) NSMutableDictionary<NSString *, MAAppOpenAd *> *appOpenAds;
  43. @property (nonatomic, strong) NSMutableDictionary<NSString *, MARewardedAd *> *rewardedAds;
  44. // AdView Fields
  45. @property (nonatomic, strong) NSMutableDictionary<NSString *, MAAdView *> *adViews;
  46. @property (nonatomic, strong) NSMutableDictionary<NSString *, MAAdFormat *> *adViewAdFormats;
  47. @property (nonatomic, strong) NSMutableDictionary<NSString *, NSString *> *adViewPositions;
  48. @property (nonatomic, strong) NSMutableDictionary<NSString *, NSValue *> *adViewOffsets;
  49. @property (nonatomic, strong) NSMutableDictionary<NSString *, NSNumber *> *adViewWidths;
  50. @property (nonatomic, strong) NSMutableDictionary<NSString *, MAAdFormat *> *verticalAdViewFormats;
  51. @property (nonatomic, strong) NSMutableDictionary<NSString *, NSArray<NSLayoutConstraint *> *> *adViewConstraints;
  52. @property (nonatomic, strong) NSMutableDictionary<NSString *, NSMutableDictionary<NSString *, NSString *> *> *adViewExtraParametersToSetAfterCreate;
  53. @property (nonatomic, strong) NSMutableDictionary<NSString *, NSMutableDictionary<NSString *, id> *> *adViewLocalExtraParametersToSetAfterCreate;
  54. @property (nonatomic, strong) NSMutableDictionary<NSString *, NSString *> *adViewCustomDataToSetAfterCreate;
  55. @property (nonatomic, strong) NSMutableArray<NSString *> *adUnitIdentifiersToShowAfterCreate;
  56. @property (nonatomic, strong) NSMutableSet<NSString *> *disabledAdaptiveBannerAdUnitIdentifiers;
  57. @property (nonatomic, strong) NSMutableSet<NSString *> *disabledAutoRefreshAdViewAdUnitIdentifiers;
  58. @property (nonatomic, strong) NSMutableSet<NSString *> *ignoreSafeAreaLandscapeAdUnitIdentifiers;
  59. @property (nonatomic, strong) UIView *safeAreaBackground;
  60. @property (nonatomic, strong, nullable) UIColor *publisherBannerBackgroundColor;
  61. @property (nonatomic, strong) NSMutableDictionary<NSString *, MAAd *> *adInfoDict;
  62. @property (nonatomic, strong) NSObject *adInfoDictLock;
  63. @property (nonatomic, strong) NSOperationQueue *backgroundCallbackEventsQueue;
  64. @property (nonatomic, assign) BOOL resumeUnityAfterApplicationBecomesActive;
  65. @end
  66. // Internal
  67. @interface UIColor (ALUtils)
  68. + (nullable UIColor *)al_colorWithHexString:(NSString *)hexString;
  69. @end
  70. @interface NSNumber (ALUtils)
  71. + (NSNumber *)al_numberWithString:(NSString *)string;
  72. @end
  73. @interface NSString (ALUtils)
  74. @property (assign, readonly, getter=al_isValidString) BOOL al_validString;
  75. @end
  76. @interface MAAdFormat (ALUtils)
  77. @property (nonatomic, assign, readonly, getter=isFullscreenAd) BOOL fullscreenAd;
  78. @property (nonatomic, assign, readonly, getter=isAdViewAd) BOOL adViewAd;
  79. @end
  80. @implementation MAUnityAdManager
  81. static NSString *const SDK_TAG = @"AppLovinSdk";
  82. static NSString *const TAG = @"MAUnityAdManager";
  83. static NSString *const DEFAULT_AD_VIEW_POSITION = @"top_left";
  84. static ALUnityBackgroundCallback backgroundCallback;
  85. #pragma mark - Initialization
  86. - (instancetype)init
  87. {
  88. self = [super init];
  89. if ( self )
  90. {
  91. self.interstitials = [NSMutableDictionary dictionaryWithCapacity: 2];
  92. self.appOpenAds = [NSMutableDictionary dictionaryWithCapacity: 2];
  93. self.rewardedAds = [NSMutableDictionary dictionaryWithCapacity: 2];
  94. self.adViews = [NSMutableDictionary dictionaryWithCapacity: 2];
  95. self.adViewAdFormats = [NSMutableDictionary dictionaryWithCapacity: 2];
  96. self.adViewPositions = [NSMutableDictionary dictionaryWithCapacity: 2];
  97. self.adViewOffsets = [NSMutableDictionary dictionaryWithCapacity: 2];
  98. self.adViewWidths = [NSMutableDictionary dictionaryWithCapacity: 2];
  99. self.verticalAdViewFormats = [NSMutableDictionary dictionaryWithCapacity: 2];
  100. self.adViewConstraints = [NSMutableDictionary dictionaryWithCapacity: 2];
  101. self.adViewExtraParametersToSetAfterCreate = [NSMutableDictionary dictionaryWithCapacity: 1];
  102. self.adViewLocalExtraParametersToSetAfterCreate = [NSMutableDictionary dictionaryWithCapacity: 1];
  103. self.adViewCustomDataToSetAfterCreate = [NSMutableDictionary dictionaryWithCapacity: 1];
  104. self.adUnitIdentifiersToShowAfterCreate = [NSMutableArray arrayWithCapacity: 2];
  105. self.disabledAdaptiveBannerAdUnitIdentifiers = [NSMutableSet setWithCapacity: 2];
  106. self.disabledAutoRefreshAdViewAdUnitIdentifiers = [NSMutableSet setWithCapacity: 2];
  107. self.ignoreSafeAreaLandscapeAdUnitIdentifiers = [NSMutableSet setWithCapacity: 2];
  108. self.adInfoDict = [NSMutableDictionary dictionary];
  109. self.adInfoDictLock = [[NSObject alloc] init];
  110. self.backgroundCallbackEventsQueue = [[NSOperationQueue alloc] init];
  111. self.backgroundCallbackEventsQueue.maxConcurrentOperationCount = 1;
  112. max_unity_dispatch_on_main_thread(^{
  113. self.safeAreaBackground = [[UIView alloc] init];
  114. self.safeAreaBackground.hidden = YES;
  115. self.safeAreaBackground.backgroundColor = UIColor.clearColor;
  116. self.safeAreaBackground.translatesAutoresizingMaskIntoConstraints = NO;
  117. self.safeAreaBackground.userInteractionEnabled = NO;
  118. UIViewController *rootViewController = [self unityViewController];
  119. [rootViewController.view addSubview: self.safeAreaBackground];
  120. });
  121. // Enable orientation change listener, so that the position can be updated for vertical banners.
  122. [[NSNotificationCenter defaultCenter] addObserverForName: UIDeviceOrientationDidChangeNotification
  123. object: nil
  124. queue: [NSOperationQueue mainQueue]
  125. usingBlock:^(NSNotification *notification) {
  126. for ( NSString *adUnitIdentifier in self.verticalAdViewFormats )
  127. {
  128. [self positionAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: self.verticalAdViewFormats[adUnitIdentifier]];
  129. }
  130. }];
  131. [[NSNotificationCenter defaultCenter] addObserver: self
  132. selector: @selector(applicationPaused:)
  133. name: UIApplicationDidEnterBackgroundNotification
  134. object: nil];
  135. [[NSNotificationCenter defaultCenter] addObserver: self
  136. selector: @selector(applicationResumed:)
  137. name: UIApplicationDidBecomeActiveNotification
  138. object: nil];
  139. [[NSNotificationCenter defaultCenter] addObserverForName: UIApplicationDidBecomeActiveNotification
  140. object: nil
  141. queue: [NSOperationQueue mainQueue]
  142. usingBlock:^(NSNotification *notification) {
  143. #if !IS_TEST_APP
  144. if ( self.resumeUnityAfterApplicationBecomesActive && UnityIsPaused() )
  145. {
  146. UnityPause(NO);
  147. }
  148. #endif
  149. self.backgroundCallbackEventsQueue.suspended = NO;
  150. }];
  151. }
  152. return self;
  153. }
  154. + (MAUnityAdManager *)shared
  155. {
  156. static dispatch_once_t token;
  157. static MAUnityAdManager *shared;
  158. dispatch_once(&token, ^{
  159. shared = [[MAUnityAdManager alloc] init];
  160. });
  161. return shared;
  162. }
  163. + (void)setUnityBackgroundCallback:(ALUnityBackgroundCallback)unityBackgroundCallback
  164. {
  165. backgroundCallback = unityBackgroundCallback;
  166. }
  167. #pragma mark - Plugin Initialization
  168. - (void)initializeSdkWithConfiguration:(ALSdkInitializationConfiguration *)initConfig andCompletionHandler:(ALSdkInitializationCompletionHandler)completionHandler;
  169. {
  170. self.sdk = [ALSdk shared];
  171. [self.sdk initializeWithConfiguration: initConfig completionHandler:^(ALSdkConfiguration *configuration) {
  172. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  173. // Note: internal state should be updated first
  174. completionHandler( configuration );
  175. NSString *consentFlowUserGeographyStr = @(configuration.consentFlowUserGeography).stringValue;
  176. NSString *consentDialogStateStr = @(configuration.consentDialogState).stringValue;
  177. NSString *appTrackingStatus = @(configuration.appTrackingTransparencyStatus).stringValue; // Deliberately name it `appTrackingStatus` to be a bit more generic (in case Android introduces a similar concept)
  178. [self forwardUnityEventWithArgs: @{@"name" : @"OnSdkInitializedEvent",
  179. @"consentFlowUserGeography" : consentFlowUserGeographyStr,
  180. @"consentDialogState" : consentDialogStateStr,
  181. @"countryCode" : configuration.countryCode,
  182. @"appTrackingStatus" : appTrackingStatus,
  183. @"isSuccessfullyInitialized" : @([self.sdk isInitialized]),
  184. @"isTestModeEnabled" : @([configuration isTestModeEnabled])}];
  185. });
  186. }];
  187. }
  188. #pragma mark - Banners
  189. - (void)createBannerWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier atPosition:(nullable NSString *)bannerPosition
  190. {
  191. [self createAdViewWithAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier] atPosition: bannerPosition withOffset: CGPointZero];
  192. }
  193. - (void)createBannerWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier x:(CGFloat)xOffset y:(CGFloat)yOffset
  194. {
  195. [self createAdViewWithAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier] atPosition: DEFAULT_AD_VIEW_POSITION withOffset: CGPointMake(xOffset, yOffset)];
  196. }
  197. - (void)loadBannerWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
  198. {
  199. [self loadAdViewWithAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier]];
  200. }
  201. - (void)setBannerBackgroundColorForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier hexColorCode:(nullable NSString *)hexColorCode
  202. {
  203. [self setAdViewBackgroundColorForAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier] hexColorCode: hexColorCode];
  204. }
  205. - (void)setBannerPlacement:(nullable NSString *)placement forAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
  206. {
  207. [self setAdViewPlacement: placement forAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier]];
  208. }
  209. - (void)startBannerAutoRefreshForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
  210. {
  211. [self startAdViewAutoRefreshForAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier]];
  212. }
  213. - (void)stopBannerAutoRefreshForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
  214. {
  215. [self stopAdViewAutoRefreshForAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier]];
  216. }
  217. - (void)setBannerWidth:(CGFloat)width forAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
  218. {
  219. [self setAdViewWidth: width forAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier]];
  220. }
  221. - (void)updateBannerPosition:(nullable NSString *)bannerPosition forAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
  222. {
  223. [self updateAdViewPosition: bannerPosition withOffset: CGPointZero forAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier]];
  224. }
  225. - (void)updateBannerPosition:(CGFloat)xOffset y:(CGFloat)yOffset forAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
  226. {
  227. [self updateAdViewPosition: DEFAULT_AD_VIEW_POSITION withOffset: CGPointMake(xOffset, yOffset) forAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier]];
  228. }
  229. - (void)setBannerExtraParameterForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier key:(nullable NSString *)key value:(nullable NSString *)value
  230. {
  231. [self setAdViewExtraParameterForAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier] key: key value: value];
  232. }
  233. - (void)setBannerLocalExtraParameterForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier key:(nullable NSString *)key value:(nullable id)value
  234. {
  235. if ( !key )
  236. {
  237. [self log: @"Failed to set local extra parameter: No key specified"];
  238. return;
  239. }
  240. [self setAdViewLocalExtraParameterForAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier] key: key value: value];
  241. }
  242. - (void)setBannerCustomData:(nullable NSString *)customData forAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
  243. {
  244. [self setAdViewCustomData: customData forAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier]];
  245. }
  246. - (void)showBannerWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
  247. {
  248. [self showAdViewWithAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier]];
  249. }
  250. - (void)hideBannerWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
  251. {
  252. [self hideAdViewWithAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier]];
  253. }
  254. - (NSString *)bannerLayoutForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
  255. {
  256. return [self adViewLayoutForAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier]];
  257. }
  258. - (void)destroyBannerWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
  259. {
  260. [self destroyAdViewWithAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier]];
  261. }
  262. + (CGFloat)adaptiveBannerHeightForWidth:(CGFloat)width
  263. {
  264. return [DEVICE_SPECIFIC_ADVIEW_AD_FORMAT adaptiveSizeForWidth: width].height;
  265. }
  266. #pragma mark - MRECs
  267. - (void)createMRecWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier atPosition:(nullable NSString *)mrecPosition
  268. {
  269. [self createAdViewWithAdUnitIdentifier: adUnitIdentifier adFormat: MAAdFormat.mrec atPosition: mrecPosition withOffset: CGPointZero];
  270. }
  271. - (void)createMRecWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier x:(CGFloat)xOffset y:(CGFloat)yOffset
  272. {
  273. [self createAdViewWithAdUnitIdentifier: adUnitIdentifier adFormat: MAAdFormat.mrec atPosition: DEFAULT_AD_VIEW_POSITION withOffset: CGPointMake(xOffset, yOffset)];
  274. }
  275. - (void)loadMRecWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
  276. {
  277. [self loadAdViewWithAdUnitIdentifier: adUnitIdentifier adFormat: MAAdFormat.mrec];
  278. }
  279. - (void)setMRecPlacement:(nullable NSString *)placement forAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
  280. {
  281. [self setAdViewPlacement: placement forAdUnitIdentifier: adUnitIdentifier adFormat: MAAdFormat.mrec];
  282. }
  283. - (void)startMRecAutoRefreshForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
  284. {
  285. [self startAdViewAutoRefreshForAdUnitIdentifier: adUnitIdentifier adFormat: MAAdFormat.mrec];
  286. }
  287. - (void)stopMRecAutoRefreshForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
  288. {
  289. [self stopAdViewAutoRefreshForAdUnitIdentifier: adUnitIdentifier adFormat: MAAdFormat.mrec];
  290. }
  291. - (void)updateMRecPosition:(nullable NSString *)mrecPosition forAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
  292. {
  293. [self updateAdViewPosition: mrecPosition withOffset: CGPointZero forAdUnitIdentifier: adUnitIdentifier adFormat: MAAdFormat.mrec];
  294. }
  295. - (void)updateMRecPosition:(CGFloat)xOffset y:(CGFloat)yOffset forAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
  296. {
  297. [self updateAdViewPosition: DEFAULT_AD_VIEW_POSITION withOffset: CGPointMake(xOffset, yOffset) forAdUnitIdentifier: adUnitIdentifier adFormat: MAAdFormat.mrec];
  298. }
  299. - (void)setMRecExtraParameterForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier key:(nullable NSString *)key value:(nullable NSString *)value
  300. {
  301. [self setAdViewExtraParameterForAdUnitIdentifier: adUnitIdentifier adFormat: MAAdFormat.mrec key: key value: value];
  302. }
  303. - (void)setMRecLocalExtraParameterForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier key:(nullable NSString *)key value:(nullable id)value
  304. {
  305. if ( !key )
  306. {
  307. [self log: @"Failed to set local extra parameter: No key specified"];
  308. return;
  309. }
  310. [self setAdViewLocalExtraParameterForAdUnitIdentifier: adUnitIdentifier adFormat: MAAdFormat.mrec key: key value: value];
  311. }
  312. - (void)setMRecCustomData:(nullable NSString *)customData forAdUnitIdentifier:(nullable NSString *)adUnitIdentifier;
  313. {
  314. [self setAdViewCustomData: customData forAdUnitIdentifier: adUnitIdentifier adFormat: MAAdFormat.mrec];
  315. }
  316. - (void)showMRecWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
  317. {
  318. [self showAdViewWithAdUnitIdentifier: adUnitIdentifier adFormat: MAAdFormat.mrec];
  319. }
  320. - (void)destroyMRecWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
  321. {
  322. [self destroyAdViewWithAdUnitIdentifier: adUnitIdentifier adFormat: MAAdFormat.mrec];
  323. }
  324. - (void)hideMRecWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
  325. {
  326. [self hideAdViewWithAdUnitIdentifier: adUnitIdentifier adFormat: MAAdFormat.mrec];
  327. }
  328. - (NSString *)mrecLayoutForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
  329. {
  330. return [self adViewLayoutForAdUnitIdentifier: adUnitIdentifier adFormat: MAAdFormat.mrec];
  331. }
  332. #pragma mark - Interstitials
  333. - (void)loadInterstitialWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
  334. {
  335. MAInterstitialAd *interstitial = [self retrieveInterstitialForAdUnitIdentifier: adUnitIdentifier];
  336. [interstitial loadAd];
  337. }
  338. - (BOOL)isInterstitialReadyWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
  339. {
  340. MAInterstitialAd *interstitial = [self retrieveInterstitialForAdUnitIdentifier: adUnitIdentifier];
  341. return [interstitial isReady];
  342. }
  343. - (void)showInterstitialWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier placement:(nullable NSString *)placement customData:(nullable NSString *)customData
  344. {
  345. MAInterstitialAd *interstitial = [self retrieveInterstitialForAdUnitIdentifier: adUnitIdentifier];
  346. [interstitial showAdForPlacement: placement customData: customData];
  347. }
  348. - (void)setInterstitialExtraParameterForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier key:(nullable NSString *)key value:(nullable NSString *)value
  349. {
  350. MAInterstitialAd *interstitial = [self retrieveInterstitialForAdUnitIdentifier: adUnitIdentifier];
  351. [interstitial setExtraParameterForKey: key value: value];
  352. }
  353. - (void)setInterstitialLocalExtraParameterForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier key:(nullable NSString *)key value:(nullable id)value
  354. {
  355. if ( !key )
  356. {
  357. [self log: @"Failed to set local extra parameter: No key specified"];
  358. return;
  359. }
  360. MAInterstitialAd *interstitial = [self retrieveInterstitialForAdUnitIdentifier: adUnitIdentifier];
  361. [interstitial setLocalExtraParameterForKey: key value: value];
  362. }
  363. #pragma mark - App Open Ads
  364. - (void)loadAppOpenAdWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
  365. {
  366. MAAppOpenAd *appOpenAd = [self retrieveAppOpenAdForAdUnitIdentifier: adUnitIdentifier];
  367. [appOpenAd loadAd];
  368. }
  369. - (BOOL)isAppOpenAdReadyWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
  370. {
  371. MAAppOpenAd *appOpenAd = [self retrieveAppOpenAdForAdUnitIdentifier: adUnitIdentifier];
  372. return [appOpenAd isReady];
  373. }
  374. - (void)showAppOpenAdWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier placement:(nullable NSString *)placement customData:(nullable NSString *)customData
  375. {
  376. MAAppOpenAd *appOpenAd = [self retrieveAppOpenAdForAdUnitIdentifier: adUnitIdentifier];
  377. [appOpenAd showAdForPlacement: placement customData: customData];
  378. }
  379. - (void)setAppOpenAdExtraParameterForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier key:(nullable NSString *)key value:(nullable NSString *)value
  380. {
  381. MAAppOpenAd *appOpenAd = [self retrieveAppOpenAdForAdUnitIdentifier: adUnitIdentifier];
  382. [appOpenAd setExtraParameterForKey: key value: value];
  383. }
  384. - (void)setAppOpenAdLocalExtraParameterForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier key:(nullable NSString *)key value:(nullable id)value
  385. {
  386. if ( !key )
  387. {
  388. [self log: @"Failed to set local extra parameter: No key specified"];
  389. return;
  390. }
  391. MAAppOpenAd *appOpenAd = [self retrieveAppOpenAdForAdUnitIdentifier: adUnitIdentifier];
  392. [appOpenAd setLocalExtraParameterForKey: key value: value];
  393. }
  394. #pragma mark - Rewarded
  395. - (void)loadRewardedAdWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
  396. {
  397. MARewardedAd *rewardedAd = [self retrieveRewardedAdForAdUnitIdentifier: adUnitIdentifier];
  398. [rewardedAd loadAd];
  399. }
  400. - (BOOL)isRewardedAdReadyWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
  401. {
  402. MARewardedAd *rewardedAd = [self retrieveRewardedAdForAdUnitIdentifier: adUnitIdentifier];
  403. return [rewardedAd isReady];
  404. }
  405. - (void)showRewardedAdWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier placement:(nullable NSString *)placement customData:(nullable NSString *)customData
  406. {
  407. MARewardedAd *rewardedAd = [self retrieveRewardedAdForAdUnitIdentifier: adUnitIdentifier];
  408. [rewardedAd showAdForPlacement: placement customData: customData];
  409. }
  410. - (void)setRewardedAdExtraParameterForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier key:(nullable NSString *)key value:(nullable NSString *)value
  411. {
  412. MARewardedAd *rewardedAd = [self retrieveRewardedAdForAdUnitIdentifier: adUnitIdentifier];
  413. [rewardedAd setExtraParameterForKey: key value: value];
  414. }
  415. - (void)setRewardedAdLocalExtraParameterForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier key:(nullable NSString *)key value:(nullable id)value;
  416. {
  417. if ( !key )
  418. {
  419. [self log: @"Failed to set local extra parameter: No key specified"];
  420. return;
  421. }
  422. MARewardedAd *rewardedAd = [self retrieveRewardedAdForAdUnitIdentifier: adUnitIdentifier];
  423. [rewardedAd setLocalExtraParameterForKey: key value: value];
  424. }
  425. #pragma mark - Event Tracking
  426. - (void)trackEvent:(nullable NSString *)event parameters:(nullable NSString *)parameters
  427. {
  428. NSDictionary<NSString *, id> *deserializedParameters = [MAUnityAdManager deserializeParameters: parameters];
  429. [self.sdk.eventService trackEvent: event parameters: deserializedParameters];
  430. }
  431. #pragma mark - Ad Info
  432. - (NSDictionary<NSString *, id> *)adInfoForAd:(MAAd *)ad
  433. {
  434. return @{@"adUnitId" : ad.adUnitIdentifier,
  435. @"adFormat" : ad.format.label,
  436. @"networkName" : ad.networkName,
  437. @"networkPlacement" : ad.networkPlacement,
  438. @"creativeId" : ad.creativeIdentifier ?: @"",
  439. @"placement" : ad.placement ?: @"",
  440. @"revenue" : [@(ad.revenue) stringValue],
  441. @"revenuePrecision" : ad.revenuePrecision,
  442. @"waterfallInfo" : [self createAdWaterfallInfo: ad.waterfall],
  443. @"latencyMillis" : [self requestLatencyMillisFromRequestLatency: ad.requestLatency],
  444. @"dspName" : ad.DSPName ?: @""};
  445. }
  446. #pragma mark - Waterfall Information
  447. - (NSDictionary<NSString *, id> *)createAdWaterfallInfo:(MAAdWaterfallInfo *)waterfallInfo
  448. {
  449. NSMutableDictionary<NSString *, NSObject *> *waterfallInfoDict = [NSMutableDictionary dictionary];
  450. if ( !waterfallInfo ) return waterfallInfoDict;
  451. waterfallInfoDict[@"name"] = waterfallInfo.name;
  452. waterfallInfoDict[@"testName"] = waterfallInfo.testName;
  453. NSMutableArray<NSDictionary<NSString *, NSObject *> *> *networkResponsesArray = [NSMutableArray arrayWithCapacity: waterfallInfo.networkResponses.count];
  454. for ( MANetworkResponseInfo *response in waterfallInfo.networkResponses )
  455. {
  456. [networkResponsesArray addObject: [self createNetworkResponseInfo: response]];
  457. }
  458. waterfallInfoDict[@"networkResponses"] = networkResponsesArray;
  459. waterfallInfoDict[@"latencyMillis"] = [self requestLatencyMillisFromRequestLatency: waterfallInfo.latency];
  460. return waterfallInfoDict;
  461. }
  462. - (NSDictionary<NSString *, id> *)createNetworkResponseInfo:(MANetworkResponseInfo *)response
  463. {
  464. NSMutableDictionary<NSString *, NSObject *> *networkResponseDict = [NSMutableDictionary dictionary];
  465. networkResponseDict[@"adLoadState"] = @(response.adLoadState).stringValue;
  466. MAMediatedNetworkInfo *mediatedNetworkInfo = response.mediatedNetwork;
  467. if ( mediatedNetworkInfo )
  468. {
  469. NSMutableDictionary <NSString *, NSObject *> *networkInfoObject = [NSMutableDictionary dictionary];
  470. networkInfoObject[@"name"] = response.mediatedNetwork.name;
  471. networkInfoObject[@"adapterClassName"] = response.mediatedNetwork.adapterClassName;
  472. networkInfoObject[@"adapterVersion"] = response.mediatedNetwork.adapterVersion;
  473. networkInfoObject[@"sdkVersion"] = response.mediatedNetwork.sdkVersion;
  474. networkInfoObject[@"initializationStatus"] = @(response.mediatedNetwork.initializationStatus);
  475. networkResponseDict[@"mediatedNetwork"] = networkInfoObject;
  476. }
  477. networkResponseDict[@"credentials"] = response.credentials;
  478. networkResponseDict[@"isBidding"] = @([response isBidding]);
  479. MAError *error = response.error;
  480. if ( error )
  481. {
  482. NSMutableDictionary<NSString *, NSObject *> *errorObject = [NSMutableDictionary dictionary];
  483. errorObject[@"errorMessage"] = error.message;
  484. errorObject[@"adLoadFailure"] = error.adLoadFailureInfo;
  485. errorObject[@"errorCode"] = @(error.code).stringValue;
  486. errorObject[@"latencyMillis"] = [self requestLatencyMillisFromRequestLatency: error.requestLatency];
  487. networkResponseDict[@"error"] = errorObject;
  488. }
  489. networkResponseDict[@"latencyMillis"] = [self requestLatencyMillisFromRequestLatency: response.latency];
  490. return networkResponseDict;
  491. }
  492. #pragma mark - Ad Value
  493. - (NSString *)adValueForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier withKey:(nullable NSString *)key
  494. {
  495. if ( adUnitIdentifier == nil || adUnitIdentifier.length == 0 ) return @"";
  496. if ( key == nil || key.length == 0 ) return @"";
  497. MAAd *ad = [self adWithAdUnitIdentifier: adUnitIdentifier];
  498. if ( !ad ) return @"";
  499. return [ad adValueForKey: key];
  500. }
  501. #pragma mark - Ad Callbacks
  502. - (void)didLoadAd:(MAAd *)ad
  503. {
  504. NSString *name;
  505. MAAdFormat *adFormat = ad.format;
  506. if ( [adFormat isAdViewAd] )
  507. {
  508. MAAdView *adView = [self retrieveAdViewForAdUnitIdentifier: ad.adUnitIdentifier adFormat: adFormat];
  509. // An ad is now being shown, enable user interaction.
  510. adView.userInteractionEnabled = YES;
  511. if ( MAAdFormat.mrec == adFormat )
  512. {
  513. name = @"OnMRecAdLoadedEvent";
  514. }
  515. else
  516. {
  517. name = @"OnBannerAdLoadedEvent";
  518. }
  519. [self positionAdViewForAd: ad];
  520. // Do not auto-refresh by default if the ad view is not showing yet (e.g. first load during app launch and publisher does not automatically show banner upon load success)
  521. // We will resume auto-refresh in -[MAUnityAdManager showBannerWithAdUnitIdentifier:].
  522. if ( adView && [adView isHidden] )
  523. {
  524. [adView stopAutoRefresh];
  525. }
  526. }
  527. else if ( MAAdFormat.interstitial == adFormat )
  528. {
  529. name = @"OnInterstitialLoadedEvent";
  530. }
  531. else if ( MAAdFormat.appOpen == adFormat )
  532. {
  533. name = @"OnAppOpenAdLoadedEvent";
  534. }
  535. else if ( MAAdFormat.rewarded == adFormat )
  536. {
  537. name = @"OnRewardedAdLoadedEvent";
  538. }
  539. else
  540. {
  541. [self logInvalidAdFormat: adFormat];
  542. return;
  543. }
  544. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  545. @synchronized ( self.adInfoDictLock )
  546. {
  547. self.adInfoDict[ad.adUnitIdentifier] = ad;
  548. }
  549. NSDictionary<NSString *, id> *args = [self defaultAdEventParametersForName: name withAd: ad];
  550. [self forwardUnityEventWithArgs: args];
  551. });
  552. }
  553. - (void)didFailToLoadAdForAdUnitIdentifier:(NSString *)adUnitIdentifier withError:(MAError *)error
  554. {
  555. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  556. if ( !adUnitIdentifier )
  557. {
  558. [self log: @"adUnitIdentifier cannot be nil from %@", [NSThread callStackSymbols]];
  559. return;
  560. }
  561. NSString *name;
  562. if ( self.adViews[adUnitIdentifier] )
  563. {
  564. MAAdFormat *adFormat = self.adViewAdFormats[adUnitIdentifier];
  565. if ( MAAdFormat.mrec == adFormat )
  566. {
  567. name = @"OnMRecAdLoadFailedEvent";
  568. }
  569. else
  570. {
  571. name = @"OnBannerAdLoadFailedEvent";
  572. }
  573. }
  574. else if ( self.interstitials[adUnitIdentifier] )
  575. {
  576. name = @"OnInterstitialLoadFailedEvent";
  577. }
  578. else if ( self.appOpenAds[adUnitIdentifier] )
  579. {
  580. name = @"OnAppOpenAdLoadFailedEvent";
  581. }
  582. else if ( self.rewardedAds[adUnitIdentifier] )
  583. {
  584. name = @"OnRewardedAdLoadFailedEvent";
  585. }
  586. else
  587. {
  588. [self log: @"invalid adUnitId from %@", [NSThread callStackSymbols]];
  589. return;
  590. }
  591. @synchronized ( self.adInfoDictLock )
  592. {
  593. [self.adInfoDict removeObjectForKey: adUnitIdentifier];
  594. }
  595. [self forwardUnityEventWithArgs: @{@"name" : name,
  596. @"adUnitId" : adUnitIdentifier,
  597. @"errorCode" : [@(error.code) stringValue],
  598. @"errorMessage" : error.message,
  599. @"waterfallInfo" : [self createAdWaterfallInfo: error.waterfall],
  600. @"adLoadFailureInfo" : error.adLoadFailureInfo ?: @"",
  601. @"latencyMillis" : [self requestLatencyMillisFromRequestLatency: error.requestLatency]}];
  602. });
  603. }
  604. - (void)didClickAd:(MAAd *)ad
  605. {
  606. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  607. NSString *name;
  608. MAAdFormat *adFormat = ad.format;
  609. if ( MAAdFormat.banner == adFormat || MAAdFormat.leader == adFormat )
  610. {
  611. name = @"OnBannerAdClickedEvent";
  612. }
  613. else if ( MAAdFormat.mrec == adFormat )
  614. {
  615. name = @"OnMRecAdClickedEvent";
  616. }
  617. else if ( MAAdFormat.interstitial == adFormat )
  618. {
  619. name = @"OnInterstitialClickedEvent";
  620. }
  621. else if ( MAAdFormat.appOpen == adFormat )
  622. {
  623. name = @"OnAppOpenAdClickedEvent";
  624. }
  625. else if ( MAAdFormat.rewarded == adFormat )
  626. {
  627. name = @"OnRewardedAdClickedEvent";
  628. }
  629. else
  630. {
  631. [self logInvalidAdFormat: adFormat];
  632. return;
  633. }
  634. NSDictionary<NSString *, id> *args = [self defaultAdEventParametersForName: name withAd: ad];
  635. [self forwardUnityEventWithArgs: args];
  636. });
  637. }
  638. - (void)didDisplayAd:(MAAd *)ad
  639. {
  640. // BMLs do not support [DISPLAY] events in Unity
  641. MAAdFormat *adFormat = ad.format;
  642. if ( ![adFormat isFullscreenAd] ) return;
  643. #if !IS_TEST_APP
  644. // UnityPause needs to be called on the main thread.
  645. UnityPause(YES);
  646. #endif
  647. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  648. NSString *name;
  649. if ( MAAdFormat.interstitial == adFormat )
  650. {
  651. name = @"OnInterstitialDisplayedEvent";
  652. }
  653. else if ( MAAdFormat.appOpen == adFormat )
  654. {
  655. name = @"OnAppOpenAdDisplayedEvent";
  656. }
  657. else // rewarded
  658. {
  659. name = @"OnRewardedAdDisplayedEvent";
  660. }
  661. NSDictionary<NSString *, id> *args = [self defaultAdEventParametersForName: name withAd: ad];
  662. [self forwardUnityEventWithArgs: args];
  663. });
  664. }
  665. - (void)didFailToDisplayAd:(MAAd *)ad withError:(MAError *)error
  666. {
  667. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  668. // BMLs do not support [DISPLAY] events in Unity
  669. MAAdFormat *adFormat = ad.format;
  670. if ( ![adFormat isFullscreenAd] ) return;
  671. NSString *name;
  672. if ( MAAdFormat.interstitial == adFormat )
  673. {
  674. name = @"OnInterstitialAdFailedToDisplayEvent";
  675. }
  676. else if ( MAAdFormat.appOpen == adFormat )
  677. {
  678. name = @"OnAppOpenAdFailedToDisplayEvent";
  679. }
  680. else // rewarded
  681. {
  682. name = @"OnRewardedAdFailedToDisplayEvent";
  683. }
  684. NSMutableDictionary<NSString *, id> *args = [self defaultAdEventParametersForName: name withAd: ad];
  685. args[@"errorCode"] = [@(error.code) stringValue];
  686. args[@"errorMessage"] = error.message;
  687. args[@"mediatedNetworkErrorCode"] = [@(error.mediatedNetworkErrorCode) stringValue];
  688. args[@"mediatedNetworkErrorMessage"] = error.mediatedNetworkErrorMessage;
  689. args[@"waterfallInfo"] = [self createAdWaterfallInfo: error.waterfall];
  690. args[@"latencyMillis"] = [self requestLatencyMillisFromRequestLatency: error.requestLatency];
  691. [self forwardUnityEventWithArgs: args];
  692. });
  693. }
  694. - (void)didHideAd:(MAAd *)ad
  695. {
  696. // BMLs do not support [HIDDEN] events in Unity
  697. MAAdFormat *adFormat = ad.format;
  698. if ( ![adFormat isFullscreenAd] ) return;
  699. #if !IS_TEST_APP
  700. extern bool _didResignActive;
  701. if ( _didResignActive )
  702. {
  703. // If the application is not active, we should wait until application becomes active to resume unity.
  704. self.resumeUnityAfterApplicationBecomesActive = YES;
  705. }
  706. else
  707. {
  708. // UnityPause needs to be called on the main thread.
  709. UnityPause(NO);
  710. }
  711. #endif
  712. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  713. NSString *name;
  714. if ( MAAdFormat.interstitial == adFormat )
  715. {
  716. name = @"OnInterstitialHiddenEvent";
  717. }
  718. else if ( MAAdFormat.appOpen == adFormat )
  719. {
  720. name = @"OnAppOpenAdHiddenEvent";
  721. }
  722. else // rewarded
  723. {
  724. name = @"OnRewardedAdHiddenEvent";
  725. }
  726. NSDictionary<NSString *, id> *args = [self defaultAdEventParametersForName: name withAd: ad];
  727. [self forwardUnityEventWithArgs: args];
  728. });
  729. }
  730. - (void)didExpandAd:(MAAd *)ad
  731. {
  732. MAAdFormat *adFormat = ad.format;
  733. if ( ![adFormat isAdViewAd] )
  734. {
  735. [self logInvalidAdFormat: adFormat];
  736. return;
  737. }
  738. #if !IS_TEST_APP
  739. // UnityPause needs to be called on the main thread.
  740. UnityPause(YES);
  741. #endif
  742. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  743. NSString *name;
  744. if ( MAAdFormat.mrec == adFormat )
  745. {
  746. name = @"OnMRecAdExpandedEvent";
  747. }
  748. else
  749. {
  750. name = @"OnBannerAdExpandedEvent";
  751. }
  752. NSDictionary<NSString *, id> *args = [self defaultAdEventParametersForName: name withAd: ad];
  753. [self forwardUnityEventWithArgs: args];
  754. });
  755. }
  756. - (void)didCollapseAd:(MAAd *)ad
  757. {
  758. MAAdFormat *adFormat = ad.format;
  759. if ( ![adFormat isAdViewAd] )
  760. {
  761. [self logInvalidAdFormat: adFormat];
  762. return;
  763. }
  764. #if !IS_TEST_APP
  765. extern bool _didResignActive;
  766. if ( _didResignActive )
  767. {
  768. // If the application is not active, we should wait until application becomes active to resume unity.
  769. self.resumeUnityAfterApplicationBecomesActive = YES;
  770. }
  771. else
  772. {
  773. // UnityPause needs to be called on the main thread.
  774. UnityPause(NO);
  775. }
  776. #endif
  777. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  778. NSString *name;
  779. if ( MAAdFormat.mrec == adFormat )
  780. {
  781. name = @"OnMRecAdCollapsedEvent";
  782. }
  783. else
  784. {
  785. name = @"OnBannerAdCollapsedEvent";
  786. }
  787. NSDictionary<NSString *, id> *args = [self defaultAdEventParametersForName: name withAd: ad];
  788. [self forwardUnityEventWithArgs: args];
  789. });
  790. }
  791. - (void)didRewardUserForAd:(MAAd *)ad withReward:(MAReward *)reward
  792. {
  793. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  794. MAAdFormat *adFormat = ad.format;
  795. if ( adFormat != MAAdFormat.rewarded )
  796. {
  797. [self logInvalidAdFormat: adFormat];
  798. return;
  799. }
  800. NSString *rewardLabel = reward ? reward.label : @"";
  801. NSInteger rewardAmountInt = reward ? reward.amount : 0;
  802. NSString *rewardAmount = [@(rewardAmountInt) stringValue];
  803. NSString *name = @"OnRewardedAdReceivedRewardEvent";
  804. NSMutableDictionary<NSString *, id> *args = [self defaultAdEventParametersForName: name withAd: ad];
  805. args[@"rewardLabel"] = rewardLabel;
  806. args[@"rewardAmount"] = rewardAmount;
  807. [self forwardUnityEventWithArgs: args];
  808. });
  809. }
  810. - (void)didPayRevenueForAd:(MAAd *)ad
  811. {
  812. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  813. NSString *name;
  814. MAAdFormat *adFormat = ad.format;
  815. if ( MAAdFormat.banner == adFormat || MAAdFormat.leader == adFormat )
  816. {
  817. name = @"OnBannerAdRevenuePaidEvent";
  818. }
  819. else if ( MAAdFormat.mrec == adFormat )
  820. {
  821. name = @"OnMRecAdRevenuePaidEvent";
  822. }
  823. else if ( MAAdFormat.interstitial == adFormat )
  824. {
  825. name = @"OnInterstitialAdRevenuePaidEvent";
  826. }
  827. else if ( MAAdFormat.appOpen == adFormat )
  828. {
  829. name = @"OnAppOpenAdRevenuePaidEvent";
  830. }
  831. else if ( MAAdFormat.rewarded == adFormat )
  832. {
  833. name = @"OnRewardedAdRevenuePaidEvent";
  834. }
  835. else
  836. {
  837. [self logInvalidAdFormat: adFormat];
  838. return;
  839. }
  840. NSMutableDictionary<NSString *, id> *args = [self defaultAdEventParametersForName: name withAd: ad];
  841. args[@"keepInBackground"] = @([adFormat isFullscreenAd]);
  842. [self forwardUnityEventWithArgs: args];
  843. });
  844. }
  845. - (void)didReloadExpiredAd:(MAAd *)expiredAd withNewAd:(MAAd *)newAd;
  846. {
  847. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  848. NSString *name;
  849. MAAdFormat *adFormat = newAd.format;
  850. if ( MAAdFormat.interstitial == adFormat )
  851. {
  852. name = @"OnExpiredInterstitialAdReloadedEvent";
  853. }
  854. else if ( MAAdFormat.appOpen == adFormat )
  855. {
  856. name = @"OnExpiredAppOpenAdReloadedEvent";
  857. }
  858. else if ( MAAdFormat.rewarded == adFormat )
  859. {
  860. name = @"OnExpiredRewardedAdReloadedEvent ";
  861. }
  862. else
  863. {
  864. [self logInvalidAdFormat: adFormat];
  865. return;
  866. }
  867. @synchronized ( self.adInfoDictLock )
  868. {
  869. self.adInfoDict[newAd.adUnitIdentifier] = newAd;
  870. }
  871. NSMutableDictionary<NSString *, NSObject *> *args = [NSMutableDictionary dictionary];
  872. args[@"expiredAdInfo"] = [self adInfoForAd: expiredAd];
  873. args[@"newAdInfo"] = [self adInfoForAd: newAd];
  874. args[@"name"] = name;
  875. [self forwardUnityEventWithArgs: args];
  876. });
  877. }
  878. - (void)didGenerateCreativeIdentifier:(NSString *)creativeIdentifier forAd:(MAAd *)ad
  879. {
  880. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  881. NSString *name;
  882. MAAdFormat *adFormat = ad.format;
  883. if ( MAAdFormat.banner == adFormat || MAAdFormat.leader == adFormat )
  884. {
  885. name = @"OnBannerAdReviewCreativeIdGeneratedEvent";
  886. }
  887. else if ( MAAdFormat.mrec == adFormat )
  888. {
  889. name = @"OnMRecAdReviewCreativeIdGeneratedEvent";
  890. }
  891. else if ( MAAdFormat.interstitial == adFormat )
  892. {
  893. name = @"OnInterstitialAdReviewCreativeIdGeneratedEvent";
  894. }
  895. else if ( MAAdFormat.rewarded == adFormat )
  896. {
  897. name = @"OnRewardedAdReviewCreativeIdGeneratedEvent";
  898. }
  899. else
  900. {
  901. [self logInvalidAdFormat: adFormat];
  902. return;
  903. }
  904. NSMutableDictionary<NSString *, id> *args = [self defaultAdEventParametersForName: name withAd: ad];
  905. args[@"adReviewCreativeId"] = creativeIdentifier;
  906. args[@"keepInBackground"] = @([adFormat isFullscreenAd]);
  907. // Forward the event in background for fullscreen ads so that the user gets the callback even while the ad is playing.
  908. [self forwardUnityEventWithArgs: args];
  909. });
  910. }
  911. - (NSMutableDictionary<NSString *, id> *)defaultAdEventParametersForName:(NSString *)name withAd:(MAAd *)ad
  912. {
  913. NSMutableDictionary<NSString *, id> *args = [[self adInfoForAd: ad] mutableCopy];
  914. args[@"name"] = name;
  915. return args;
  916. }
  917. #pragma mark - Internal Methods
  918. - (void)createAdViewWithAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat atPosition:(NSString *)adViewPosition withOffset:(CGPoint)offset
  919. {
  920. max_unity_dispatch_on_main_thread(^{
  921. [self log: @"Creating %@ with ad unit identifier \"%@\" and position: \"%@\"", adFormat, adUnitIdentifier, adViewPosition];
  922. if ( self.adViews[adUnitIdentifier] )
  923. {
  924. [self log: @"Trying to create a %@ that was already created. This will cause the current ad to be hidden.", adFormat.label];
  925. }
  926. // Retrieve ad view from the map
  927. MAAdView *adView = [self retrieveAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat atPosition: adViewPosition withOffset: offset];
  928. adView.hidden = YES;
  929. self.safeAreaBackground.hidden = YES;
  930. // Position ad view immediately so if publisher sets color before ad loads, it will not be the size of the screen
  931. self.adViewAdFormats[adUnitIdentifier] = adFormat;
  932. [self positionAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
  933. NSDictionary<NSString *, NSString *> *extraParameters = self.adViewExtraParametersToSetAfterCreate[adUnitIdentifier];
  934. // Enable adaptive banners by default for banners and leaders.
  935. if ( [adFormat isBannerOrLeaderAd] )
  936. {
  937. // Check if there is already a pending setting for adaptive banners. If not, enable them.
  938. if ( !extraParameters[@"adaptive_banner"] )
  939. {
  940. [adView setExtraParameterForKey: @"adaptive_banner" value: @"true"];
  941. }
  942. }
  943. // Handle initial extra parameters if publisher sets it before creating ad view
  944. if ( extraParameters )
  945. {
  946. for ( NSString *key in extraParameters )
  947. {
  948. [adView setExtraParameterForKey: key value: extraParameters[key]];
  949. [self handleExtraParameterChangesIfNeededForAdUnitIdentifier: adUnitIdentifier
  950. adFormat: adFormat
  951. key: key
  952. value: extraParameters[key]];
  953. }
  954. [self.adViewExtraParametersToSetAfterCreate removeObjectForKey: adUnitIdentifier];
  955. }
  956. // Handle initial local extra parameters if publisher sets it before creating ad view
  957. if ( self.adViewLocalExtraParametersToSetAfterCreate[adUnitIdentifier] )
  958. {
  959. NSDictionary<NSString *, NSString *> *localExtraParameters = self.adViewLocalExtraParametersToSetAfterCreate[adUnitIdentifier];
  960. for ( NSString *key in localExtraParameters )
  961. {
  962. [adView setLocalExtraParameterForKey: key value: localExtraParameters[key]];
  963. }
  964. [self.adViewLocalExtraParametersToSetAfterCreate removeObjectForKey: adUnitIdentifier];
  965. }
  966. // Handle initial custom data if publisher sets it before creating ad view
  967. if ( self.adViewCustomDataToSetAfterCreate[adUnitIdentifier] )
  968. {
  969. NSString *customData = self.adViewCustomDataToSetAfterCreate[adUnitIdentifier];
  970. adView.customData = customData;
  971. [self.adViewCustomDataToSetAfterCreate removeObjectForKey: adUnitIdentifier];
  972. }
  973. [adView loadAd];
  974. // Disable auto-refresh if publisher sets it before creating the ad view.
  975. if ( [self.disabledAutoRefreshAdViewAdUnitIdentifiers containsObject: adUnitIdentifier] )
  976. {
  977. [adView stopAutoRefresh];
  978. }
  979. // The publisher may have requested to show the banner before it was created. Now that the banner is created, show it.
  980. if ( [self.adUnitIdentifiersToShowAfterCreate containsObject: adUnitIdentifier] )
  981. {
  982. [self showAdViewWithAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
  983. [self.adUnitIdentifiersToShowAfterCreate removeObject: adUnitIdentifier];
  984. }
  985. });
  986. }
  987. - (void)loadAdViewWithAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat
  988. {
  989. max_unity_dispatch_on_main_thread(^{
  990. MAAdView *adView = [self retrieveAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
  991. if ( !adView )
  992. {
  993. [self log: @"%@ does not exist for ad unit identifier \"%@\".", adFormat.label, adUnitIdentifier];
  994. return;
  995. }
  996. if ( ![self.disabledAutoRefreshAdViewAdUnitIdentifiers containsObject: adUnitIdentifier] )
  997. {
  998. if ( [adView isHidden] )
  999. {
  1000. [self log: @"Auto-refresh will resume when the %@ ad is shown. You should only call LoadBanner() or LoadMRec() if you explicitly pause auto-refresh and want to manually load an ad.", adFormat.label];
  1001. return;
  1002. }
  1003. [self log: @"You must stop auto-refresh if you want to manually load %@ ads.", adFormat.label];
  1004. return;
  1005. }
  1006. [adView loadAd];
  1007. });
  1008. }
  1009. - (void)setAdViewBackgroundColorForAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat hexColorCode:(NSString *)hexColorCode
  1010. {
  1011. max_unity_dispatch_on_main_thread(^{
  1012. [self log: @"Setting %@ with ad unit identifier \"%@\" to color: \"%@\"", adFormat, adUnitIdentifier, hexColorCode];
  1013. // In some cases, black color may get redrawn on each frame update, resulting in an undesired flicker
  1014. NSString *hexColorCodeToUse = [hexColorCode containsString: @"FF000000"] ? @"FF000001" : hexColorCode;
  1015. UIColor *convertedColor = [UIColor al_colorWithHexString: hexColorCodeToUse];
  1016. MAAdView *view = [self retrieveAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
  1017. self.publisherBannerBackgroundColor = convertedColor;
  1018. self.safeAreaBackground.backgroundColor = view.backgroundColor = convertedColor;
  1019. // Position adView to ensure logic that depends on background color is properly run
  1020. [self positionAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
  1021. });
  1022. }
  1023. - (void)setAdViewPlacement:(nullable NSString *)placement forAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat
  1024. {
  1025. max_unity_dispatch_on_main_thread(^{
  1026. [self log: @"Setting placement \"%@\" for \"%@\" with ad unit identifier \"%@\"", placement, adFormat, adUnitIdentifier];
  1027. MAAdView *adView = [self retrieveAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
  1028. adView.placement = placement;
  1029. });
  1030. }
  1031. - (void)startAdViewAutoRefreshForAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat
  1032. {
  1033. max_unity_dispatch_on_main_thread(^{
  1034. [self log: @"Starting %@ auto refresh for ad unit identifier \"%@\"", adFormat.label, adUnitIdentifier];
  1035. [self.disabledAutoRefreshAdViewAdUnitIdentifiers removeObject: adUnitIdentifier];
  1036. MAAdView *adView = [self retrieveAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
  1037. if ( !adView )
  1038. {
  1039. [self log: @"%@ does not exist for ad unit identifier %@.", adFormat.label, adUnitIdentifier];
  1040. return;
  1041. }
  1042. [adView startAutoRefresh];
  1043. });
  1044. }
  1045. - (void)stopAdViewAutoRefreshForAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat
  1046. {
  1047. max_unity_dispatch_on_main_thread(^{
  1048. [self log: @"Stopping %@ auto refresh for ad unit identifier \"%@\"", adFormat.label, adUnitIdentifier];
  1049. [self.disabledAutoRefreshAdViewAdUnitIdentifiers addObject: adUnitIdentifier];
  1050. MAAdView *adView = [self retrieveAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
  1051. if ( !adView )
  1052. {
  1053. [self log: @"%@ does not exist for ad unit identifier %@.", adFormat.label, adUnitIdentifier];
  1054. return;
  1055. }
  1056. [adView stopAutoRefresh];
  1057. });
  1058. }
  1059. - (void)setAdViewWidth:(CGFloat)width forAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat
  1060. {
  1061. max_unity_dispatch_on_main_thread(^{
  1062. [self log: @"Setting width %f for \"%@\" with ad unit identifier \"%@\"", width, adFormat, adUnitIdentifier];
  1063. BOOL isBannerOrLeader = adFormat.isBannerOrLeaderAd;
  1064. // Banners and leaders need to be at least 320pts wide.
  1065. CGFloat minWidth = isBannerOrLeader ? MAAdFormat.banner.size.width : adFormat.size.width;
  1066. if ( width < minWidth )
  1067. {
  1068. [self log: @"The provided width: %f is smaller than the minimum required width: %f for ad format: %@. Automatically setting width to %f.", width, minWidth, adFormat, minWidth];
  1069. }
  1070. CGFloat widthToSet = MAX( minWidth, width );
  1071. self.adViewWidths[adUnitIdentifier] = @(widthToSet);
  1072. [self positionAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
  1073. });
  1074. }
  1075. - (void)updateAdViewPosition:(NSString *)adViewPosition withOffset:(CGPoint)offset forAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat
  1076. {
  1077. max_unity_dispatch_on_main_thread(^{
  1078. self.adViewPositions[adUnitIdentifier] = adViewPosition;
  1079. self.adViewOffsets[adUnitIdentifier] = [NSValue valueWithCGPoint: offset];
  1080. [self positionAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
  1081. });
  1082. }
  1083. - (void)setAdViewExtraParameterForAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat key:(NSString *)key value:(nullable NSString *)value
  1084. {
  1085. max_unity_dispatch_on_main_thread(^{
  1086. [self log: @"Setting %@ extra with key: \"%@\" value: \"%@\"", adFormat, key, value];
  1087. MAAdView *adView = [self retrieveAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
  1088. if ( adView )
  1089. {
  1090. [adView setExtraParameterForKey: key value: value];
  1091. }
  1092. else
  1093. {
  1094. [self log: @"%@ does not exist for ad unit identifier \"%@\". Saving extra parameter to be set when it is created.", adFormat, adUnitIdentifier];
  1095. // The adView has not yet been created. Store the extra parameters, so that they can be added once the banner has been created.
  1096. NSMutableDictionary<NSString *, NSString *> *extraParameters = self.adViewExtraParametersToSetAfterCreate[adUnitIdentifier];
  1097. if ( !extraParameters )
  1098. {
  1099. extraParameters = [NSMutableDictionary dictionaryWithCapacity: 1];
  1100. self.adViewExtraParametersToSetAfterCreate[adUnitIdentifier] = extraParameters;
  1101. }
  1102. extraParameters[key] = value;
  1103. }
  1104. // Certain extra parameters need to be handled immediately
  1105. [self handleExtraParameterChangesIfNeededForAdUnitIdentifier: adUnitIdentifier
  1106. adFormat: adFormat
  1107. key: key
  1108. value: value];
  1109. });
  1110. }
  1111. - (void)setAdViewLocalExtraParameterForAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat key:(NSString *)key value:(nullable id)value
  1112. {
  1113. max_unity_dispatch_on_main_thread(^{
  1114. [self log: @"Setting %@ local extra with key: \"%@\" value: \"%@\"", adFormat, key, value];
  1115. MAAdView *adView = [self retrieveAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
  1116. if ( adView )
  1117. {
  1118. [adView setLocalExtraParameterForKey: key value: value];
  1119. }
  1120. else
  1121. {
  1122. [self log: @"%@ does not exist for ad unit identifier \"%@\". Saving local extra parameter to be set when it is created.", adFormat, adUnitIdentifier];
  1123. // The adView has not yet been created. Store the loca extra parameters, so that they can be added once the adview has been created.
  1124. NSMutableDictionary<NSString *, id> *localExtraParameters = self.adViewLocalExtraParametersToSetAfterCreate[adUnitIdentifier];
  1125. if ( !localExtraParameters )
  1126. {
  1127. localExtraParameters = [NSMutableDictionary dictionaryWithCapacity: 1];
  1128. self.adViewLocalExtraParametersToSetAfterCreate[adUnitIdentifier] = localExtraParameters;
  1129. }
  1130. localExtraParameters[key] = value;
  1131. }
  1132. });
  1133. }
  1134. - (void)setAdViewCustomData:(nullable NSString *)customData forAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat
  1135. {
  1136. max_unity_dispatch_on_main_thread(^{
  1137. MAAdView *adView = [self retrieveAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
  1138. if ( adView )
  1139. {
  1140. adView.customData = customData;
  1141. }
  1142. else
  1143. {
  1144. [self log: @"%@ does not exist for ad unit identifier %@. Saving custom data to be set when it is created.", adFormat, adUnitIdentifier];
  1145. // The adView has not yet been created. Store the custom data, so that they can be added once the AdView has been created.
  1146. self.adViewCustomDataToSetAfterCreate[adUnitIdentifier] = customData;
  1147. }
  1148. });
  1149. }
  1150. - (void)handleExtraParameterChangesIfNeededForAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat key:(NSString *)key value:(nullable NSString *)value
  1151. {
  1152. if ( MAAdFormat.mrec != adFormat )
  1153. {
  1154. if ( [@"force_banner" isEqualToString: key] )
  1155. {
  1156. BOOL shouldForceBanner = [NSNumber al_numberWithString: value].boolValue;
  1157. MAAdFormat *forcedAdFormat = shouldForceBanner ? MAAdFormat.banner : DEVICE_SPECIFIC_ADVIEW_AD_FORMAT;
  1158. self.adViewAdFormats[adUnitIdentifier] = forcedAdFormat;
  1159. [self positionAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: forcedAdFormat];
  1160. }
  1161. else if ( [@"adaptive_banner" isEqualToString: key] )
  1162. {
  1163. BOOL shouldUseAdaptiveBanner = [NSNumber al_numberWithString: value].boolValue;
  1164. if ( shouldUseAdaptiveBanner )
  1165. {
  1166. [self.disabledAdaptiveBannerAdUnitIdentifiers removeObject: adUnitIdentifier];
  1167. }
  1168. else
  1169. {
  1170. [self.disabledAdaptiveBannerAdUnitIdentifiers addObject: adUnitIdentifier];
  1171. }
  1172. [self positionAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
  1173. }
  1174. else if ( [@"ignore_safe_area_landscape" isEqualToString: key] && [NSNumber al_numberWithString: value].boolValue )
  1175. {
  1176. [self.ignoreSafeAreaLandscapeAdUnitIdentifiers addObject: adUnitIdentifier];
  1177. [self positionAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
  1178. }
  1179. }
  1180. }
  1181. - (void)showAdViewWithAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat
  1182. {
  1183. max_unity_dispatch_on_main_thread(^{
  1184. [self log: @"Showing %@ with ad unit identifier \"%@\"", adFormat, adUnitIdentifier];
  1185. MAAdView *view = [self retrieveAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
  1186. if ( !view )
  1187. {
  1188. [self log: @"%@ does not exist for ad unit identifier %@.", adFormat, adUnitIdentifier];
  1189. // The adView has not yet been created. Store the ad unit ID, so that it can be displayed once the banner has been created.
  1190. [self.adUnitIdentifiersToShowAfterCreate addObject: adUnitIdentifier];
  1191. }
  1192. else
  1193. {
  1194. // Check edge case where ad may be detatched from view controller
  1195. if ( !view.window.rootViewController )
  1196. {
  1197. [self log: @"%@ missing view controller - re-attaching to %@...", adFormat, [self unityViewController]];
  1198. UIViewController *rootViewController = [self unityViewController];
  1199. [rootViewController.view addSubview: view];
  1200. [self positionAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
  1201. }
  1202. }
  1203. self.safeAreaBackground.hidden = NO;
  1204. view.hidden = NO;
  1205. if ( ![self.disabledAutoRefreshAdViewAdUnitIdentifiers containsObject: adUnitIdentifier] )
  1206. {
  1207. [view startAutoRefresh];
  1208. }
  1209. });
  1210. }
  1211. - (void)hideAdViewWithAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat
  1212. {
  1213. max_unity_dispatch_on_main_thread(^{
  1214. [self log: @"Hiding %@ with ad unit identifier \"%@\"", adFormat, adUnitIdentifier];
  1215. [self.adUnitIdentifiersToShowAfterCreate removeObject: adUnitIdentifier];
  1216. MAAdView *view = [self retrieveAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
  1217. view.hidden = YES;
  1218. self.safeAreaBackground.hidden = YES;
  1219. [view stopAutoRefresh];
  1220. });
  1221. }
  1222. - (NSString *)adViewLayoutForAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat
  1223. {
  1224. [self log: @"Getting %@ position with ad unit identifier \"%@\"", adFormat, adUnitIdentifier];
  1225. MAAdView *view = [self retrieveAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
  1226. if ( !view )
  1227. {
  1228. [self log: @"%@ does not exist for ad unit identifier %@", adFormat, adUnitIdentifier];
  1229. return @"";
  1230. }
  1231. CGRect adViewFrame = view.frame;
  1232. return [MAUnityAdManager serializeParameters: @{@"origin_x" : [NSString stringWithFormat: @"%f", adViewFrame.origin.x],
  1233. @"origin_y" : [NSString stringWithFormat: @"%f", adViewFrame.origin.y],
  1234. @"width" : [NSString stringWithFormat: @"%f", CGRectGetWidth(adViewFrame)],
  1235. @"height" : [NSString stringWithFormat: @"%f", CGRectGetHeight(adViewFrame)]}];
  1236. }
  1237. - (void)destroyAdViewWithAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat
  1238. {
  1239. max_unity_dispatch_on_main_thread(^{
  1240. [self log: @"Destroying %@ with ad unit identifier \"%@\"", adFormat, adUnitIdentifier];
  1241. MAAdView *view = [self retrieveAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
  1242. view.delegate = nil;
  1243. view.revenueDelegate = nil;
  1244. view.adReviewDelegate = nil;
  1245. [view removeFromSuperview];
  1246. [self.adViews removeObjectForKey: adUnitIdentifier];
  1247. [self.adViewAdFormats removeObjectForKey: adUnitIdentifier];
  1248. [self.adViewPositions removeObjectForKey: adUnitIdentifier];
  1249. [self.adViewOffsets removeObjectForKey: adUnitIdentifier];
  1250. [self.adViewWidths removeObjectForKey: adUnitIdentifier];
  1251. [self.verticalAdViewFormats removeObjectForKey: adUnitIdentifier];
  1252. [self.disabledAdaptiveBannerAdUnitIdentifiers removeObject: adUnitIdentifier];
  1253. });
  1254. }
  1255. - (void)logInvalidAdFormat:(MAAdFormat *)adFormat
  1256. {
  1257. [self log: @"invalid ad format: %@, from %@", adFormat, [NSThread callStackSymbols]];
  1258. }
  1259. - (void)log:(NSString *)format, ...
  1260. {
  1261. if (max_unity_should_disable_all_logs()) return;
  1262. va_list valist;
  1263. va_start(valist, format);
  1264. NSString *message = [[NSString alloc] initWithFormat: format arguments: valist];
  1265. va_end(valist);
  1266. NSLog(@"[%@] [%@] %@", SDK_TAG, TAG, message);
  1267. }
  1268. + (void)log:(NSString *)format, ...
  1269. {
  1270. if (max_unity_should_disable_all_logs()) return;
  1271. va_list valist;
  1272. va_start(valist, format);
  1273. NSString *message = [[NSString alloc] initWithFormat: format arguments: valist];
  1274. va_end(valist);
  1275. NSLog(@"[%@] [%@] %@", SDK_TAG, TAG, message);
  1276. }
  1277. - (MAInterstitialAd *)retrieveInterstitialForAdUnitIdentifier:(NSString *)adUnitIdentifier
  1278. {
  1279. MAInterstitialAd *result = self.interstitials[adUnitIdentifier];
  1280. if ( !result )
  1281. {
  1282. result = [[MAInterstitialAd alloc] initWithAdUnitIdentifier: adUnitIdentifier sdk: self.sdk];
  1283. result.delegate = self;
  1284. result.revenueDelegate = self;
  1285. result.adReviewDelegate = self;
  1286. result.expirationDelegate = self;
  1287. self.interstitials[adUnitIdentifier] = result;
  1288. }
  1289. return result;
  1290. }
  1291. - (MAAppOpenAd *)retrieveAppOpenAdForAdUnitIdentifier:(NSString *)adUnitIdentifier
  1292. {
  1293. MAAppOpenAd *result = self.appOpenAds[adUnitIdentifier];
  1294. if ( !result )
  1295. {
  1296. result = [[MAAppOpenAd alloc] initWithAdUnitIdentifier: adUnitIdentifier sdk: self.sdk];
  1297. result.delegate = self;
  1298. result.revenueDelegate = self;
  1299. result.expirationDelegate = self;
  1300. self.appOpenAds[adUnitIdentifier] = result;
  1301. }
  1302. return result;
  1303. }
  1304. - (MARewardedAd *)retrieveRewardedAdForAdUnitIdentifier:(NSString *)adUnitIdentifier
  1305. {
  1306. MARewardedAd *result = self.rewardedAds[adUnitIdentifier];
  1307. if ( !result )
  1308. {
  1309. result = [MARewardedAd sharedWithAdUnitIdentifier: adUnitIdentifier sdk: self.sdk];
  1310. result.delegate = self;
  1311. result.revenueDelegate = self;
  1312. result.adReviewDelegate = self;
  1313. result.expirationDelegate = self;
  1314. self.rewardedAds[adUnitIdentifier] = result;
  1315. }
  1316. return result;
  1317. }
  1318. - (MAAdView *)retrieveAdViewForAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat
  1319. {
  1320. return [self retrieveAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat atPosition: nil withOffset: CGPointZero];
  1321. }
  1322. - (MAAdView *)retrieveAdViewForAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat atPosition:(NSString *)adViewPosition withOffset:(CGPoint)offset
  1323. {
  1324. MAAdView *result = self.adViews[adUnitIdentifier];
  1325. if ( !result && adViewPosition )
  1326. {
  1327. result = [[MAAdView alloc] initWithAdUnitIdentifier: adUnitIdentifier adFormat: adFormat sdk: self.sdk];
  1328. // There is a Unity bug where if an empty UIView is on screen with user interaction enabled, and a user interacts with it, it just passes the events to random parts of the screen.
  1329. result.userInteractionEnabled = NO;
  1330. result.translatesAutoresizingMaskIntoConstraints = NO;
  1331. result.delegate = self;
  1332. result.revenueDelegate = self;
  1333. result.adReviewDelegate = self;
  1334. self.adViews[adUnitIdentifier] = result;
  1335. self.adViewPositions[adUnitIdentifier] = adViewPosition;
  1336. self.adViewOffsets[adUnitIdentifier] = [NSValue valueWithCGPoint: offset];
  1337. UIViewController *rootViewController = [self unityViewController];
  1338. [rootViewController.view addSubview: result];
  1339. // Allow pubs to pause auto-refresh immediately, by default.
  1340. [result setExtraParameterForKey: @"allow_pause_auto_refresh_immediately" value: @"true"];
  1341. }
  1342. return result;
  1343. }
  1344. - (void)positionAdViewForAd:(MAAd *)ad
  1345. {
  1346. [self positionAdViewForAdUnitIdentifier: ad.adUnitIdentifier adFormat: ad.format];
  1347. }
  1348. - (void)positionAdViewForAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat
  1349. {
  1350. max_unity_dispatch_on_main_thread(^{
  1351. MAAdView *adView = [self retrieveAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
  1352. NSString *adViewPosition = self.adViewPositions[adUnitIdentifier];
  1353. NSValue *adViewPositionValue = self.adViewOffsets[adUnitIdentifier];
  1354. CGPoint adViewOffset = [adViewPositionValue CGPointValue];
  1355. BOOL isAdaptiveBannerDisabled = [self.disabledAdaptiveBannerAdUnitIdentifiers containsObject: adUnitIdentifier];
  1356. BOOL isWidthPtsOverridden = self.adViewWidths[adUnitIdentifier] != nil;
  1357. UIView *superview = adView.superview;
  1358. if ( !superview ) return;
  1359. // Deactivate any previous constraints and reset rotation so that the banner can be positioned again.
  1360. NSArray<NSLayoutConstraint *> *activeConstraints = self.adViewConstraints[adUnitIdentifier];
  1361. [NSLayoutConstraint deactivateConstraints: activeConstraints];
  1362. adView.transform = CGAffineTransformIdentity;
  1363. [self.verticalAdViewFormats removeObjectForKey: adUnitIdentifier];
  1364. // Ensure superview contains the safe area background.
  1365. if ( ![superview.subviews containsObject: self.safeAreaBackground] )
  1366. {
  1367. [self.safeAreaBackground removeFromSuperview];
  1368. [superview insertSubview: self.safeAreaBackground belowSubview: adView];
  1369. }
  1370. // Deactivate any previous constraints and reset visibility state so that the safe area background can be positioned again.
  1371. [NSLayoutConstraint deactivateConstraints: self.safeAreaBackground.constraints];
  1372. self.safeAreaBackground.hidden = adView.hidden;
  1373. //
  1374. // Determine ad width
  1375. //
  1376. CGFloat adViewWidth;
  1377. // Check if publisher has overridden width as points
  1378. if ( isWidthPtsOverridden )
  1379. {
  1380. adViewWidth = self.adViewWidths[adUnitIdentifier].floatValue;
  1381. }
  1382. // Top center / bottom center stretches full screen
  1383. else if ( [adViewPosition isEqual: @"top_center"] || [adViewPosition isEqual: @"bottom_center"] )
  1384. {
  1385. adViewWidth = CGRectGetWidth(KEY_WINDOW.bounds);
  1386. }
  1387. // Else use standard widths of 320, 728, or 300
  1388. else
  1389. {
  1390. adViewWidth = adFormat.size.width;
  1391. }
  1392. //
  1393. // Determine ad height
  1394. //
  1395. CGFloat adViewHeight;
  1396. if ( (adFormat == MAAdFormat.banner || adFormat == MAAdFormat.leader) && !isAdaptiveBannerDisabled )
  1397. {
  1398. adViewHeight = [adFormat adaptiveSizeForWidth: adViewWidth].height;
  1399. }
  1400. else
  1401. {
  1402. adViewHeight = adFormat.size.height;
  1403. }
  1404. CGSize adViewSize = CGSizeMake(adViewWidth, adViewHeight);
  1405. // All positions have constant height
  1406. NSMutableArray<NSLayoutConstraint *> *constraints = [NSMutableArray arrayWithObject: [adView.heightAnchor constraintEqualToConstant: adViewSize.height]];
  1407. UILayoutGuide *layoutGuide = superview.safeAreaLayoutGuide;
  1408. BOOL shouldIgnoreSafeArea = [self shouldIgnoreSafeAreaForAdUnitIdentifier: adUnitIdentifier];
  1409. NSLayoutAnchor *topAnchor = shouldIgnoreSafeArea ? superview.topAnchor : layoutGuide.topAnchor;
  1410. if ( [adViewPosition isEqual: @"top_center"] || [adViewPosition isEqual: @"bottom_center"] )
  1411. {
  1412. // Non AdMob banners will still be of 50/90 points tall. Set the auto sizing mask such that the inner ad view is pinned to the bottom or top according to the ad view position.
  1413. if ( !isAdaptiveBannerDisabled )
  1414. {
  1415. adView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
  1416. if ( [@"top_center" isEqual: adViewPosition] )
  1417. {
  1418. adView.autoresizingMask |= UIViewAutoresizingFlexibleBottomMargin;
  1419. }
  1420. else // bottom_center
  1421. {
  1422. adView.autoresizingMask |= UIViewAutoresizingFlexibleTopMargin;
  1423. }
  1424. }
  1425. // If publisher actually provided a banner background color
  1426. if ( self.publisherBannerBackgroundColor && adFormat != MAAdFormat.mrec )
  1427. {
  1428. if ( isWidthPtsOverridden )
  1429. {
  1430. [constraints addObjectsFromArray: @[[adView.widthAnchor constraintEqualToConstant: adViewWidth],
  1431. [adView.centerXAnchor constraintEqualToAnchor: layoutGuide.centerXAnchor],
  1432. [self.safeAreaBackground.widthAnchor constraintEqualToConstant: adViewWidth],
  1433. [self.safeAreaBackground.centerXAnchor constraintEqualToAnchor: layoutGuide.centerXAnchor]]];
  1434. if ( [adViewPosition isEqual: @"top_center"] )
  1435. {
  1436. [constraints addObjectsFromArray: @[[adView.topAnchor constraintEqualToAnchor: topAnchor],
  1437. [self.safeAreaBackground.topAnchor constraintEqualToAnchor: superview.topAnchor],
  1438. [self.safeAreaBackground.bottomAnchor constraintEqualToAnchor: adView.topAnchor]]];
  1439. }
  1440. else // bottom_center
  1441. {
  1442. [constraints addObjectsFromArray: @[[adView.bottomAnchor constraintEqualToAnchor: layoutGuide.bottomAnchor],
  1443. [self.safeAreaBackground.topAnchor constraintEqualToAnchor: adView.bottomAnchor],
  1444. [self.safeAreaBackground.bottomAnchor constraintEqualToAnchor: superview.bottomAnchor]]];
  1445. }
  1446. }
  1447. else
  1448. {
  1449. [constraints addObjectsFromArray: @[[adView.leftAnchor constraintEqualToAnchor: superview.leftAnchor],
  1450. [adView.rightAnchor constraintEqualToAnchor: superview.rightAnchor],
  1451. [self.safeAreaBackground.leftAnchor constraintEqualToAnchor: superview.leftAnchor],
  1452. [self.safeAreaBackground.rightAnchor constraintEqualToAnchor: superview.rightAnchor]]];
  1453. if ( [adViewPosition isEqual: @"top_center"] )
  1454. {
  1455. [constraints addObjectsFromArray: @[[adView.topAnchor constraintEqualToAnchor: topAnchor],
  1456. [self.safeAreaBackground.topAnchor constraintEqualToAnchor: superview.topAnchor],
  1457. [self.safeAreaBackground.bottomAnchor constraintEqualToAnchor: adView.topAnchor]]];
  1458. }
  1459. else // bottom_center
  1460. {
  1461. [constraints addObjectsFromArray: @[[adView.bottomAnchor constraintEqualToAnchor: layoutGuide.bottomAnchor],
  1462. [self.safeAreaBackground.topAnchor constraintEqualToAnchor: adView.bottomAnchor],
  1463. [self.safeAreaBackground.bottomAnchor constraintEqualToAnchor: superview.bottomAnchor]]];
  1464. }
  1465. }
  1466. }
  1467. // If pub does not have a background color set or this is not a banner
  1468. else
  1469. {
  1470. self.safeAreaBackground.hidden = YES;
  1471. [constraints addObjectsFromArray: @[[adView.widthAnchor constraintEqualToConstant: adViewWidth],
  1472. [adView.centerXAnchor constraintEqualToAnchor: layoutGuide.centerXAnchor]]];
  1473. if ( [adViewPosition isEqual: @"top_center"] )
  1474. {
  1475. [constraints addObject: [adView.topAnchor constraintEqualToAnchor: topAnchor]];
  1476. }
  1477. else // BottomCenter
  1478. {
  1479. [constraints addObject: [adView.bottomAnchor constraintEqualToAnchor: layoutGuide.bottomAnchor]];
  1480. }
  1481. }
  1482. }
  1483. // Check if the publisher wants vertical banners.
  1484. else if ( [adViewPosition isEqual: @"center_left"] || [adViewPosition isEqual: @"center_right"] )
  1485. {
  1486. if ( MAAdFormat.mrec == adFormat )
  1487. {
  1488. [constraints addObject: [adView.widthAnchor constraintEqualToConstant: adViewSize.width]];
  1489. if ( [adViewPosition isEqual: @"center_left"] )
  1490. {
  1491. [constraints addObjectsFromArray: @[[adView.centerYAnchor constraintEqualToAnchor: layoutGuide.centerYAnchor],
  1492. [adView.leftAnchor constraintEqualToAnchor: superview.leftAnchor]]];
  1493. [constraints addObjectsFromArray: @[[self.safeAreaBackground.rightAnchor constraintEqualToAnchor: layoutGuide.leftAnchor],
  1494. [self.safeAreaBackground.leftAnchor constraintEqualToAnchor: superview.leftAnchor]]];
  1495. }
  1496. else // center_right
  1497. {
  1498. [constraints addObjectsFromArray: @[[adView.centerYAnchor constraintEqualToAnchor: layoutGuide.centerYAnchor],
  1499. [adView.rightAnchor constraintEqualToAnchor: superview.rightAnchor]]];
  1500. [constraints addObjectsFromArray: @[[self.safeAreaBackground.leftAnchor constraintEqualToAnchor: layoutGuide.rightAnchor],
  1501. [self.safeAreaBackground.rightAnchor constraintEqualToAnchor: superview.rightAnchor]]];
  1502. }
  1503. }
  1504. else
  1505. {
  1506. /* Align the center of the view such that when rotated it snaps into place.
  1507. *
  1508. * +---+---+-------+
  1509. * | | |
  1510. * | | |
  1511. * | | |
  1512. * | | |
  1513. * | | |
  1514. * | | |
  1515. * +-------------+---+-----------+--+
  1516. * | | + | + | |
  1517. * +-------------+---+-----------+--+
  1518. * <+> | |
  1519. * |+ | |
  1520. * || | |
  1521. * || | |
  1522. * || | |
  1523. * || | |
  1524. * +|--+-----------+
  1525. * v
  1526. * Banner Half Height
  1527. */
  1528. self.safeAreaBackground.hidden = YES;
  1529. adView.transform = CGAffineTransformRotate(CGAffineTransformIdentity, M_PI_2);
  1530. CGFloat width;
  1531. // If the publiser has a background color set - set the width to the height of the screen, to span the ad across the screen after it is rotated.
  1532. if ( self.publisherBannerBackgroundColor )
  1533. {
  1534. if ( isWidthPtsOverridden )
  1535. {
  1536. width = adViewWidth;
  1537. }
  1538. else
  1539. {
  1540. width = CGRectGetHeight(KEY_WINDOW.bounds);
  1541. }
  1542. }
  1543. // Otherwise - we shouldn't span the banner the width of the realm (there might be user-interactable UI on the sides)
  1544. else
  1545. {
  1546. width = adViewWidth;
  1547. }
  1548. [constraints addObject: [adView.widthAnchor constraintEqualToConstant: width]];
  1549. // Set constraints such that the center of the banner aligns with the center left or right as needed. That way, once rotated, the banner snaps into place.
  1550. [constraints addObject: [adView.centerYAnchor constraintEqualToAnchor: superview.centerYAnchor]];
  1551. // Place the center of the banner half the height of the banner away from the side. If we align the center exactly with the left/right anchor, only half the banner will be visible.
  1552. CGFloat bannerHalfHeight = adViewSize.height / 2.0;
  1553. UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
  1554. if ( [adViewPosition isEqual: @"center_left"] )
  1555. {
  1556. NSLayoutAnchor *anchor = ( orientation == UIInterfaceOrientationLandscapeRight ) ? layoutGuide.leftAnchor : superview.leftAnchor;
  1557. [constraints addObject: [adView.centerXAnchor constraintEqualToAnchor: anchor constant: bannerHalfHeight]];
  1558. }
  1559. else // CenterRight
  1560. {
  1561. NSLayoutAnchor *anchor = ( orientation == UIInterfaceOrientationLandscapeLeft ) ? layoutGuide.rightAnchor : superview.rightAnchor;
  1562. [constraints addObject: [adView.centerXAnchor constraintEqualToAnchor: anchor constant: -bannerHalfHeight]];
  1563. }
  1564. // Store the ad view with format, so that it can be updated when the orientation changes.
  1565. self.verticalAdViewFormats[adUnitIdentifier] = adFormat;
  1566. // If adaptive - make top flexible since we anchor with the bottom of the banner at the edge of the screen
  1567. if ( !isAdaptiveBannerDisabled )
  1568. {
  1569. adView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
  1570. }
  1571. }
  1572. }
  1573. // Otherwise, publisher will likely construct his own views around the adview
  1574. else
  1575. {
  1576. self.safeAreaBackground.hidden = YES;
  1577. [constraints addObject: [adView.widthAnchor constraintEqualToConstant: adViewWidth]];
  1578. if ( [adViewPosition isEqual: @"top_left"] )
  1579. {
  1580. [constraints addObjectsFromArray: @[[adView.leftAnchor constraintEqualToAnchor: superview.leftAnchor constant: adViewOffset.x],
  1581. [adView.topAnchor constraintEqualToAnchor: topAnchor constant: adViewOffset.y]]];
  1582. }
  1583. else if ( [adViewPosition isEqual: @"top_right"] )
  1584. {
  1585. [constraints addObjectsFromArray: @[[adView.topAnchor constraintEqualToAnchor: topAnchor],
  1586. [adView.rightAnchor constraintEqualToAnchor: superview.rightAnchor]]];
  1587. }
  1588. else if ( [adViewPosition isEqual: @"centered"] )
  1589. {
  1590. [constraints addObjectsFromArray: @[[adView.centerXAnchor constraintEqualToAnchor: layoutGuide.centerXAnchor],
  1591. [adView.centerYAnchor constraintEqualToAnchor: layoutGuide.centerYAnchor]]];
  1592. }
  1593. else if ( [adViewPosition isEqual: @"bottom_left"] )
  1594. {
  1595. [constraints addObjectsFromArray: @[[adView.bottomAnchor constraintEqualToAnchor: layoutGuide.bottomAnchor],
  1596. [adView.leftAnchor constraintEqualToAnchor: superview.leftAnchor]]];
  1597. }
  1598. else if ( [adViewPosition isEqual: @"bottom_right"] )
  1599. {
  1600. [constraints addObjectsFromArray: @[[adView.bottomAnchor constraintEqualToAnchor: layoutGuide.bottomAnchor],
  1601. [adView.rightAnchor constraintEqualToAnchor: superview.rightAnchor]]];
  1602. }
  1603. }
  1604. self.adViewConstraints[adUnitIdentifier] = constraints;
  1605. [NSLayoutConstraint activateConstraints: constraints];
  1606. });
  1607. }
  1608. - (UIViewController *)unityViewController
  1609. {
  1610. // Handle edge case where `UnityGetGLViewController()` returns nil
  1611. return UnityGetGLViewController() ?: UnityGetMainWindow().rootViewController ?: [KEY_WINDOW rootViewController];
  1612. }
  1613. - (void)forwardUnityEventWithArgs:(NSDictionary<NSString *, id> *)args
  1614. {
  1615. #if !IS_TEST_APP
  1616. extern bool _didResignActive;
  1617. // We should not call any script callbacks when application is not active. Suspend the callback queue if resign is active.
  1618. // We'll resume the queue once the application becomes active again.
  1619. self.backgroundCallbackEventsQueue.suspended = _didResignActive;
  1620. #endif
  1621. [self.backgroundCallbackEventsQueue addOperationWithBlock:^{
  1622. NSString *serializedParameters = [MAUnityAdManager serializeParameters: args];
  1623. backgroundCallback(serializedParameters.UTF8String);
  1624. }];
  1625. }
  1626. + (NSString *)serializeParameters:(NSDictionary<NSString *, id> *)dict
  1627. {
  1628. NSData *jsonData = [NSJSONSerialization dataWithJSONObject: dict options: 0 error: nil];
  1629. return [[NSString alloc] initWithData: jsonData encoding: NSUTF8StringEncoding];
  1630. }
  1631. + (NSDictionary<NSString *, id> *)deserializeParameters:(nullable NSString *)serialized
  1632. {
  1633. if ( serialized.length > 0 )
  1634. {
  1635. NSError *error;
  1636. NSDictionary<NSString *, id> *deserialized = [NSJSONSerialization JSONObjectWithData: [serialized dataUsingEncoding:NSUTF8StringEncoding]
  1637. options: 0
  1638. error: &error];
  1639. if ( error )
  1640. {
  1641. [self log: @"Failed to deserialize (%@) with error %@", serialized, error];
  1642. return @{};
  1643. }
  1644. return deserialized;
  1645. }
  1646. else
  1647. {
  1648. return @{};
  1649. }
  1650. }
  1651. - (MAAdFormat *)adViewAdFormatForAdUnitIdentifier:(NSString *)adUnitIdentifier
  1652. {
  1653. if ( self.adViewAdFormats[adUnitIdentifier] )
  1654. {
  1655. return self.adViewAdFormats[adUnitIdentifier];
  1656. }
  1657. else
  1658. {
  1659. return DEVICE_SPECIFIC_ADVIEW_AD_FORMAT;
  1660. }
  1661. }
  1662. - (NSString *)requestLatencyMillisFromRequestLatency:(NSTimeInterval)requestLatency
  1663. {
  1664. // Convert latency from seconds to milliseconds to match Android.
  1665. long requestLatencyMillis = requestLatency * 1000;
  1666. return @(requestLatencyMillis).stringValue;
  1667. }
  1668. #pragma mark - User Service
  1669. - (void)didDismissUserConsentDialog
  1670. {
  1671. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  1672. [self forwardUnityEventWithArgs: @{@"name" : @"OnSdkConsentDialogDismissedEvent"}];
  1673. });
  1674. }
  1675. #pragma mark - CMP Service
  1676. - (void)showCMPForExistingUser
  1677. {
  1678. [self.sdk.cmpService showCMPForExistingUserWithCompletion:^(ALCMPError * _Nullable error) {
  1679. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  1680. NSMutableDictionary<NSString *, id> *args = [NSMutableDictionary dictionaryWithCapacity: 2];
  1681. args[@"name"] = @"OnCmpCompletedEvent";
  1682. if ( error )
  1683. {
  1684. args[@"error"] = @{@"code": @(error.code),
  1685. @"message": error.message,
  1686. @"cmpCode": @(error.cmpCode),
  1687. @"cmpMessage": error.cmpMessage,
  1688. @"keepInBackground": @(YES)};
  1689. }
  1690. [self forwardUnityEventWithArgs: args];
  1691. });
  1692. }];
  1693. }
  1694. #pragma mark - Application
  1695. - (void)applicationPaused:(NSNotification *)notification
  1696. {
  1697. [self notifyApplicationStateChangedEventForPauseState: YES];
  1698. }
  1699. - (void)applicationResumed:(NSNotification *)notification
  1700. {
  1701. [self notifyApplicationStateChangedEventForPauseState: NO];
  1702. }
  1703. - (void)notifyApplicationStateChangedEventForPauseState:(BOOL)isPaused
  1704. {
  1705. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  1706. [self forwardUnityEventWithArgs: @{@"name": @"OnApplicationStateChanged",
  1707. @"isPaused": @(isPaused)}];
  1708. });
  1709. }
  1710. - (MAAd *)adWithAdUnitIdentifier:(NSString *)adUnitIdentifier
  1711. {
  1712. @synchronized ( self.adInfoDictLock )
  1713. {
  1714. return self.adInfoDict[adUnitIdentifier];
  1715. }
  1716. }
  1717. #pragma mark - Helper
  1718. - (BOOL)shouldIgnoreSafeAreaForAdUnitIdentifier:(NSString *)adUnitIdentifier
  1719. {
  1720. if ( ![self.ignoreSafeAreaLandscapeAdUnitIdentifiers containsObject: adUnitIdentifier] ) return NO;
  1721. // We should use the superview instead of layout guide if the application's orientation is landscape and the extra parameter is set
  1722. UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
  1723. return orientation == UIInterfaceOrientationLandscapeRight || orientation == UIInterfaceOrientationLandscapeLeft;
  1724. }
  1725. @end