424 lines
15 KiB
Objective-C
424 lines
15 KiB
Objective-C
/*
|
|
|
|
AdWhirlAdapterCustom.m
|
|
|
|
Copyright 2009 AdMob, Inc.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
#import "AdWhirlAdapterCustom.h"
|
|
#import "AdWhirlView.h"
|
|
#import "AdWhirlLog.h"
|
|
#import "AdWhirlConfig.h"
|
|
#import "AdWhirlAdNetworkConfig.h"
|
|
#import "AdWhirlError.h"
|
|
#import "CJSONDeserializer.h"
|
|
#import "AdWhirlCustomAdView.h"
|
|
#import "AdWhirlAdNetworkAdapter+Helpers.h"
|
|
#import "AdWhirlAdNetworkRegistry.h"
|
|
|
|
@interface AdWhirlAdapterCustom ()
|
|
|
|
- (BOOL)parseAdData:(NSData *)data error:(NSError **)error;
|
|
|
|
@property (nonatomic,readonly) CLLocationManager *locationManager;
|
|
@property (nonatomic,retain) NSURLConnection *adConnection;
|
|
@property (nonatomic,retain) NSURLConnection *imageConnection;
|
|
@property (nonatomic,retain) AdWhirlCustomAdView *adView;
|
|
@property (nonatomic,retain) AdWhirlWebBrowserController *webBrowserController;
|
|
@property (nonatomic, assign) CGFloat scale;
|
|
|
|
@end
|
|
|
|
|
|
@implementation AdWhirlAdapterCustom
|
|
|
|
@synthesize adConnection;
|
|
@synthesize imageConnection;
|
|
@synthesize adView;
|
|
@synthesize webBrowserController;
|
|
@synthesize scale;
|
|
|
|
+ (AdWhirlAdNetworkType)networkType {
|
|
return AdWhirlAdNetworkTypeCustom;
|
|
}
|
|
|
|
+ (void)load {
|
|
[[AdWhirlAdNetworkRegistry sharedRegistry] registerClass:self];
|
|
}
|
|
|
|
- (id)initWithAdWhirlDelegate:(id<AdWhirlDelegate>)delegate
|
|
view:(AdWhirlView *)view
|
|
config:(AdWhirlConfig *)config
|
|
networkConfig:(AdWhirlAdNetworkConfig *)netConf {
|
|
self = [super initWithAdWhirlDelegate:delegate
|
|
view:view
|
|
config:config
|
|
networkConfig:netConf];
|
|
if (self != nil) {
|
|
adData = [[NSMutableData alloc] init];
|
|
imageData = [[NSMutableData alloc] init];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (BOOL)shouldSendExMetric {
|
|
return NO; // since we are getting the ad from the AdWhirl server anyway, no
|
|
// need to send extra metric ping to the same server.
|
|
}
|
|
|
|
- (void)getAd {
|
|
@synchronized(self) {
|
|
if (requesting) return;
|
|
requesting = YES;
|
|
}
|
|
|
|
NSURL *adRequestBaseURL = nil;
|
|
if ([self.adWhirlDelegate respondsToSelector:@selector(adWhirlCustomAdURL)]) {
|
|
adRequestBaseURL = [self.adWhirlDelegate adWhirlCustomAdURL];
|
|
}
|
|
if (adRequestBaseURL == nil) {
|
|
adRequestBaseURL = [NSURL URLWithString:kAdWhirlDefaultCustomAdURL];
|
|
}
|
|
NSString *query;
|
|
if (self.adWhirlConfig.locationOn) {
|
|
AWLogDebug(@"Allow location access in custom ad");
|
|
CLLocation *location;
|
|
if ([self.adWhirlDelegate respondsToSelector:@selector(locationInfo)]) {
|
|
location = [self.adWhirlDelegate locationInfo];
|
|
}
|
|
else {
|
|
location = [self.locationManager location];
|
|
}
|
|
NSString *locationStr = [NSString stringWithFormat:@"%lf,%lf",
|
|
location.coordinate.latitude,
|
|
location.coordinate.longitude];
|
|
query = [NSString stringWithFormat:@"?appver=%d&country_code=%@&appid=%@&nid=%@&location=%@&location_timestamp=%lf&client=1",
|
|
kAdWhirlAppVer,
|
|
[[NSLocale currentLocale] localeIdentifier],
|
|
self.adWhirlConfig.appKey,
|
|
self.networkConfig.nid,
|
|
locationStr,
|
|
[[NSDate date] timeIntervalSince1970]];
|
|
}
|
|
else {
|
|
AWLogDebug(@"Do not allow location access in custom ad");
|
|
query = [NSString stringWithFormat:@"?appver=%d&country_code=%@&appid=%@&nid=%@&client=1",
|
|
kAdWhirlAppVer,
|
|
[[NSLocale currentLocale] localeIdentifier],
|
|
self.adWhirlConfig.appKey,
|
|
self.networkConfig.nid];
|
|
}
|
|
NSURL *adRequestURL = [NSURL URLWithString:query relativeToURL:adRequestBaseURL];
|
|
AWLogDebug(@"Requesting custom ad at %@", adRequestURL);
|
|
NSURLRequest *adRequest = [NSURLRequest requestWithURL:adRequestURL];
|
|
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:adRequest
|
|
delegate:self];
|
|
self.adConnection = conn;
|
|
[conn release];
|
|
}
|
|
|
|
- (void)stopBeingDelegate {
|
|
AdWhirlCustomAdView *theAdView = (AdWhirlCustomAdView *)self.adNetworkView;
|
|
if (theAdView != nil) {
|
|
theAdView.delegate = nil;
|
|
}
|
|
}
|
|
|
|
- (void)dealloc {
|
|
[locationManager release], locationManager = nil;
|
|
[adConnection release], adConnection = nil;
|
|
[adData release], adData = nil;
|
|
[imageConnection release], imageConnection = nil;
|
|
[imageData release], imageData = nil;
|
|
[adView release], adView = nil;
|
|
[webBrowserController release], webBrowserController = nil;
|
|
[super dealloc];
|
|
}
|
|
|
|
|
|
- (CLLocationManager *)locationManager {
|
|
if (locationManager == nil) {
|
|
locationManager = [[CLLocationManager alloc] init];
|
|
}
|
|
return locationManager;
|
|
}
|
|
|
|
- (BOOL)parseEnums:(int *)val
|
|
adInfo:(NSDictionary*)info
|
|
minVal:(int)min
|
|
maxVal:(int)max
|
|
fieldName:(NSString *)name
|
|
error:(NSError **)error {
|
|
NSString *str = [info objectForKey:name];
|
|
if (str == nil) {
|
|
if (error != nil)
|
|
*error = [AdWhirlError errorWithCode:AdWhirlCustomAdDataError
|
|
description:[NSString stringWithFormat:
|
|
@"Custom ad data has no '%@' field", name]];
|
|
return NO;
|
|
}
|
|
int intVal = [str intValue];
|
|
if (intVal <= min || intVal >= max) {
|
|
if (error != nil)
|
|
*error = [AdWhirlError errorWithCode:AdWhirlCustomAdDataError
|
|
description:[NSString stringWithFormat:
|
|
@"Custom ad: Invalid value for %@ - %d", name, intVal]];
|
|
return NO;
|
|
}
|
|
*val = intVal;
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)parseAdData:(NSData *)data error:(NSError **)error {
|
|
NSError *jsonError = nil;
|
|
id parsed = [[CJSONDeserializer deserializer] deserialize:data error:&jsonError];
|
|
if (parsed == nil) {
|
|
if (error != nil)
|
|
*error = [AdWhirlError errorWithCode:AdWhirlCustomAdParseError
|
|
description:@"Error parsing custom ad JSON from server"
|
|
underlyingError:jsonError];
|
|
return NO;
|
|
}
|
|
if ([parsed isKindOfClass:[NSDictionary class]]) {
|
|
NSDictionary *adInfo = parsed;
|
|
|
|
// gather up and validate ad info
|
|
NSString *text = [adInfo objectForKey:@"ad_text"];
|
|
NSString *redirectURLStr = [adInfo objectForKey:@"redirect_url"];
|
|
|
|
int adTypeInt;
|
|
if (![self parseEnums:&adTypeInt
|
|
adInfo:adInfo
|
|
minVal:AWCustomAdTypeMIN
|
|
maxVal:AWCustomAdTypeMAX
|
|
fieldName:@"ad_type"
|
|
error:error]) {
|
|
return NO;
|
|
}
|
|
AWCustomAdType adType = adTypeInt;
|
|
|
|
int launchTypeInt;
|
|
if (![self parseEnums:&launchTypeInt
|
|
adInfo:adInfo
|
|
minVal:AWCustomAdLaunchTypeMIN
|
|
maxVal:AWCustomAdLaunchTypeMAX
|
|
fieldName:@"launch_type"
|
|
error:error]) {
|
|
return NO;
|
|
}
|
|
AWCustomAdLaunchType launchType = launchTypeInt;
|
|
|
|
int animTypeInt;
|
|
if (![self parseEnums:&animTypeInt
|
|
adInfo:adInfo
|
|
minVal:AWCustomAdWebViewAnimTypeMIN
|
|
maxVal:AWCustomAdWebViewAnimTypeMAX
|
|
fieldName:@"webview_animation_type"
|
|
error:error]) {
|
|
return NO;
|
|
}
|
|
AWCustomAdWebViewAnimType animType = animTypeInt;
|
|
|
|
NSURL *redirectURL = nil;
|
|
if (redirectURLStr == nil) {
|
|
AWLogWarn(@"No redirect URL for custom ad");
|
|
}
|
|
else {
|
|
redirectURL = [[NSURL alloc] initWithString:redirectURLStr];
|
|
if (!redirectURL)
|
|
AWLogWarn(@"Custom ad: Malformed redirect URL string %@", redirectURLStr);
|
|
}
|
|
|
|
NSString *clickMetricsURLStr = [adInfo objectForKey:@"metrics_url"];
|
|
NSURL *clickMetricsURL = nil;
|
|
if (clickMetricsURLStr == nil) {
|
|
AWLogWarn(@"No click metric URL for custom ad");
|
|
}
|
|
else {
|
|
clickMetricsURL = [[NSURL alloc] initWithString:clickMetricsURLStr];
|
|
if (!clickMetricsURL)
|
|
AWLogWarn(@"Malformed click metrics URL string %@", clickMetricsURLStr);
|
|
}
|
|
|
|
AWLogDebug(@"Got custom ad '%@' %@ %@ %d %d %d", text, redirectURL,
|
|
clickMetricsURL, adType, launchType, animType);
|
|
|
|
self.adView = [[AdWhirlCustomAdView alloc] initWithDelegate:self
|
|
text:text
|
|
redirectURL:redirectURL
|
|
clickMetricsURL:clickMetricsURL
|
|
adType:adType
|
|
launchType:launchType
|
|
animType:animType
|
|
backgroundColor:[self helperBackgroundColorToUse]
|
|
textColor:[self helperTextColorToUse]];
|
|
[self.adView release];
|
|
self.adNetworkView = adView;
|
|
[redirectURL release];
|
|
[clickMetricsURL release];
|
|
if (adView == nil) {
|
|
if (error != nil)
|
|
*error = [AdWhirlError errorWithCode:AdWhirlCustomAdDataError
|
|
description:@"Error initializing AdWhirl custom ad view"];
|
|
return NO;
|
|
}
|
|
|
|
// fetch image, set scale
|
|
self.scale = [[UIScreen mainScreen] respondsToSelector:@selector(scale)] ? [[UIScreen mainScreen] scale] : 1.0;
|
|
NSString *imageURL;
|
|
if (self.scale == 2.0 && adType == AWCustomAdTypeBanner) {
|
|
imageURL = [adInfo objectForKey:@"img_url_640x100"];
|
|
if (imageURL == nil || [imageURL length] == 0) {
|
|
self.scale = 1.0f;
|
|
imageURL = [adInfo objectForKey:@"img_url"];
|
|
}
|
|
} else {
|
|
imageURL = [adInfo objectForKey:@"img_url"];
|
|
}
|
|
AWLogDebug(@"Request custom ad image at %@", imageURL);
|
|
NSURLRequest *imageRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:imageURL]];
|
|
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:imageRequest
|
|
delegate:self];
|
|
self.imageConnection = conn;
|
|
[conn release];
|
|
}
|
|
else {
|
|
if (error != nil)
|
|
*error = [AdWhirlError errorWithCode:AdWhirlCustomAdDataError
|
|
description:@"Expected top-level dictionary in custom ad data"];
|
|
return NO;
|
|
}
|
|
return YES;
|
|
}
|
|
|
|
|
|
#pragma mark NSURLConnection delegate methods.
|
|
|
|
- (void)connection:(NSURLConnection *)conn didReceiveResponse:(NSURLResponse *)response {
|
|
if (conn == adConnection) {
|
|
[adData setLength:0];
|
|
}
|
|
else if (conn == imageConnection) {
|
|
[imageData setLength:0];
|
|
}
|
|
}
|
|
|
|
- (void)connection:(NSURLConnection *)conn didFailWithError:(NSError *)error {
|
|
if (conn == adConnection) {
|
|
[self.adWhirlView adapter:self didFailAd:[AdWhirlError errorWithCode:AdWhirlCustomAdConnectionError
|
|
description:@"Error connecting to custom ad server"
|
|
underlyingError:error]];
|
|
requesting = NO;
|
|
}
|
|
else if (conn == imageConnection) {
|
|
[self.adWhirlView adapter:self didFailAd:[AdWhirlError errorWithCode:AdWhirlCustomAdConnectionError
|
|
description:@"Error connecting to custom ad server to fetch image"
|
|
underlyingError:error]];
|
|
requesting = NO;
|
|
}
|
|
}
|
|
|
|
- (void)connectionDidFinishLoading:(NSURLConnection *)conn {
|
|
if (conn == adConnection) {
|
|
NSError *error = nil;
|
|
if (![self parseAdData:adData error:&error]) {
|
|
[self.adWhirlView adapter:self didFailAd:error];
|
|
requesting = NO;
|
|
return;
|
|
}
|
|
}
|
|
else if (conn == imageConnection) {
|
|
UIImage *image = [[UIImage alloc] initWithData:imageData];
|
|
if (self.scale == 2.0) {
|
|
UIImage *img = [[UIImage alloc] initWithCGImage:image.CGImage scale:2.0 orientation:image.imageOrientation];
|
|
[image release];
|
|
image = img;
|
|
}
|
|
if (image == nil) {
|
|
[self.adWhirlView adapter:self didFailAd:[AdWhirlError errorWithCode:AdWhirlCustomAdImageError
|
|
description:@"Cannot initialize custom ad image from data"]];
|
|
requesting = NO;
|
|
return;
|
|
}
|
|
adView.image = image;
|
|
[adView setNeedsDisplay];
|
|
[image release];
|
|
requesting = NO;
|
|
[self.adWhirlView adapter:self didReceiveAdView:self.adView];
|
|
}
|
|
}
|
|
|
|
- (void)connection:(NSURLConnection *)conn didReceiveData:(NSData *)data {
|
|
if (conn == adConnection) {
|
|
[adData appendData:data];
|
|
}
|
|
else if (conn == imageConnection) {
|
|
[imageData appendData:data];
|
|
}
|
|
}
|
|
|
|
|
|
#pragma mark AdWhirlCustomAdViewDelegate methods
|
|
|
|
- (void)adTapped:(AdWhirlCustomAdView *)ad {
|
|
if (ad != adView) return;
|
|
if (ad.clickMetricsURL != nil) {
|
|
NSURLRequest *metRequest = [NSURLRequest requestWithURL:ad.clickMetricsURL];
|
|
[NSURLConnection connectionWithRequest:metRequest
|
|
delegate:nil]; // fire and forget
|
|
AWLogDebug(@"Sent custom ad click ping to %@", ad.clickMetricsURL);
|
|
}
|
|
if (ad.redirectURL == nil) {
|
|
AWLogError(@"Custom ad redirect URL is nil");
|
|
return;
|
|
}
|
|
switch (ad.launchType) {
|
|
case AWCustomAdLaunchTypeSafari:
|
|
AWLogDebug(@"Opening URL '%@' for custom ad", ad.redirectURL);
|
|
if ([[UIApplication sharedApplication] openURL:ad.redirectURL] == NO) {
|
|
AWLogError(@"Cannot open URL '%@' for custom ad", ad.redirectURL);
|
|
}
|
|
break;
|
|
case AWCustomAdLaunchTypeCanvas:
|
|
if (self.webBrowserController == nil) {
|
|
AdWhirlWebBrowserController *ctrlr = [[AdWhirlWebBrowserController alloc] init];
|
|
self.webBrowserController = ctrlr;
|
|
[ctrlr release];
|
|
}
|
|
webBrowserController.delegate = self;
|
|
[webBrowserController presentWithController:[self.adWhirlDelegate viewControllerForPresentingModalView]
|
|
transition:ad.animType];
|
|
[self helperNotifyDelegateOfFullScreenModal];
|
|
[webBrowserController loadURL:ad.redirectURL];
|
|
break;
|
|
default:
|
|
AWLogError(@"Custom ad: Unsupported launch type %d", ad.launchType);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
#pragma mark AdWhirlWebBrowserControllerDelegate methods
|
|
|
|
- (void)webBrowserClosed:(AdWhirlWebBrowserController *)controller {
|
|
if (controller != webBrowserController) return;
|
|
self.webBrowserController = nil; // don't keep around to save memory
|
|
[self helperNotifyDelegateOfFullScreenModalDismissal];
|
|
}
|
|
|
|
@end
|
|
|