summaryrefslogtreecommitdiff
path: root/platform/iphone
diff options
context:
space:
mode:
Diffstat (limited to 'platform/iphone')
-rw-r--r--platform/iphone/Appirater.m553
-rw-r--r--platform/iphone/AppiraterDelegate.h23
-rw-r--r--platform/iphone/SCsub7
-rw-r--r--platform/iphone/app_delegate.mm30
-rw-r--r--platform/iphone/detect.py6
-rw-r--r--platform/iphone/game_center.mm46
-rwxr-xr-xplatform/iphone/gl_view.mm54
-rw-r--r--platform/iphone/globals/global_defaults.h2
-rw-r--r--platform/iphone/icloud.mm84
-rw-r--r--platform/iphone/in_app_store.mm28
-rw-r--r--platform/iphone/ios.h20
-rw-r--r--platform/iphone/ios.mm36
-rw-r--r--platform/iphone/os_iphone.cpp3
-rw-r--r--platform/iphone/rasterizer_iphone.cpp26
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) {