429 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Swift
		
	
	
		
		
			
		
	
	
			429 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Swift
		
	
	
|  | // DefaultBannerAdEngine.swift | ||
|  | // Default implementation of BannerAdEngine | ||
|  | // Corresponds to DefaultBannerAdEngine.kt in Android implementation | ||
|  | 
 | ||
|  | import Foundation | ||
|  | import UIKit | ||
|  | 
 | ||
|  | public class DefaultBannerAdEngine: BannerAdEngine, OrientationAware { | ||
|  |      | ||
|  |     private let ad: GuruBannerAd | ||
|  |      | ||
|  |     // Constants | ||
|  |     private enum Action { | ||
|  |         static let LOAD = 1 | ||
|  |         static let SHOW = 2 | ||
|  |         static let HIDE = 3 | ||
|  |         static let DESTROY = 4 | ||
|  |         static let RETRY = 5 | ||
|  |     } | ||
|  |      | ||
|  |     private enum Event { | ||
|  |         static let ON_LOADED = 100 | ||
|  |         static let ON_LOAD_FAILED = 101 | ||
|  |         static let ON_DISPLAYED = 102 | ||
|  |         static let ON_DISPLAY_FAILED = 103 | ||
|  |         static let ON_CLICK = 104 | ||
|  |         static let ON_HIDDEN = 105 | ||
|  |         static let ON_USER_REWARDED = 106 | ||
|  |         static let REVENUE_PAID = 107 | ||
|  |         static let LOAD_TIMEOUT = 108 | ||
|  |         static let SHOW_TIMEOUT = 109 | ||
|  |     } | ||
|  |      | ||
|  |     private static let LOAD_TIMEOUT_MILLIS: TimeInterval = 120000 / 1000.0 | ||
|  |     private static let SHOW_TIMEOUT_MILLIS: TimeInterval = 120000 / 1000.0 | ||
|  |      | ||
|  |     public override var supportedAdPlatforms: Set<AdPlatform> { | ||
|  |         return [ad.adPlatform] | ||
|  |     } | ||
|  |      | ||
|  |     public init(viewController: UIViewController, id: Int, ad: GuruBannerAd) { | ||
|  |         self.ad = ad | ||
|  |         super.init(viewController: viewController, id: id, adType: .Banner, strategyName: ad.adPlatform.name) | ||
|  |         setupAdListener() | ||
|  |         initialize() | ||
|  |     } | ||
|  |      | ||
|  |     private func setupAdListener() { | ||
|  |         ad.listener = FusionAdListenerImpl { [weak self] event in | ||
|  |             guard let self = self else { return } | ||
|  |              | ||
|  |             switch event { | ||
|  |             case .onAdLoaded(let ad): | ||
|  |                 self.sendMessage(what: Event.ON_LOADED, obj: EventParams(ad: ad)) | ||
|  |                  | ||
|  |             case .onAdDisplayed(let ad): | ||
|  |                 // Not used for banner ads, but handled for completeness | ||
|  |                 break | ||
|  |                  | ||
|  |             case .onAdHidden(let ad): | ||
|  |                 // Not used for banner ads, but handled for completeness | ||
|  |                 break | ||
|  |                  | ||
|  |             case .onUserRewarded(let ad, let reward): | ||
|  |                 // Not used for banner ads, but handled for completeness | ||
|  |                 break | ||
|  |                  | ||
|  |             case .onAdClicked(let ad): | ||
|  |                 self.sendMessage(what: Event.ON_CLICK, obj: EventParams(ad: ad)) | ||
|  |                  | ||
|  |             case .onAdLoadFailed(let loadFailedInfo): | ||
|  |                 self.sendMessage(what: Event.ON_LOAD_FAILED, obj: EventParams(loadFailedInfo: loadFailedInfo)) | ||
|  |                  | ||
|  |             case .onAdDisplayFailed(let ad, let error): | ||
|  |                 self.sendMessage(what: Event.ON_DISPLAY_FAILED, obj: EventParams(ad: ad, displayFailedInfo: error)) | ||
|  |                  | ||
|  |             case .onAdRevenuePaid(let ad): | ||
|  |                 self.sendMessage(what: Event.REVENUE_PAID, obj: EventParams(ad: ad)) | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  |      | ||
|  |     // MARK: - State Classes | ||
|  |      | ||
|  |     public override func createIdleState() -> Idle { | ||
|  |         return IdleImpl(self) | ||
|  |     } | ||
|  |      | ||
|  |     public override func createLoadingState() -> Loading { | ||
|  |         return LoadingImpl(self) | ||
|  |     } | ||
|  |      | ||
|  |     public override func createActiveState() -> Active { | ||
|  |         return ActiveImpl(self) | ||
|  |     } | ||
|  |      | ||
|  |     public override func createShowingState() -> Showing { | ||
|  |         return ShowingImpl(self) | ||
|  |     } | ||
|  |      | ||
|  |     public override func createHiddenState() -> Hidden { | ||
|  |         return HiddenImpl(self) | ||
|  |     } | ||
|  |      | ||
|  |     // MARK: - Request Methods | ||
|  |      | ||
|  |     override internal func requestLoad() { | ||
|  |         sendMessage(what: Action.LOAD) | ||
|  |     } | ||
|  |      | ||
|  |     override internal func requestShow(_ request: BannerShowRequest? = nil) { | ||
|  |         sendMessage(what: Action.SHOW, obj: request) | ||
|  |     } | ||
|  |      | ||
|  |     override internal func requestHide() { | ||
|  |         sendMessage(what: Action.HIDE) | ||
|  |     } | ||
|  |      | ||
|  |     override internal func requestDestroy() { | ||
|  |         sendMessage(what: Action.DESTROY) | ||
|  |     } | ||
|  |      | ||
|  |     // MARK: - Helper Methods | ||
|  |      | ||
|  |     private func handleUnhandledMessage(_ msg: Message) -> Bool { | ||
|  |         switch msg.what { | ||
|  |         case Action.DESTROY: | ||
|  |             ad.destroy() | ||
|  |             transitionTo(idleState) | ||
|  |             return true | ||
|  |         default: | ||
|  |             return false | ||
|  |         } | ||
|  |     } | ||
|  |      | ||
|  |     // MARK: - State Implementations | ||
|  |      | ||
|  |     class IdleImpl: Idle { | ||
|  |         private let engine: DefaultBannerAdEngine | ||
|  |          | ||
|  |         init(_ engine: DefaultBannerAdEngine) { | ||
|  |             self.engine = engine | ||
|  |             super.init() | ||
|  |         } | ||
|  |         public override func enter(from: IState?, params: Any?) { | ||
|  |             super.enter(from: from, params: params) | ||
|  |         } | ||
|  |          | ||
|  |         public override func processMessage(_ msg: Message) -> Bool { | ||
|  |              | ||
|  |             switch msg.what { | ||
|  |             case Action.LOAD: | ||
|  |                 if engine.ad.load() { | ||
|  |                     engine.transitionTo(engine.loadingState) | ||
|  |                 } else { | ||
|  |                     engine.adLoadFailed(loadFailedInfo: LoadFailedInfo( | ||
|  |                         engineId: engine.id, | ||
|  |                         adPlatform: engine.ad.adPlatform, | ||
|  |                         adType: engine.adType, | ||
|  |                         adUnitId: engine.ad.adUnitId, | ||
|  |                         error: LoadAdRequestError() | ||
|  |                     )) | ||
|  |                 } | ||
|  |                 return true | ||
|  |             default: | ||
|  |                 return engine.handleUnhandledMessage(msg) || super.processMessage(msg) | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  |      | ||
|  |     class LoadingImpl: Loading { | ||
|  |         private var retryCount = 0 | ||
|  |         private let engine: DefaultBannerAdEngine | ||
|  |          | ||
|  |         init(_ engine: DefaultBannerAdEngine) { | ||
|  |             self.engine = engine | ||
|  |             super.init() | ||
|  |         } | ||
|  |          | ||
|  |         public override func enter(from: IState?, params: Any?) { | ||
|  |             super.enter(from: from, params: params) | ||
|  |             retryCount = 0 | ||
|  |              | ||
|  |             // Set load timeout | ||
|  |             engine.sendMessageDelayed(what: Event.LOAD_TIMEOUT, delayMillis: Int(DefaultBannerAdEngine.LOAD_TIMEOUT_MILLIS * 1000)) | ||
|  |         } | ||
|  |          | ||
|  |         public override func exit(to: IState?) { | ||
|  |             engine.removeMessages(what: Event.LOAD_TIMEOUT) | ||
|  |             engine.removeMessages(what: Action.RETRY) | ||
|  |             retryCount = 0 | ||
|  |             super.exit(to: to) | ||
|  |         } | ||
|  |          | ||
|  |         public override func processMessage(_ msg: Message) -> Bool { | ||
|  |              | ||
|  |             switch msg.what { | ||
|  |             case Action.RETRY: | ||
|  |                 retryCount += 1 | ||
|  |                 if !engine.ad.load() { | ||
|  |                     engine.sendMessage(what: Event.ON_LOAD_FAILED, obj: EventParams( | ||
|  |                         loadFailedInfo: LoadFailedInfo( | ||
|  |                             engineId: engine.id, | ||
|  |                             adPlatform: engine.ad.adPlatform, | ||
|  |                             adType: engine.adType, | ||
|  |                             adUnitId: engine.ad.adUnitId, | ||
|  |                             error: LoadAdRequestError() | ||
|  |                         ) | ||
|  |                     )) | ||
|  |                 } else { | ||
|  |                     engine.sendMessageDelayed(what: Event.LOAD_TIMEOUT, delayMillis: Int(DefaultBannerAdEngine.LOAD_TIMEOUT_MILLIS * 1000)) | ||
|  |                 } | ||
|  |                 return true | ||
|  |                  | ||
|  |             case Action.LOAD: | ||
|  |                 engine.logWarn("Already loading! Ignore load request") | ||
|  |                 return true | ||
|  |                  | ||
|  |             case Event.LOAD_TIMEOUT: | ||
|  |                 engine.removeMessages(what: Action.RETRY) | ||
|  |                 engine.sendMessage(what: Action.RETRY) | ||
|  |                 return true | ||
|  |                  | ||
|  |             case Event.ON_LOADED: | ||
|  |                 if let eventParams = msg.obj as? EventParams, eventParams.ad != nil { | ||
|  |                     engine.transitionTo(engine.hiddenState, params: eventParams) | ||
|  |                 } | ||
|  |                 engine.deferMessage(msg) | ||
|  |                 return true | ||
|  |                  | ||
|  |             case Event.ON_LOAD_FAILED: | ||
|  |                 let params = msg.obj as? EventParams | ||
|  |                 engine.adLoadFailed(loadFailedInfo: params?.loadFailedInfo ?? LoadFailedInfo( | ||
|  |                     engineId: engine.id, | ||
|  |                     adPlatform: engine.ad.adPlatform, | ||
|  |                     adType: engine.adType, | ||
|  |                     adUnitId: engine.ad.adUnitId, | ||
|  |                     error: LoadAdRequestError() | ||
|  |                 )) | ||
|  |                 engine.transitionTo(engine.idleState) | ||
|  |                 return true | ||
|  |                  | ||
|  |             default: | ||
|  |                 return engine.handleUnhandledMessage(msg) || super.processMessage(msg) | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  |      | ||
|  |     class ActiveImpl: Active { | ||
|  |         private let engine: DefaultBannerAdEngine | ||
|  |          | ||
|  |         init(_ engine: DefaultBannerAdEngine) { | ||
|  |             self.engine = engine | ||
|  |             super.init() | ||
|  |         } | ||
|  |          | ||
|  |          | ||
|  |         public override func enter(from: IState?, params: Any?) { | ||
|  |             super.enter(from: from, params: params) | ||
|  |         } | ||
|  |          | ||
|  |         public override func processMessage(_ msg: Message) -> Bool { | ||
|  |              | ||
|  |             switch msg.what { | ||
|  |             case Event.ON_CLICK: | ||
|  |                 if let eventParams = msg.obj as? EventParams, let ad = eventParams.ad { | ||
|  |                     engine.adClicked(ad: ad) | ||
|  |                 } | ||
|  |                 return true | ||
|  |                  | ||
|  |             case Event.REVENUE_PAID: | ||
|  |                 if let eventParams = msg.obj as? EventParams, let ad = eventParams.ad { | ||
|  |                     engine.adRevenuePaid(ad: ad) | ||
|  |                 } | ||
|  |                 return true | ||
|  |                  | ||
|  |             default: | ||
|  |                 return engine.handleUnhandledMessage(msg) || super.processMessage(msg) | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  |      | ||
|  |     class ShowingImpl: Showing { | ||
|  |         private let engine: DefaultBannerAdEngine | ||
|  |          | ||
|  |         init(_ engine: DefaultBannerAdEngine) { | ||
|  |             self.engine = engine | ||
|  |             super.init() | ||
|  |         } | ||
|  |          | ||
|  |         public override func enter(from: IState?, params: Any?) { | ||
|  |             super.enter(from: from, params: params) | ||
|  |              | ||
|  |             if let eventParams = params as? EventParams, let ad = eventParams.ad { | ||
|  |                 engine.adDisplayed(ad: ad) | ||
|  |             } | ||
|  |         } | ||
|  |          | ||
|  |         public override func processMessage(_ msg: Message) -> Bool { | ||
|  |              | ||
|  |             switch msg.what { | ||
|  |             case Action.HIDE: | ||
|  |                 if engine.ad.hide() { | ||
|  |                     engine.transitionTo(engine.hiddenState) | ||
|  |                 } else { | ||
|  |                     engine.logWarn("Failed to hide banner") | ||
|  |                 } | ||
|  |                 return true | ||
|  |                  | ||
|  |             case Action.SHOW: | ||
|  |                 engine.logWarn("Already showing! Ignore show request") | ||
|  |                 return true | ||
|  |                  | ||
|  |             case Event.ON_DISPLAY_FAILED: | ||
|  |                 if let eventParams = msg.obj as? EventParams, let ad = eventParams.ad { | ||
|  |                     engine.adDisplayFailed(ad: ad, error: eventParams.displayFailedInfo) | ||
|  |                 } | ||
|  |                 engine.transitionTo(engine.hiddenState) | ||
|  |                 engine.requestLoad() | ||
|  |                 return true | ||
|  |                  | ||
|  |             default: | ||
|  |                 return engine.handleUnhandledMessage(msg) || super.processMessage(msg) | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  |      | ||
|  |     class HiddenImpl: Hidden { | ||
|  |         private let engine: DefaultBannerAdEngine | ||
|  |          | ||
|  |         init(_ engine: DefaultBannerAdEngine) { | ||
|  |             self.engine = engine | ||
|  |             super.init() | ||
|  |         } | ||
|  |          | ||
|  |         public override func enter(from: IState?, params: Any?) { | ||
|  |             super.enter(from: from, params: params) | ||
|  |              | ||
|  |             if let eventParams = params as? EventParams, let ad = eventParams.ad { | ||
|  |                 engine.adLoaded(ad: ad) | ||
|  |             } | ||
|  |         } | ||
|  |          | ||
|  |         public override func processMessage(_ msg: Message) -> Bool { | ||
|  |              | ||
|  |             switch msg.what { | ||
|  |             case Action.SHOW: | ||
|  |                 let request = msg.obj as? BannerShowRequest | ||
|  |                 if engine.ad.show(request) { | ||
|  |                     engine.transitionTo(engine.showingState) | ||
|  |                 } else { | ||
|  |                     engine.logWarn("Failed to show banner") | ||
|  |                     // no FusionAd here | ||
|  | //                    if let eventParams = msg.obj as? EventParams, let ad = eventParams.ad { | ||
|  | //                        engine.adDisplayFailed(ad: ad, error: ShowAdRequestError()) | ||
|  | //                    } | ||
|  |                     if(engine.ad.load()) { | ||
|  |                         engine.transitionTo(engine.loadingState) | ||
|  |                     } | ||
|  |                 } | ||
|  |                 return true | ||
|  |                  | ||
|  |             case Action.HIDE: | ||
|  |                 engine.logWarn("Already hidden! Ignore hide request") | ||
|  |                 return true | ||
|  |                  | ||
|  |             default: | ||
|  |                 return engine.handleUnhandledMessage(msg) || super.processMessage(msg) | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  |      | ||
|  |     func onOrientationUpdate(orientation: ScreenOrientation) { | ||
|  |         ad.updateOrientation(orientation: orientation) | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | // Helper class to implement the FusionAdListener protocol | ||
|  | private class FusionAdListenerImpl: FusionAdListener { | ||
|  |     enum Event { | ||
|  |         case onAdLoaded(ad: FusionAd) | ||
|  |         case onAdDisplayed(ad: FusionAd) | ||
|  |         case onAdHidden(ad: FusionAd) | ||
|  |         case onUserRewarded(ad: FusionAd, reward: FusionReward?) | ||
|  |         case onAdClicked(ad: FusionAd) | ||
|  |         case onAdLoadFailed(loadFailedInfo: LoadFailedInfo) | ||
|  |         case onAdDisplayFailed(ad: FusionAd, error: FusionError?) | ||
|  |         case onAdRevenuePaid(ad: FusionAd) | ||
|  |     } | ||
|  |      | ||
|  |     private let handler: (Event) -> Void | ||
|  |      | ||
|  |     init(handler: @escaping (Event) -> Void) { | ||
|  |         self.handler = handler | ||
|  |     } | ||
|  |      | ||
|  |     func onAdLoaded(ad: FusionAd) { | ||
|  |         handler(.onAdLoaded(ad: ad)) | ||
|  |     } | ||
|  |      | ||
|  |     func onAdDisplayed(ad: FusionAd) { | ||
|  |         handler(.onAdDisplayed(ad: ad)) | ||
|  |     } | ||
|  |      | ||
|  |     func onAdHidden(ad: FusionAd) { | ||
|  |         handler(.onAdHidden(ad: ad)) | ||
|  |     } | ||
|  |      | ||
|  |     func onUserRewarded(ad: FusionAd, reward: FusionReward?) { | ||
|  |         handler(.onUserRewarded(ad: ad, reward: reward)) | ||
|  |     } | ||
|  |      | ||
|  |     func onAdClicked(ad: FusionAd) { | ||
|  |         handler(.onAdClicked(ad: ad)) | ||
|  |     } | ||
|  |      | ||
|  |     func onAdLoadFailed(loadFailedInfo: LoadFailedInfo) { | ||
|  |         handler(.onAdLoadFailed(loadFailedInfo: loadFailedInfo)) | ||
|  |     } | ||
|  |      | ||
|  |     func onAdDisplayFailed(ad: FusionAd, error: FusionError?) { | ||
|  |         handler(.onAdDisplayFailed(ad: ad, error: error)) | ||
|  |     } | ||
|  |      | ||
|  |     func onAdRevenuePaid(ad: FusionAd) { | ||
|  |         handler(.onAdRevenuePaid(ad: ad)) | ||
|  |     } | ||
|  | } |