diff options
Diffstat (limited to 'platform/iphone')
-rw-r--r-- | platform/iphone/Appirater.m | 553 | ||||
-rw-r--r-- | platform/iphone/AppiraterDelegate.h | 23 | ||||
-rw-r--r-- | platform/iphone/SCsub | 7 | ||||
-rw-r--r-- | platform/iphone/app_delegate.mm | 30 | ||||
-rw-r--r-- | platform/iphone/detect.py | 6 | ||||
-rw-r--r-- | platform/iphone/game_center.mm | 46 | ||||
-rwxr-xr-x | platform/iphone/gl_view.mm | 54 | ||||
-rw-r--r-- | platform/iphone/globals/global_defaults.h | 2 | ||||
-rw-r--r-- | platform/iphone/icloud.mm | 84 | ||||
-rw-r--r-- | platform/iphone/in_app_store.mm | 28 | ||||
-rw-r--r-- | platform/iphone/ios.h | 20 | ||||
-rw-r--r-- | platform/iphone/ios.mm | 36 | ||||
-rw-r--r-- | platform/iphone/os_iphone.cpp | 3 | ||||
-rw-r--r-- | platform/iphone/rasterizer_iphone.cpp | 26 |
14 files changed, 677 insertions, 241 deletions
diff --git a/platform/iphone/Appirater.m b/platform/iphone/Appirater.m index d9144eda3e..951b892032 100644 --- a/platform/iphone/Appirater.m +++ b/platform/iphone/Appirater.m @@ -1,9 +1,7 @@ -#ifdef APPIRATER_ENABLED - /* This file is part of Appirater. - Copyright (c) 2010, Arash Payan + Copyright (c) 2012, Arash Payan All rights reserved. Permission is hereby granted, free of charge, to any person @@ -33,13 +31,17 @@ * * Created by Arash Payan on 9/5/09. * http://arashpayan.com - * Copyright 2010 Arash Payan. All rights reserved. + * Copyright 2012 Arash Payan. All rights reserved. */ #import "Appirater.h" #import <SystemConfiguration/SCNetworkReachability.h> #include <netinet/in.h> +#if ! __has_feature(objc_arc) +#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). +#endif + NSString *const kAppiraterFirstUseDate = @"kAppiraterFirstUseDate"; NSString *const kAppiraterUseCount = @"kAppiraterUseCount"; NSString *const kAppiraterSignificantEventCount = @"kAppiraterSignificantEventCount"; @@ -49,18 +51,175 @@ NSString *const kAppiraterDeclinedToRate = @"kAppiraterDeclinedToRate"; NSString *const kAppiraterReminderRequestDate = @"kAppiraterReminderRequestDate"; NSString *templateReviewURL = @"itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=APP_ID"; +NSString *templateReviewURLiOS7 = @"itms-apps://itunes.apple.com/app/idAPP_ID"; +NSString *templateReviewURLiOS8 = @"itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?id=APP_ID&onlyLatestVersion=true&pageNumber=0&sortOrdering=1&type=Purple+Software"; -static int app_id = 0; +static NSString *_appId; +static double _daysUntilPrompt = 30; +static NSInteger _usesUntilPrompt = 20; +static NSInteger _significantEventsUntilPrompt = -1; +static double _timeBeforeReminding = 1; +static BOOL _debug = NO; +#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_5_0 + static id<AppiraterDelegate> _delegate; +#else + __weak static id<AppiraterDelegate> _delegate; +#endif +static BOOL _usesAnimation = TRUE; +static UIStatusBarStyle _statusBarStyle; +static BOOL _modalOpen = false; +static BOOL _alwaysUseMainBundle = NO; -@interface Appirater (hidden) +@interface Appirater () +@property (nonatomic, copy) NSString *alertTitle; +@property (nonatomic, copy) NSString *alertMessage; +@property (nonatomic, copy) NSString *alertCancelTitle; +@property (nonatomic, copy) NSString *alertRateTitle; +@property (nonatomic, copy) NSString *alertRateLaterTitle; - (BOOL)connectedToNetwork; + (Appirater*)sharedInstance; +- (void)showPromptWithChecks:(BOOL)withChecks + displayRateLaterButton:(BOOL)displayRateLaterButton; +- (void)showRatingAlert:(BOOL)displayRateLaterButton; - (void)showRatingAlert; +- (BOOL)ratingAlertIsAppropriate; - (BOOL)ratingConditionsHaveBeenMet; - (void)incrementUseCount; +- (void)hideRatingAlert; @end -@implementation Appirater (hidden) +@implementation Appirater + +@synthesize ratingAlert; + ++ (void) setAppId:(NSString *)appId { + _appId = appId; +} + ++ (void) setDaysUntilPrompt:(double)value { + _daysUntilPrompt = value; +} + ++ (void) setUsesUntilPrompt:(NSInteger)value { + _usesUntilPrompt = value; +} + ++ (void) setSignificantEventsUntilPrompt:(NSInteger)value { + _significantEventsUntilPrompt = value; +} + ++ (void) setTimeBeforeReminding:(double)value { + _timeBeforeReminding = value; +} + ++ (void) setCustomAlertTitle:(NSString *)title +{ + [self sharedInstance].alertTitle = title; +} + ++ (void) setCustomAlertMessage:(NSString *)message +{ + [self sharedInstance].alertMessage = message; +} + ++ (void) setCustomAlertCancelButtonTitle:(NSString *)cancelTitle +{ + [self sharedInstance].alertCancelTitle = cancelTitle; +} + ++ (void) setCustomAlertRateButtonTitle:(NSString *)rateTitle +{ + [self sharedInstance].alertRateTitle = rateTitle; +} + ++ (void) setCustomAlertRateLaterButtonTitle:(NSString *)rateLaterTitle +{ + [self sharedInstance].alertRateLaterTitle = rateLaterTitle; +} + ++ (void) setDebug:(BOOL)debug { + _debug = debug; +} ++ (void)setDelegate:(id<AppiraterDelegate>)delegate{ + _delegate = delegate; +} ++ (void)setUsesAnimation:(BOOL)animation { + _usesAnimation = animation; +} ++ (void)setOpenInAppStore:(BOOL)openInAppStore { + [Appirater sharedInstance].openInAppStore = openInAppStore; +} ++ (void)setStatusBarStyle:(UIStatusBarStyle)style { + _statusBarStyle = style; +} ++ (void)setModalOpen:(BOOL)open { + _modalOpen = open; +} ++ (void)setAlwaysUseMainBundle:(BOOL)alwaysUseMainBundle { + _alwaysUseMainBundle = alwaysUseMainBundle; +} + ++ (NSBundle *)bundle +{ + NSBundle *bundle; + + if (_alwaysUseMainBundle) { + bundle = [NSBundle mainBundle]; + } else { + NSURL *appiraterBundleURL = [[NSBundle mainBundle] URLForResource:@"Appirater" withExtension:@"bundle"]; + + if (appiraterBundleURL) { + // Appirater.bundle will likely only exist when used via CocoaPods + bundle = [NSBundle bundleWithURL:appiraterBundleURL]; + } else { + bundle = [NSBundle mainBundle]; + } + } + + return bundle; +} + +- (NSString *)alertTitle +{ + return _alertTitle ? _alertTitle : APPIRATER_MESSAGE_TITLE; +} + +- (NSString *)alertMessage +{ + return _alertMessage ? _alertMessage : APPIRATER_MESSAGE; +} + +- (NSString *)alertCancelTitle +{ + return _alertCancelTitle ? _alertCancelTitle : APPIRATER_CANCEL_BUTTON; +} + +- (NSString *)alertRateTitle +{ + return _alertRateTitle ? _alertRateTitle : APPIRATER_RATE_BUTTON; +} + +- (NSString *)alertRateLaterTitle +{ + return _alertRateLaterTitle ? _alertRateLaterTitle : APPIRATER_RATE_LATER; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (id)init { + self = [super init]; + if (self) { + if ([[UIDevice currentDevice].systemVersion floatValue] >= 7.0) { + self.openInAppStore = YES; + } else { + self.openInAppStore = NO; + } + } + + return self; +} - (BOOL)connectedToNetwork { // Create zero addy @@ -73,7 +232,7 @@ static int app_id = 0; SCNetworkReachabilityRef defaultRouteReachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&zeroAddress); SCNetworkReachabilityFlags flags; - BOOL didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags); + Boolean didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags); CFRelease(defaultRouteReachability); if (!didRetrieveFlags) @@ -97,61 +256,110 @@ static int app_id = 0; static Appirater *appirater = nil; if (appirater == nil) { - @synchronized(self) { - if (appirater == nil) { - appirater = [[Appirater alloc] init]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillResignActive) name:@"UIApplicationWillResignActiveNotification" object:nil]; - } - } + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + appirater = [[Appirater alloc] init]; + appirater.delegate = _delegate; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillResignActive) name: + UIApplicationWillResignActiveNotification object:nil]; + }); } return appirater; } -- (void)showRatingAlert { - UIAlertView *alertView = [[[UIAlertView alloc] initWithTitle:APPIRATER_MESSAGE_TITLE - message:APPIRATER_MESSAGE - delegate:self - cancelButtonTitle:APPIRATER_CANCEL_BUTTON - otherButtonTitles:APPIRATER_RATE_BUTTON, APPIRATER_RATE_LATER, nil] autorelease]; +- (void)showRatingAlert:(BOOL)displayRateLaterButton { + UIAlertView *alertView = nil; + id <AppiraterDelegate> delegate = _delegate; + + if(delegate && [delegate respondsToSelector:@selector(appiraterShouldDisplayAlert:)] && ![delegate appiraterShouldDisplayAlert:self]) { + return; + } + + if (displayRateLaterButton) { + alertView = [[UIAlertView alloc] initWithTitle:self.alertTitle + message:self.alertMessage + delegate:self + cancelButtonTitle:self.alertCancelTitle + otherButtonTitles:self.alertRateTitle, self.alertRateLaterTitle, nil]; + } else { + alertView = [[UIAlertView alloc] initWithTitle:self.alertTitle + message:self.alertMessage + delegate:self + cancelButtonTitle:self.alertCancelTitle + otherButtonTitles:self.alertRateTitle, nil]; + } + self.ratingAlert = alertView; - [alertView show]; + [alertView show]; + + if (delegate && [delegate respondsToSelector:@selector(appiraterDidDisplayAlert:)]) { + [delegate appiraterDidDisplayAlert:self]; + } +} + +- (void)showRatingAlert +{ + [self showRatingAlert:true]; } +// is this an ok time to show the alert? (regardless of whether the rating conditions have been met) +// +// things checked here: +// * connectivity with network +// * whether user has rated before +// * whether user has declined to rate +// * whether rating alert is currently showing visibly +// things NOT checked here: +// * time since first launch +// * number of uses of app +// * number of significant events +// * time since last reminder +- (BOOL)ratingAlertIsAppropriate { + return ([self connectedToNetwork] + && ![self userHasDeclinedToRate] + && !self.ratingAlert.visible + && ![self userHasRatedCurrentVersion]); +} + +// have the rating conditions been met/earned? (regardless of whether this would be a moment when it's appropriate to show a new rating alert) +// +// things checked here: +// * time since first launch +// * number of uses of app +// * number of significant events +// * time since last reminder +// things NOT checked here: +// * connectivity with network +// * whether user has rated before +// * whether user has declined to rate +// * whether rating alert is currently showing visibly - (BOOL)ratingConditionsHaveBeenMet { - if (APPIRATER_DEBUG) + if (_debug) return YES; NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; NSDate *dateOfFirstLaunch = [NSDate dateWithTimeIntervalSince1970:[userDefaults doubleForKey:kAppiraterFirstUseDate]]; NSTimeInterval timeSinceFirstLaunch = [[NSDate date] timeIntervalSinceDate:dateOfFirstLaunch]; - NSTimeInterval timeUntilRate = 60 * 60 * 24 * APPIRATER_DAYS_UNTIL_PROMPT; + NSTimeInterval timeUntilRate = 60 * 60 * 24 * _daysUntilPrompt; if (timeSinceFirstLaunch < timeUntilRate) return NO; // check if the app has been used enough - int useCount = [userDefaults integerForKey:kAppiraterUseCount]; - if (useCount <= APPIRATER_USES_UNTIL_PROMPT) + NSInteger useCount = [userDefaults integerForKey:kAppiraterUseCount]; + if (useCount < _usesUntilPrompt) return NO; // check if the user has done enough significant events - int sigEventCount = [userDefaults integerForKey:kAppiraterSignificantEventCount]; - if (sigEventCount <= APPIRATER_SIG_EVENTS_UNTIL_PROMPT) - return NO; - - // has the user previously declined to rate this version of the app? - if ([userDefaults boolForKey:kAppiraterDeclinedToRate]) - return NO; - - // has the user already rated the app? - if ([userDefaults boolForKey:kAppiraterRatedCurrentVersion]) + NSInteger sigEventCount = [userDefaults integerForKey:kAppiraterSignificantEventCount]; + if (sigEventCount < _significantEventsUntilPrompt) return NO; // if the user wanted to be reminded later, has enough time passed? NSDate *reminderRequestDate = [NSDate dateWithTimeIntervalSince1970:[userDefaults doubleForKey:kAppiraterReminderRequestDate]]; NSTimeInterval timeSinceReminderRequest = [[NSDate date] timeIntervalSinceDate:reminderRequestDate]; - NSTimeInterval timeUntilReminder = 60 * 60 * 24 * APPIRATER_TIME_BEFORE_REMINDING; + NSTimeInterval timeUntilReminder = 60 * 60 * 24 * _timeBeforeReminding; if (timeSinceReminderRequest < timeUntilReminder) return NO; @@ -171,7 +379,7 @@ static int app_id = 0; [userDefaults setObject:version forKey:kAppiraterCurrentVersion]; } - if (APPIRATER_DEBUG) + if (_debug) NSLog(@"APPIRATER Tracking version: %@", trackingVersion); if ([trackingVersion isEqualToString:version]) @@ -185,11 +393,11 @@ static int app_id = 0; } // increment the use count - int useCount = [userDefaults integerForKey:kAppiraterUseCount]; + NSInteger useCount = [userDefaults integerForKey:kAppiraterUseCount]; useCount++; [userDefaults setInteger:useCount forKey:kAppiraterUseCount]; - if (APPIRATER_DEBUG) - NSLog(@"APPIRATER Use count: %d", useCount); + if (_debug) + NSLog(@"APPIRATER Use count: %@", @(useCount)); } else { @@ -219,7 +427,7 @@ static int app_id = 0; [userDefaults setObject:version forKey:kAppiraterCurrentVersion]; } - if (APPIRATER_DEBUG) + if (_debug) NSLog(@"APPIRATER Tracking version: %@", trackingVersion); if ([trackingVersion isEqualToString:version]) @@ -233,11 +441,11 @@ static int app_id = 0; } // increment the significant event count - int sigEventCount = [userDefaults integerForKey:kAppiraterSignificantEventCount]; + NSInteger sigEventCount = [userDefaults integerForKey:kAppiraterSignificantEventCount]; sigEventCount++; [userDefaults setInteger:sigEventCount forKey:kAppiraterSignificantEventCount]; - if (APPIRATER_DEBUG) - NSLog(@"APPIRATER Significant event count: %d", sigEventCount); + if (_debug) + NSLog(@"APPIRATER Significant event count: %@", @(sigEventCount)); } else { @@ -254,105 +462,214 @@ static int app_id = 0; [userDefaults synchronize]; } -@end - - -@interface Appirater () -- (void)hideRatingAlert; -@end - -@implementation Appirater - -@synthesize ratingAlert; - -- (void)incrementAndRate:(NSNumber*)_canPromptForRating { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - +- (void)incrementAndRate:(BOOL)canPromptForRating { [self incrementUseCount]; - if ([_canPromptForRating boolValue] == YES && - [self ratingConditionsHaveBeenMet] && - [self connectedToNetwork]) + if (canPromptForRating && + [self ratingConditionsHaveBeenMet] && + [self ratingAlertIsAppropriate]) { - [self performSelectorOnMainThread:@selector(showRatingAlert) withObject:nil waitUntilDone:NO]; + dispatch_async(dispatch_get_main_queue(), + ^{ + [self showRatingAlert]; + }); } - - [pool release]; } -- (void)incrementSignificantEventAndRate:(NSNumber*)_canPromptForRating { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - +- (void)incrementSignificantEventAndRate:(BOOL)canPromptForRating { [self incrementSignificantEventCount]; - if ([_canPromptForRating boolValue] == YES && - [self ratingConditionsHaveBeenMet] && - [self connectedToNetwork]) + if (canPromptForRating && + [self ratingConditionsHaveBeenMet] && + [self ratingAlertIsAppropriate]) { - [self performSelectorOnMainThread:@selector(showRatingAlert) withObject:nil waitUntilDone:NO]; + dispatch_async(dispatch_get_main_queue(), + ^{ + [self showRatingAlert]; + }); } - - [pool release]; } -+ (void)appLaunched:(int)p_app_id { - app_id = p_app_id; +- (BOOL)userHasDeclinedToRate { + return [[NSUserDefaults standardUserDefaults] boolForKey:kAppiraterDeclinedToRate]; +} + +- (BOOL)userHasRatedCurrentVersion { + return [[NSUserDefaults standardUserDefaults] boolForKey:kAppiraterRatedCurrentVersion]; +} + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-implementations" ++ (void)appLaunched { [Appirater appLaunched:YES]; } +#pragma GCC diagnostic pop -+ (void)appLaunched:(BOOL)canPromptForRating app_id:(int)p_app_id { - app_id = p_app_id; - NSNumber *_canPromptForRating = [[NSNumber alloc] initWithBool:canPromptForRating]; - [NSThread detachNewThreadSelector:@selector(incrementAndRate:) - toTarget:[Appirater sharedInstance] - withObject:_canPromptForRating]; - [_canPromptForRating release]; ++ (void)appLaunched:(BOOL)canPromptForRating { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), + ^{ + Appirater *a = [Appirater sharedInstance]; + if (_debug) { + dispatch_async(dispatch_get_main_queue(), + ^{ + [a showRatingAlert]; + }); + } else { + [a incrementAndRate:canPromptForRating]; + } + }); } - (void)hideRatingAlert { if (self.ratingAlert.visible) { - if (APPIRATER_DEBUG) + if (_debug) NSLog(@"APPIRATER Hiding Alert"); [self.ratingAlert dismissWithClickedButtonIndex:-1 animated:NO]; } } + (void)appWillResignActive { - if (APPIRATER_DEBUG) + if (_debug) NSLog(@"APPIRATER appWillResignActive"); [[Appirater sharedInstance] hideRatingAlert]; } + (void)appEnteredForeground:(BOOL)canPromptForRating { - NSNumber *_canPromptForRating = [[NSNumber alloc] initWithBool:canPromptForRating]; - [NSThread detachNewThreadSelector:@selector(incrementAndRate:) - toTarget:[Appirater sharedInstance] - withObject:_canPromptForRating]; - [_canPromptForRating release]; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), + ^{ + [[Appirater sharedInstance] incrementAndRate:canPromptForRating]; + }); } + (void)userDidSignificantEvent:(BOOL)canPromptForRating { - NSNumber *_canPromptForRating = [[NSNumber alloc] initWithBool:canPromptForRating]; - [NSThread detachNewThreadSelector:@selector(incrementSignificantEventAndRate:) - toTarget:[Appirater sharedInstance] - withObject:_canPromptForRating]; - [_canPromptForRating release]; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), + ^{ + [[Appirater sharedInstance] incrementSignificantEventAndRate:canPromptForRating]; + }); +} + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-implementations" ++ (void)showPrompt { + [Appirater tryToShowPrompt]; +} +#pragma GCC diagnostic pop + ++ (void)tryToShowPrompt { + [[Appirater sharedInstance] showPromptWithChecks:true + displayRateLaterButton:true]; +} + ++ (void)forceShowPrompt:(BOOL)displayRateLaterButton { + [[Appirater sharedInstance] showPromptWithChecks:false + displayRateLaterButton:displayRateLaterButton]; +} + +- (void)showPromptWithChecks:(BOOL)withChecks + displayRateLaterButton:(BOOL)displayRateLaterButton { + if (withChecks == NO || [self ratingAlertIsAppropriate]) { + [self showRatingAlert:displayRateLaterButton]; + } +} + ++ (id)getRootViewController { + UIWindow *window = [[UIApplication sharedApplication] keyWindow]; + if (window.windowLevel != UIWindowLevelNormal) { + NSArray *windows = [[UIApplication sharedApplication] windows]; + for(window in windows) { + if (window.windowLevel == UIWindowLevelNormal) { + break; + } + } + } + + return [Appirater iterateSubViewsForViewController:window]; // iOS 8+ deep traverse +} + ++ (id)iterateSubViewsForViewController:(UIView *) parentView { + for (UIView *subView in [parentView subviews]) { + UIResponder *responder = [subView nextResponder]; + if([responder isKindOfClass:[UIViewController class]]) { + return [self topMostViewController: (UIViewController *) responder]; + } + id found = [Appirater iterateSubViewsForViewController:subView]; + if( nil != found) { + return found; + } + } + return nil; +} + ++ (UIViewController *) topMostViewController: (UIViewController *) controller { + BOOL isPresenting = NO; + do { + // this path is called only on iOS 6+, so -presentedViewController is fine here. + UIViewController *presented = [controller presentedViewController]; + isPresenting = presented != nil; + if(presented != nil) { + controller = presented; + } + + } while (isPresenting); + + return controller; } + (void)rateApp { -#if TARGET_IPHONE_SIMULATOR - NSLog(@"APPIRATER NOTE: iTunes App Store is not supported on the iOS simulator. Unable to open App Store page."); -#else + NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; - NSString *reviewURL = [templateReviewURL stringByReplacingOccurrencesOfString:@"APP_ID" withString:[NSString stringWithFormat:@"%d", app_id]]; [userDefaults setBool:YES forKey:kAppiraterRatedCurrentVersion]; [userDefaults synchronize]; - [[UIApplication sharedApplication] openURL:[NSURL URLWithString:reviewURL]]; + + //Use the in-app StoreKit view if available (iOS 6) and imported. This works in the simulator. + if (![Appirater sharedInstance].openInAppStore && NSStringFromClass([SKStoreProductViewController class]) != nil) { + + SKStoreProductViewController *storeViewController = [[SKStoreProductViewController alloc] init]; + NSNumber *appId = [NSNumber numberWithInteger:_appId.integerValue]; + [storeViewController loadProductWithParameters:@{SKStoreProductParameterITunesItemIdentifier:appId} completionBlock:nil]; + storeViewController.delegate = self.sharedInstance; + + id <AppiraterDelegate> delegate = self.sharedInstance.delegate; + if ([delegate respondsToSelector:@selector(appiraterWillPresentModalView:animated:)]) { + [delegate appiraterWillPresentModalView:self.sharedInstance animated:_usesAnimation]; + } + [[self getRootViewController] presentViewController:storeViewController animated:_usesAnimation completion:^{ + [self setModalOpen:YES]; + //Temporarily use a black status bar to match the StoreKit view. + [self setStatusBarStyle:[UIApplication sharedApplication].statusBarStyle]; +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 + [[UIApplication sharedApplication]setStatusBarStyle:UIStatusBarStyleLightContent animated:_usesAnimation]; #endif + }]; + + //Use the standard openUrl method if StoreKit is unavailable. + } else { + + #if TARGET_IPHONE_SIMULATOR + NSLog(@"APPIRATER NOTE: iTunes App Store is not supported on the iOS simulator. Unable to open App Store page."); + #else + NSString *reviewURL = [templateReviewURL stringByReplacingOccurrencesOfString:@"APP_ID" withString:[NSString stringWithFormat:@"%@", _appId]]; + + // iOS 7 needs a different templateReviewURL @see https://github.com/arashpayan/appirater/issues/131 + // Fixes condition @see https://github.com/arashpayan/appirater/issues/205 + if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0 && [[[UIDevice currentDevice] systemVersion] floatValue] < 8.0) { + reviewURL = [templateReviewURLiOS7 stringByReplacingOccurrencesOfString:@"APP_ID" withString:[NSString stringWithFormat:@"%@", _appId]]; + } + // iOS 8 needs a different templateReviewURL also @see https://github.com/arashpayan/appirater/issues/182 + else if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) + { + reviewURL = [templateReviewURLiOS8 stringByReplacingOccurrencesOfString:@"APP_ID" withString:[NSString stringWithFormat:@"%@", _appId]]; + } + + [[UIApplication sharedApplication] openURL:[NSURL URLWithString:reviewURL]]; + #endif + } } -- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { +- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex { NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; + + id <AppiraterDelegate> delegate = _delegate; switch (buttonIndex) { case 0: @@ -360,24 +677,56 @@ static int app_id = 0; // they don't want to rate it [userDefaults setBool:YES forKey:kAppiraterDeclinedToRate]; [userDefaults synchronize]; + if(delegate && [delegate respondsToSelector:@selector(appiraterDidDeclineToRate:)]){ + [delegate appiraterDidDeclineToRate:self]; + } break; } case 1: { // they want to rate it [Appirater rateApp]; + if(delegate&& [delegate respondsToSelector:@selector(appiraterDidOptToRate:)]){ + [delegate appiraterDidOptToRate:self]; + } break; } case 2: // remind them later [userDefaults setDouble:[[NSDate date] timeIntervalSince1970] forKey:kAppiraterReminderRequestDate]; [userDefaults synchronize]; + if(delegate && [delegate respondsToSelector:@selector(appiraterDidOptToRemindLater:)]){ + [delegate appiraterDidOptToRemindLater:self]; + } break; default: break; } } -@end +//Delegate call from the StoreKit view. +- (void)productViewControllerDidFinish:(SKStoreProductViewController *)viewController { + [Appirater closeModal]; +} -#endif +//Close the in-app rating (StoreKit) view and restore the previous status bar style. ++ (void)closeModal { + if (_modalOpen) { + [[UIApplication sharedApplication]setStatusBarStyle:_statusBarStyle animated:_usesAnimation]; + BOOL usedAnimation = _usesAnimation; + [self setModalOpen:NO]; + + // get the top most controller (= the StoreKit Controller) and dismiss it + UIViewController *presentingController = [UIApplication sharedApplication].keyWindow.rootViewController; + presentingController = [self topMostViewController: presentingController]; + [presentingController dismissViewControllerAnimated:_usesAnimation completion:^{ + id <AppiraterDelegate> delegate = self.sharedInstance.delegate; + if ([delegate respondsToSelector:@selector(appiraterDidDismissModalView:animated:)]) { + [delegate appiraterDidDismissModalView:(Appirater *)self animated:usedAnimation]; + } + }]; + [self.class setStatusBarStyle:(UIStatusBarStyle)nil]; + } +} + +@end diff --git a/platform/iphone/AppiraterDelegate.h b/platform/iphone/AppiraterDelegate.h new file mode 100644 index 0000000000..cbe0cfad5b --- /dev/null +++ b/platform/iphone/AppiraterDelegate.h @@ -0,0 +1,23 @@ +// +// AppiraterDelegate.h +// Banana Stand +// +// Created by Robert Haining on 9/25/12. +// Copyright (c) 2012 News.me. All rights reserved. +// + +#import <Foundation/Foundation.h> + +@class Appirater; + +@protocol AppiraterDelegate <NSObject> + +@optional +-(BOOL)appiraterShouldDisplayAlert:(Appirater *)appirater; +-(void)appiraterDidDisplayAlert:(Appirater *)appirater; +-(void)appiraterDidDeclineToRate:(Appirater *)appirater; +-(void)appiraterDidOptToRate:(Appirater *)appirater; +-(void)appiraterDidOptToRemindLater:(Appirater *)appirater; +-(void)appiraterWillPresentModalView:(Appirater *)appirater animated:(BOOL)animated; +-(void)appiraterDidDismissModalView:(Appirater *)appirater animated:(BOOL)animated; +@end diff --git a/platform/iphone/SCsub b/platform/iphone/SCsub index 922a324694..5b2e1533da 100644 --- a/platform/iphone/SCsub +++ b/platform/iphone/SCsub @@ -13,7 +13,8 @@ iphone_lib = [ 'game_center.mm', 'in_app_store.mm', 'icloud.mm', - 'Appirater.m', + #'Appirater.m', + 'ios.mm', ] #env.Depends('#core/math/vector3.h', 'vector3_psp.h') @@ -27,8 +28,8 @@ if env['ios_gles22_override'] == "yes": env_ios.Append(CPPFLAGS=['-DGLES2_OVERRIDE']) -if env['ios_appirater'] == "yes": - env_ios.Append(CPPFLAGS=['-DAPPIRATER_ENABLED']) +#if env['ios_appirater'] == "yes": +# env_ios.Append(CPPFLAGS=['-DAPPIRATER_ENABLED']) obj = env_ios.Object('godot_iphone.cpp') diff --git a/platform/iphone/app_delegate.mm b/platform/iphone/app_delegate.mm index a3af8ff163..dab75e08c8 100644 --- a/platform/iphone/app_delegate.mm +++ b/platform/iphone/app_delegate.mm @@ -100,7 +100,7 @@ static int frame_count = 0; int backingHeight; glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth); glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight); - + OS::VideoMode vm; vm.fullscreen = true; @@ -118,7 +118,7 @@ static int frame_count = 0; NSString *documentsDirectory = [paths objectAtIndex:0]; //NSString *documentsDirectory = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]; OSIPhone::get_singleton()->set_data_dir(String::utf8([documentsDirectory UTF8String])); - + NSString *locale_code = [[[NSLocale preferredLanguages] objectAtIndex:0] substringToIndex:2]; OSIPhone::get_singleton()->set_locale(String::utf8([locale_code UTF8String])); @@ -218,7 +218,7 @@ static int frame_count = 0; [application setStatusBarHidden:YES withAnimation:UIStatusBarAnimationNone]; // disable idle timer //application.idleTimerDisabled = YES; - + //Create a full-screen window window = [[UIWindow alloc] initWithFrame:rect]; //window.autoresizesSubviews = YES; @@ -236,9 +236,9 @@ static int frame_count = 0; int backingHeight; glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth); glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight); - + iphone_main(backingWidth, backingHeight, gargc, gargv); - + view_controller = [[ViewController alloc] init]; view_controller.view = glView; window.rootViewController = view_controller; @@ -248,7 +248,7 @@ static int frame_count = 0; printf("cadisaplylink: %d", glView.useCADisplayLink); glView.animationInterval = 1.0 / kRenderingFrequency; [glView startAnimation]; - + //Show the window [window makeKeyAndVisible]; @@ -261,9 +261,9 @@ static int frame_count = 0; //OSIPhone::screen_width = rect.size.width - rect.origin.x; //OSIPhone::screen_height = rect.size.height - rect.origin.y; - + mainViewController = view_controller; - + #ifdef MODULE_GAME_ANALYTICS_ENABLED printf("********************* didFinishLaunchingWithOptions\n"); if(!Globals::get_singleton()->has("mobileapptracker/advertiser_id")) @@ -274,24 +274,24 @@ static int frame_count = 0; { return; } - + String adid = GLOBAL_DEF("mobileapptracker/advertiser_id",""); String convkey = GLOBAL_DEF("mobileapptracker/conversion_key",""); - + NSString * advertiser_id = [NSString stringWithUTF8String:adid.utf8().get_data()]; NSString * conversion_key = [NSString stringWithUTF8String:convkey.utf8().get_data()]; - + // Account Configuration info - must be set [MobileAppTracker initializeWithMATAdvertiserId:advertiser_id MATConversionKey:conversion_key]; - + // Used to pass us the IFA, enables highly accurate 1-to-1 attribution. // Required for many advertising networks. [MobileAppTracker setAppleAdvertisingIdentifier:[[ASIdentifierManager sharedManager] advertisingIdentifier] advertisingTrackingEnabled:[[ASIdentifierManager sharedManager] isAdvertisingTrackingEnabled]]; - + #endif - + }; - (void)applicationWillTerminate:(UIApplication*)application { @@ -389,7 +389,7 @@ static int frame_count = 0; [PFPush handlePush:userInfo]; NSDictionary *aps = [userInfo objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]; NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; - + NSLog(@"Push Notification Payload (app active) %@", aps); [defaults setObject:aps forKey:@"notificationInfo"]; [defaults synchronize]; diff --git a/platform/iphone/detect.py b/platform/iphone/detect.py index 9472f05e16..c3ee098350 100644 --- a/platform/iphone/detect.py +++ b/platform/iphone/detect.py @@ -108,7 +108,7 @@ def configure(env): if env['store_kit'] == 'yes': env.Append(CPPFLAGS=['-DSTOREKIT_ENABLED']) env.Append(LINKFLAGS=['-framework', 'StoreKit']) - + if env['icloud'] == 'yes': env.Append(CPPFLAGS=['-DICLOUD_ENABLED']) @@ -117,7 +117,7 @@ def configure(env): if (env["target"]=="release"): env.Append(CCFLAGS=['-O3', '-DNS_BLOCK_ASSERTIONS=1','-Wall', '-gdwarf-2']) # removed -ffast-math - env.Append(LINKFLAGS=['-O3']) # + env.Append(LINKFLAGS=['-O3']) # elif env["target"] == "release_debug": env.Append(CCFLAGS=['-Os', '-DNS_BLOCK_ASSERTIONS=1','-Wall','-DDEBUG_ENABLED']) @@ -151,7 +151,7 @@ def configure(env): env.Append(CPPFLAGS=['-fno-exceptions']) #env['neon_enabled']=True env['S_compiler'] = '$IPHONEPATH/Developer/usr/bin/gcc' - + import methods env.Append( BUILDERS = { 'GLSL120' : env.Builder(action = methods.build_legacygl_headers, suffix = 'glsl.h',src_suffix = '.glsl') } ) env.Append( BUILDERS = { 'GLSL' : env.Builder(action = methods.build_glsl_headers, suffix = 'glsl.h',src_suffix = '.glsl') } ) diff --git a/platform/iphone/game_center.mm b/platform/iphone/game_center.mm index 3756b58699..a5b90514da 100644 --- a/platform/iphone/game_center.mm +++ b/platform/iphone/game_center.mm @@ -94,10 +94,10 @@ Error GameCenter::connect() { ret["error_description"] = [error.localizedDescription UTF8String]; GameCenter::get_singleton()->connected = false; }; - + pending_events.push_back(ret); }; - + }); return OK; @@ -156,7 +156,7 @@ Error GameCenter::award_achievement(Variant p_params) { if (params.has("show_completion_banner")) { achievement.showsCompletionBanner = params["show_completion_banner"] ? YES : NO; } - + [GKAchievement reportAchievements:@[achievement] withCompletionHandler:^(NSError *error) { Dictionary ret; @@ -189,30 +189,30 @@ void GameCenter::request_achievement_descriptions() { IntArray maximum_points; Array hidden; Array replayable; - + for (int i=0; i<[descriptions count]; i++) { GKAchievementDescription* description = [descriptions objectAtIndex:i]; - + const char* str = [description.identifier UTF8String]; names.push_back(String::utf8(str != NULL ? str : "")); - + str = [description.title UTF8String]; titles.push_back(String::utf8(str != NULL ? str : "")); - + str = [description.unachievedDescription UTF8String]; unachieved_descriptions.push_back(String::utf8(str != NULL ? str : "")); - + str = [description.achievedDescription UTF8String]; achieved_descriptions.push_back(String::utf8(str != NULL ? str : "")); - + maximum_points.push_back(description.maximumPoints); - + hidden.push_back(description.hidden == YES); - + replayable.push_back(description.replayable == YES); } - + ret["names"] = names; ret["titles"] = titles; ret["unachieved_descriptions"] = unachieved_descriptions; @@ -220,7 +220,7 @@ void GameCenter::request_achievement_descriptions() { ret["maximum_points"] = maximum_points; ret["hidden"] = hidden; ret["replayable"] = replayable; - + } else { ret["result"] = "error"; ret["error_code"] = error.code; @@ -241,19 +241,19 @@ void GameCenter::request_achievements() { ret["result"] = "ok"; StringArray names; RealArray percentages; - + for (int i=0; i<[achievements count]; i++) { GKAchievement* achievement = [achievements objectAtIndex:i]; const char* str = [achievement.identifier UTF8String]; names.push_back(String::utf8(str != NULL ? str : "")); - + percentages.push_back(achievement.percentComplete); } - + ret["names"] = names; ret["progress"] = percentages; - + } else { ret["result"] = "error"; ret["error_code"] = error.code; @@ -275,7 +275,7 @@ void GameCenter::reset_achievements() { ret["result"] = "error"; ret["error_code"] = error.code; }; - + pending_events.push_back(ret); }]; }; @@ -311,7 +311,7 @@ Error GameCenter::show_game_center(Variant p_params) { ViewController *root_controller=(ViewController *)((AppDelegate *)[[UIApplication sharedApplication] delegate]).window.rootViewController; ERR_FAIL_COND_V(!root_controller, FAILED); - + controller.gameCenterDelegate = root_controller; controller.viewState = view_state; if (view_state == GKGameCenterViewControllerStateLeaderboards) { @@ -322,14 +322,14 @@ Error GameCenter::show_game_center(Variant p_params) { controller.leaderboardIdentifier = name_str; } } - + [root_controller presentViewController: controller animated: YES completion:nil]; - - return OK; + + return OK; }; void GameCenter::game_center_closed() { - + Dictionary ret; ret["type"] = "show_game_center"; ret["result"] = "ok"; diff --git a/platform/iphone/gl_view.mm b/platform/iphone/gl_view.mm index 94fbb9e174..607352ab0b 100755 --- a/platform/iphone/gl_view.mm +++ b/platform/iphone/gl_view.mm @@ -107,7 +107,7 @@ bool _play_video(String p_path, float p_volume, String p_audio_track, String p_s { NSString* language = [[track locale] localeIdentifier]; NSLog(@"subtitle lang: %@", language); - + if ([language isEqualToString:[NSString stringWithUTF8String:p_audio_track.utf8()]]) { AVMutableAudioMixInputParameters *audioInputParams = [AVMutableAudioMixInputParameters audioMixInputParameters]; @@ -132,7 +132,7 @@ bool _play_video(String p_path, float p_volume, String p_audio_track, String p_s { NSString* language = [[track locale] localeIdentifier]; NSLog(@"subtitle lang: %@", language); - + if ([language isEqualToString:[NSString stringWithUTF8String:p_subtitle_track.utf8()]]) { [_instance.avPlayer.currentItem selectMediaOption:track inMediaSelectionGroup: subtitlesGroup]; @@ -147,7 +147,7 @@ bool _play_video(String p_path, float p_volume, String p_audio_track, String p_s bool _is_video_playing() { if (_instance.avPlayer.error) { - printf("Error during playback\n"); + printf("Error during playback\n"); } return (_instance.avPlayer.rate > 0 && !_instance.avPlayer.error); } @@ -257,7 +257,7 @@ static void clear_touches() { if((self = [super initWithCoder:coder])) { self = [self initGLES]; - } + } return self; } @@ -265,14 +265,14 @@ static void clear_touches() { { // Get our backing layer CAEAGLLayer *eaglLayer = (CAEAGLLayer*) self.layer; - + // Configure it so that it is opaque, does not retain the contents of the backbuffer when displayed, and uses RGBA8888 color. eaglLayer.opaque = YES; eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:FALSE], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil]; - + // Create our EAGLContext, and if successful make it current and create our framebuffer. context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; @@ -281,7 +281,7 @@ static void clear_touches() { [self release]; return nil; } - + // Default the animation interval to 1/60th of a second. animationInterval = 1.0 / 60.0; return self; @@ -327,17 +327,17 @@ static void clear_touches() { glGenFramebuffersOES(1, &viewFramebuffer); glGenRenderbuffersOES(1, &viewRenderbuffer); - + glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer); glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); // This call associates the storage for the current render buffer with the EAGLDrawable (our CAEAGLLayer) // allowing us to draw into a buffer that will later be rendered to screen whereever the layer is (which corresponds with our view). [context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(id<EAGLDrawable>)self.layer]; glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer); - + glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth); glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight); - + // For this sample, we also need a depth buffer, so we'll create and attach one via another renderbuffer. glGenRenderbuffersOES(1, &depthRenderbuffer); glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer); @@ -371,7 +371,7 @@ static void clear_touches() { viewFramebuffer = 0; glDeleteRenderbuffersOES(1, &viewRenderbuffer); viewRenderbuffer = 0; - + if(depthRenderbuffer) { glDeleteRenderbuffersOES(1, &depthRenderbuffer); @@ -461,21 +461,21 @@ static void clear_touches() { // Make sure that you are drawing to the current context [EAGLContext setCurrentContext:context]; - + // If our drawing delegate needs to have the view setup, then call -setupView: and flag that it won't need to be called again. if(!delegateSetup) { [delegate setupView:self]; delegateSetup = YES; } - + glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer); [delegate drawView:self]; - + glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); [context presentRenderbuffer:GL_RENDERBUFFER_OES]; - + #ifdef DEBUG_ENABLED GLenum err = glGetError(); if(err) @@ -487,9 +487,9 @@ static void clear_touches() { { NSArray* tlist = [[event allTouches] allObjects]; for (unsigned int i=0; i< [tlist count]; i++) { - + if ( [touches containsObject:[tlist objectAtIndex:i]] ) { - + UITouch* touch = [tlist objectAtIndex:i]; if (touch.phase != UITouchPhaseBegan) continue; @@ -506,9 +506,9 @@ static void clear_touches() { NSArray* tlist = [[event allTouches] allObjects]; for (unsigned int i=0; i< [tlist count]; i++) { - + if ( [touches containsObject:[tlist objectAtIndex:i]] ) { - + UITouch* touch = [tlist objectAtIndex:i]; if (touch.phase != UITouchPhaseMoved) continue; @@ -527,9 +527,9 @@ static void clear_touches() { { NSArray* tlist = [[event allTouches] allObjects]; for (unsigned int i=0; i< [tlist count]; i++) { - + if ( [touches containsObject:[tlist objectAtIndex:i]] ) { - + UITouch* touch = [tlist objectAtIndex:i]; if (touch.phase != UITouchPhaseEnded) continue; @@ -543,7 +543,7 @@ static void clear_touches() { } - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { - + OSIPhone::get_singleton()->touches_cancelled(); clear_touches(); }; @@ -652,12 +652,12 @@ static void clear_touches() { - (void)dealloc { [self stopAnimation]; - + if([EAGLContext currentContext] == context) { [EAGLContext setCurrentContext:nil]; } - + [context release]; context = nil; @@ -673,8 +673,8 @@ static void clear_touches() { video_found_error = true; } - if(_instance.avPlayer.status == AVPlayerStatusReadyToPlay && - _instance.avPlayerItem.status == AVPlayerItemStatusReadyToPlay && + if(_instance.avPlayer.status == AVPlayerStatusReadyToPlay && + _instance.avPlayerItem.status == AVPlayerItemStatusReadyToPlay && CMTIME_COMPARE_INLINE(video_current_time, ==, kCMTimeZero)) { //NSLog(@"time: %@", video_current_time); @@ -703,7 +703,7 @@ static void clear_touches() { /* - (void)moviePlayBackDidFinish:(NSNotification*)notification { - + NSNumber* reason = [[notification userInfo] objectForKey:MPMoviePlayerPlaybackDidFinishReasonUserInfoKey]; switch ([reason intValue]) { diff --git a/platform/iphone/globals/global_defaults.h b/platform/iphone/globals/global_defaults.h index 25f1ae687e..305b600b43 100644 --- a/platform/iphone/globals/global_defaults.h +++ b/platform/iphone/globals/global_defaults.h @@ -1,3 +1,3 @@ -void register_iphone_global_defaults();
\ No newline at end of file +void register_iphone_global_defaults(); diff --git a/platform/iphone/icloud.mm b/platform/iphone/icloud.mm index 449670667f..0b3efe41fc 100644 --- a/platform/iphone/icloud.mm +++ b/platform/iphone/icloud.mm @@ -49,7 +49,7 @@ void ICloud::_bind_methods() { ObjectTypeDB::bind_method(_MD("get_key_value"),&ICloud::get_key_value); ObjectTypeDB::bind_method(_MD("synchronize_key_values"),&ICloud::synchronize_key_values); ObjectTypeDB::bind_method(_MD("get_all_key_values"),&ICloud::get_all_key_values); - + ObjectTypeDB::bind_method(_MD("get_pending_event_count"),&ICloud::get_pending_event_count); ObjectTypeDB::bind_method(_MD("pop_pending_event"),&ICloud::pop_pending_event); }; @@ -83,7 +83,7 @@ Variant nsobject_to_variant(NSObject* object) { if ([data length] > 0) { ret.resize([data length]); { - ByteArray::Write w = ret.write(); + ByteArray::Write w = ret.write(); copymem(w.ptr(), [data bytes], [data length]); } } @@ -101,14 +101,14 @@ Variant nsobject_to_variant(NSObject* object) { else if ([object isKindOfClass:[NSDictionary class]]) { Dictionary result; NSDictionary* dic = (NSDictionary*)object; - - + + NSArray* keys = [dic allKeys]; int count = [keys count]; for (int i=0; i < count; ++i) { NSObject* k = [ keys objectAtIndex:i]; NSObject* v = [dic objectForKey:k]; - + result[nsobject_to_variant(k)] = nsobject_to_variant(v); } return result; @@ -121,29 +121,29 @@ Variant nsobject_to_variant(NSObject* object) { NSNumber* num = (NSNumber*)object; if(strcmp([num objCType], @encode(BOOL)) == 0) { return Variant((int)[num boolValue]); - } + } else if(strcmp([num objCType], @encode(char)) == 0) { return Variant((int)[num charValue]); - } + } else if(strcmp([num objCType], @encode(int)) == 0) { return Variant([num intValue]); - } + } else if(strcmp([num objCType], @encode(unsigned int)) == 0) { return Variant((int)[num unsignedIntValue]); - } + } else if(strcmp([num objCType], @encode(long long)) == 0) { return Variant((int)[num longValue]); - } + } else if(strcmp([num objCType], @encode(float)) == 0) { return Variant([num floatValue]); - } + } else if(strcmp([num objCType], @encode(double)) == 0) { return Variant((float)[num doubleValue]); } } else if ([object isKindOfClass:[NSDate class]]) { //this is a type that icloud supports...but how did you submit it in the first place? - //I guess this is a type that *might* show up, if you were, say, trying to make your game + //I guess this is a type that *might* show up, if you were, say, trying to make your game //compatible with existing cloud data written by another engine's version of your game WARN_PRINT("NSDate unsupported, returning null Variant") return Variant(); @@ -177,13 +177,13 @@ NSObject* variant_to_nsobject(Variant v) { for (unsigned int i = 0; i < keys.size(); ++i) { NSString* key = [[[NSString alloc] initWithUTF8String:((String)(keys[i])).utf8().get_data()] autorelease]; NSObject* value = variant_to_nsobject(dic[keys[i]]); - + if (key == NULL || value == NULL) { return NULL; } - + [result setObject:value forKey:key]; - } + } return result; } else if (v.get_type() == Variant::ARRAY) { @@ -213,13 +213,13 @@ NSObject* variant_to_nsobject(Variant v) { Error ICloud::remove_key(Variant p_param) { String param = p_param; NSString* key = [[[NSString alloc] initWithUTF8String:param.utf8().get_data()] autorelease]; - + NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore]; - + if (![[store dictionaryRepresentation] objectForKey:key]) { return ERR_INVALID_PARAMETER; } - + [store removeObjectForKey:key]; return OK; } @@ -228,26 +228,26 @@ Error ICloud::remove_key(Variant p_param) { Variant ICloud::set_key_values(Variant p_params) { Dictionary params = p_params; Array keys = params.keys(); - + Array error_keys; - + for (unsigned int i = 0; i < keys.size(); ++i) { String variant_key = keys[i]; Variant variant_value = params[variant_key]; - + NSString* key = [[[NSString alloc] initWithUTF8String:variant_key.utf8().get_data()] autorelease]; if (key == NULL) { error_keys.push_back(variant_key); continue; } - + NSObject* value = variant_to_nsobject(variant_value); - + if (value == NULL) { error_keys.push_back(variant_key); continue; } - + NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore]; [store setObject:value forKey:key]; } @@ -257,37 +257,37 @@ Variant ICloud::set_key_values(Variant p_params) { Variant ICloud::get_key_value(Variant p_param) { String param = p_param; - + NSString* key = [[[NSString alloc] initWithUTF8String:param.utf8().get_data()] autorelease]; NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore]; - + if (![[store dictionaryRepresentation] objectForKey:key]) { return Variant(); } - + Variant result = nsobject_to_variant([[store dictionaryRepresentation] objectForKey:key]); - + return result; } Variant ICloud::get_all_key_values() { Dictionary result; - + NSUbiquitousKeyValueStore* store = [NSUbiquitousKeyValueStore defaultStore]; NSDictionary* store_dictionary = [store dictionaryRepresentation]; - + NSArray* keys = [store_dictionary allKeys]; int count = [keys count]; for (int i=0; i < count; ++i) { NSString* k = [ keys objectAtIndex:i]; NSObject* v = [store_dictionary objectForKey:k]; - + const char* str = [k UTF8String]; if (str != NULL) { result[String::utf8(str)] = nsobject_to_variant(v); } } - + return result; } @@ -320,7 +320,7 @@ ICloud::ICloud() { ERR_FAIL_COND(instance != NULL); instance = this; //connected = false; - + [ //[NSNotificationCenter defaultCenter] addObserverForName: @"notify" [NSNotificationCenter defaultCenter] addObserverForName: NSUbiquitousKeyValueStoreDidChangeExternallyNotification @@ -337,7 +337,7 @@ ICloud::ICloud() { //Array result_values; Dictionary keyValues; String reason = ""; - + if (change == NSUbiquitousKeyValueStoreServerChange) { reason = "server"; } @@ -350,27 +350,27 @@ ICloud::ICloud() { else if (change == NSUbiquitousKeyValueStoreAccountChange) { reason = "account"; } - + ret["reason"] = reason; - - + + NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore]; - + NSArray * keys = [userInfo objectForKey:NSUbiquitousKeyValueStoreChangedKeysKey]; for (NSString* key in keys) { const char* str = [key UTF8String]; if (str == NULL) { continue; } - + NSObject* object = [store objectForKey:key]; - + //figure out what kind of object it is Variant value = nsobject_to_variant(object); - + keyValues[String::utf8(str)] = value; } - + ret["changed_values"] = keyValues; pending_events.push_back(ret); } diff --git a/platform/iphone/in_app_store.mm b/platform/iphone/in_app_store.mm index fec007b7b6..a2efe2711b 100644 --- a/platform/iphone/in_app_store.mm +++ b/platform/iphone/in_app_store.mm @@ -179,35 +179,35 @@ Error InAppStore::request_product_info(Variant p_params) { ret["result"] = "ok"; ret["product_id"] = pid; ret["transaction_id"] = transactionId; - + NSData* receipt = nil; int sdk_version = 6; - + if([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0){ - + NSURL *receiptFileURL = nil; NSBundle *bundle = [NSBundle mainBundle]; if ([bundle respondsToSelector:@selector(appStoreReceiptURL)]) { - + // Get the transaction receipt file path location in the app bundle. receiptFileURL = [bundle appStoreReceiptURL]; - + // Read in the contents of the transaction file. receipt = [NSData dataWithContentsOfURL:receiptFileURL]; sdk_version = 7; - + } else { // Fall back to deprecated transaction receipt, // which is still available in iOS 7. - + // Use SKPaymentTransaction's transactionReceipt. receipt = transaction.transactionReceipt; } - + }else{ receipt = transaction.transactionReceipt; } - + NSString* receipt_to_send = nil; if (receipt != nil) { @@ -217,16 +217,16 @@ Error InAppStore::request_product_info(Variant p_params) { receipt_ret["receipt"] = String::utf8(receipt_to_send != nil ? [receipt_to_send UTF8String] : ""); receipt_ret["sdk"] = sdk_version; ret["receipt"] = receipt_ret; - + InAppStore::get_singleton()->_post_event(ret); - + if (auto_finish_transactions){ [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; } else{ [pending_transactions setObject:transaction forKey:transaction.payment.productIdentifier]; } - + #ifdef MODULE_FUSEBOXX_ENABLED printf("Registering transaction on Fuseboxx!\n"); [FuseSDK registerInAppPurchase: transaction]; @@ -251,7 +251,7 @@ Error InAppStore::request_product_info(Variant p_params) { default: printf("status default %i!\n", (int)transaction.transactionState); - + break; }; }; @@ -322,7 +322,7 @@ InAppStore::InAppStore() { void InAppStore::finish_transaction(String product_id){ NSString* prod_id = [NSString stringWithCString:product_id.utf8().get_data() encoding:NSUTF8StringEncoding]; - + if ([pending_transactions objectForKey:prod_id]){ [[SKPaymentQueue defaultQueue] finishTransaction:[pending_transactions objectForKey:prod_id]]; [pending_transactions removeObjectForKey:prod_id]; diff --git a/platform/iphone/ios.h b/platform/iphone/ios.h new file mode 100644 index 0000000000..0e4661520b --- /dev/null +++ b/platform/iphone/ios.h @@ -0,0 +1,20 @@ +#ifndef IOS_H +#define IOS_H + +#include "core/object.h" + +class iOS : public Object { + + OBJ_TYPE(iOS, Object); + + static void _bind_methods(); + +public: + + String get_rate_url(int p_app_id) const; + + iOS(); + +}; + +#endif diff --git a/platform/iphone/ios.mm b/platform/iphone/ios.mm new file mode 100644 index 0000000000..deb63feacf --- /dev/null +++ b/platform/iphone/ios.mm @@ -0,0 +1,36 @@ +#include "ios.h" + +#import <UIKit/UIKit.h> + +void iOS::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("get_rate_url","app_id"),&iOS::get_rate_url); +}; + +String iOS::get_rate_url(int p_app_id) const { + String templ = "itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=APP_ID"; + String templ_iOS7 = "itms-apps://itunes.apple.com/app/idAPP_ID"; + String templ_iOS8 = "itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?id=APP_ID&onlyLatestVersion=true&pageNumber=0&sortOrdering=1&type=Purple+Software"; + + //ios7 before + String ret = templ; + + // iOS 7 needs a different templateReviewURL @see https://github.com/arashpayan/appirater/issues/131 + if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0 && [[[UIDevice currentDevice] systemVersion] floatValue] < 7.1) + { + ret = templ_iOS7; + } + // iOS 8 needs a different templateReviewURL also @see https://github.com/arashpayan/appirater/issues/182 + else if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) + { + ret = templ_iOS8; + } + + // ios7 for everything? + ret = templ_iOS7.replace("APP_ID", String::num(p_app_id)); + + printf("returning rate url %ls\n", ret.c_str()); + return ret; +}; + +iOS::iOS() {}; diff --git a/platform/iphone/os_iphone.cpp b/platform/iphone/os_iphone.cpp index 56dffc8aa4..93496e8225 100644 --- a/platform/iphone/os_iphone.cpp +++ b/platform/iphone/os_iphone.cpp @@ -46,6 +46,8 @@ #include "sem_iphone.h" +#include "ios.h" + int OSIPhone::get_video_driver_count() const { return 1; @@ -167,6 +169,7 @@ void OSIPhone::initialize(const VideoMode& p_desired,int p_video_driver,int p_au Globals::get_singleton()->add_singleton(Globals::Singleton("ICloud", icloud)); //icloud->connect(); #endif + Globals::get_singleton()->add_singleton(Globals::Singleton("iOS", memnew(iOS))); }; MainLoop *OSIPhone::get_main_loop() const { diff --git a/platform/iphone/rasterizer_iphone.cpp b/platform/iphone/rasterizer_iphone.cpp index f06f12547f..99e83343d0 100644 --- a/platform/iphone/rasterizer_iphone.cpp +++ b/platform/iphone/rasterizer_iphone.cpp @@ -2446,7 +2446,7 @@ void RasterizerIPhone::canvas_draw_rect(const Rect2& p_rect, bool p_region, cons } -void RasterizerIPhone::canvas_draw_style_box(const Rect2& p_rect, RID p_texture,const float *p_margin, bool p_draw_center) { +void RasterizerIPhone::canvas_draw_style_box(const Rect2& p_rect, const Rect2& p_src_region, RID p_texture,const float *p_margin, bool p_draw_center) { glColor4f(1, 1, 1, 1); @@ -2458,52 +2458,56 @@ void RasterizerIPhone::canvas_draw_style_box(const Rect2& p_rect, RID p_texture, glBindTexture( GL_TEXTURE_2D,texture->tex_id ); + Rect2 region = p_src_region; + if (region.size.width <= 0 ) + region.size.width = texture->width; + if (region.size.height <= 0) + region.size.height = texture->height; /* CORNERS */ - _draw_textured_quad( // top left Rect2( p_rect.pos, Size2(p_margin[MARGIN_LEFT],p_margin[MARGIN_TOP])), - Rect2( Point2(), Size2(p_margin[MARGIN_LEFT],p_margin[MARGIN_TOP])), + Rect2( region.pos, Size2(p_margin[MARGIN_LEFT],p_margin[MARGIN_TOP])), Size2( texture->width, texture->height ) ); _draw_textured_quad( // top right Rect2( Point2( p_rect.pos.x + p_rect.size.width - p_margin[MARGIN_RIGHT], p_rect.pos.y), Size2(p_margin[MARGIN_RIGHT],p_margin[MARGIN_TOP])), - Rect2( Point2(texture->width-p_margin[MARGIN_RIGHT],0), Size2(p_margin[MARGIN_RIGHT],p_margin[MARGIN_TOP])), + Rect2( Point2(region.pos.x+region.size.width-p_margin[MARGIN_RIGHT], region.pos.y), Size2(p_margin[MARGIN_RIGHT],p_margin[MARGIN_TOP])), Size2( texture->width, texture->height ) ); _draw_textured_quad( // bottom left Rect2( Point2(p_rect.pos.x,p_rect.pos.y + p_rect.size.height - p_margin[MARGIN_BOTTOM]), Size2(p_margin[MARGIN_LEFT],p_margin[MARGIN_BOTTOM])), - Rect2( Point2(0,texture->height-p_margin[MARGIN_BOTTOM]), Size2(p_margin[MARGIN_LEFT],p_margin[MARGIN_BOTTOM])), + Rect2( Point2(region.pos.x, region.pos.y+region.size.height-p_margin[MARGIN_BOTTOM]), Size2(p_margin[MARGIN_LEFT],p_margin[MARGIN_BOTTOM])), Size2( texture->width, texture->height ) ); _draw_textured_quad( // bottom right Rect2( Point2( p_rect.pos.x + p_rect.size.width - p_margin[MARGIN_RIGHT], p_rect.pos.y + p_rect.size.height - p_margin[MARGIN_BOTTOM]), Size2(p_margin[MARGIN_RIGHT],p_margin[MARGIN_BOTTOM])), - Rect2( Point2(texture->width-p_margin[MARGIN_RIGHT],texture->height-p_margin[MARGIN_BOTTOM]), Size2(p_margin[MARGIN_RIGHT],p_margin[MARGIN_BOTTOM])), + Rect2( Point2(region.pos.x+region.size.width-p_margin[MARGIN_RIGHT], region.pos.y+region.size.height-p_margin[MARGIN_BOTTOM]), Size2(p_margin[MARGIN_RIGHT],p_margin[MARGIN_BOTTOM])), Size2( texture->width, texture->height ) ); Rect2 rect_center( p_rect.pos+Point2( p_margin[MARGIN_LEFT], p_margin[MARGIN_TOP]), Size2( p_rect.size.width - p_margin[MARGIN_LEFT] - p_margin[MARGIN_RIGHT], p_rect.size.height - p_margin[MARGIN_TOP] - p_margin[MARGIN_BOTTOM] )); - Rect2 src_center( Point2( p_margin[MARGIN_LEFT], p_margin[MARGIN_TOP]), Size2( texture->width - p_margin[MARGIN_LEFT] - p_margin[MARGIN_RIGHT], texture->height - p_margin[MARGIN_TOP] - p_margin[MARGIN_BOTTOM] )); + Rect2 src_center( Point2(region.pos.x+p_margin[MARGIN_LEFT], region.pos.y+p_margin[MARGIN_TOP]), Size2(region.size.width - p_margin[MARGIN_LEFT] - p_margin[MARGIN_RIGHT], region.size.height - p_margin[MARGIN_TOP] - p_margin[MARGIN_BOTTOM] )); _draw_textured_quad( // top Rect2( Point2(rect_center.pos.x,p_rect.pos.y),Size2(rect_center.size.width,p_margin[MARGIN_TOP])), - Rect2( Point2(p_margin[MARGIN_LEFT],0), Size2(src_center.size.width,p_margin[MARGIN_TOP])), + Rect2( Point2(src_center.pos.x,region.pos.y), Size2(src_center.size.width,p_margin[MARGIN_TOP])), Size2( texture->width, texture->height ) ); _draw_textured_quad( // bottom Rect2( Point2(rect_center.pos.x,rect_center.pos.y+rect_center.size.height),Size2(rect_center.size.width,p_margin[MARGIN_BOTTOM])), - Rect2( Point2(p_margin[MARGIN_LEFT],src_center.pos.y+src_center.size.height), Size2(src_center.size.width,p_margin[MARGIN_BOTTOM])), + Rect2( Point2(src_center.pos.x,src_center.pos.y+src_center.size.height), Size2(src_center.size.width,p_margin[MARGIN_BOTTOM])), Size2( texture->width, texture->height ) ); _draw_textured_quad( // left Rect2( Point2(p_rect.pos.x,rect_center.pos.y),Size2(p_margin[MARGIN_LEFT],rect_center.size.height)), - Rect2( Point2(0,p_margin[MARGIN_TOP]), Size2(p_margin[MARGIN_LEFT],src_center.size.height)), + Rect2( Point2(region.pos.x,region.pos.y+p_margin[MARGIN_TOP]), Size2(p_margin[MARGIN_LEFT],src_center.size.height)), Size2( texture->width, texture->height ) ); _draw_textured_quad( // right Rect2( Point2(rect_center.pos.x+rect_center.size.width,rect_center.pos.y),Size2(p_margin[MARGIN_RIGHT],rect_center.size.height)), - Rect2( Point2(src_center.pos.x+src_center.size.width,p_margin[MARGIN_TOP]), Size2(p_margin[MARGIN_RIGHT],src_center.size.height)), + Rect2( Point2(src_center.pos.x+src_center.size.width,region.pos.y+p_margin[MARGIN_TOP]), Size2(p_margin[MARGIN_RIGHT],src_center.size.height)), Size2( texture->width, texture->height ) ); if (p_draw_center) { |