You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
PlumberUltimateAds/Assets/Plugins/iOS/GoogleSignIn/GoogleSignIn.mm

367 lines
12 KiB
Plaintext

/**
* Copyright 2017 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "GoogleSignIn.h"
#import <GoogleSignIn/GIDAuthentication.h>
#import <GoogleSignIn/GIDGoogleUser.h>
#import <GoogleSignIn/GIDProfileData.h>
#import <GoogleSignIn/GIDSignIn.h>
#import <UnityAppController.h>
#import "UnityInterface.h"
#import <memory>
// These values are in the Unity plugin code. The iOS specific
// codes are mapped to these.
static const int kStatusCodeSuccessCached = -1;
static const int kStatusCodeSuccess = 0;
static const int kStatusCodeApiNotConnected = 1;
static const int kStatusCodeCanceled = 2;
static const int kStatusCodeInterrupted = 3;
static const int kStatusCodeInvalidAccount = 4;
static const int kStatusCodeTimeout = 5;
static const int kStatusCodeDeveloperError = 6;
static const int kStatusCodeInternalError = 7;
static const int kStatusCodeNetworkError = 8;
static const int kStatusCodeError = 9;
/**
* Helper method to pause the Unity player. This is done when showing any UI.
*/
void UnpauseUnityPlayer() {
dispatch_async(dispatch_get_main_queue(), ^{
if (UnityIsPaused() > 0) {
UnityPause(0);
}
});
}
// result for pending operation. Access to this should be protected using the
// resultLock.
struct SignInResult {
int result_code;
bool finished;
};
std::unique_ptr<SignInResult> currentResult_;
NSRecursiveLock *resultLock = [NSRecursiveLock alloc];
@implementation GoogleSignInHandler
GIDConfiguration* signInConfiguration = nil;
NSString* loginHint = nil;
NSMutableArray* additionalScopes = nil;
+ (GoogleSignInHandler *)sharedInstance {
static dispatch_once_t once;
static GoogleSignInHandler *sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [self alloc];
});
return sharedInstance;
}
/**
* Overload the presenting of the UI so we can pause the Unity player.
*/
- (void)signIn:(GIDSignIn *)signIn
presentViewController:(UIViewController *)viewController {
UnityPause(true);
[UnityGetGLViewController() presentViewController:viewController
animated:YES
completion:nil];
}
/**
* Overload the dismissing so we can resume the Unity player.
*/
- (void)signIn:(GIDSignIn *)signIn
dismissViewController:(UIViewController *)viewController {
UnityPause(false);
[UnityGetGLViewController() dismissViewControllerAnimated:YES completion:nil];
}
/**
* The sign-in flow has finished and was successful if |error| is |nil|.
* Map the errors from the iOS SDK back to the Android values for consistency's
* sake in the Unity layer.
*/
- (void)signIn:(GIDSignIn *)signIn
didSignInForUser:(GIDGoogleUser *)user
withError:(NSError *)_error {
if (_error == nil) {
if (currentResult_) {
currentResult_->result_code = kStatusCodeSuccess;
currentResult_->finished = true;
} else {
NSLog(@"No currentResult to set status on!");
}
NSLog(@"didSignInForUser: SUCCESS");
} else {
NSLog(@"didSignInForUser: %@", _error.localizedDescription);
if (currentResult_) {
switch (_error.code) {
case kGIDSignInErrorCodeUnknown:
currentResult_->result_code = kStatusCodeError;
break;
case kGIDSignInErrorCodeKeychain:
currentResult_->result_code = kStatusCodeInternalError;
break;
case kGIDSignInErrorCodeHasNoAuthInKeychain:
currentResult_->result_code = kStatusCodeError;
break;
case kGIDSignInErrorCodeCanceled:
currentResult_->result_code = kStatusCodeCanceled;
break;
default:
NSLog(@"Unmapped error code: %ld, returning Error",
static_cast<long>(_error.code));
currentResult_->result_code = kStatusCodeError;
}
currentResult_->finished = true;
UnpauseUnityPlayer();
} else {
NSLog(@"No currentResult to set status on!");
}
}
}
// Finished disconnecting |user| from the app successfully if |error| is |nil|.
- (void)signIn:(GIDSignIn *)signIn
didDisconnectWithUser:(GIDGoogleUser *)user
withError:(NSError *)_error {
if (_error == nil) {
NSLog(@"didDisconnectWithUser: SUCCESS");
} else {
NSLog(@"didDisconnectWithUser: %@", _error);
}
}
@end
/**
* These are the external "C" methods that are imported by the Unity C# code.
* The parameters are intended to be primative, easy to marshall.
*/
extern "C" {
/**
* This method does nothing in the iOS implementation. It is here
* to make the API uniform between Android and iOS.
*/
void *GoogleSignIn_Create(void *data) { return NULL; }
void GoogleSignIn_EnableDebugLogging(void *unused, bool flag) {
if (flag) {
NSLog(@"GoogleSignIn: No optional logging available on iOS");
}
}
/**
* Configures the GIDSignIn instance. The first parameter is unused in iOS.
* It is here to make the API between Android and iOS uniform.
*/
bool GoogleSignIn_Configure(void *unused, bool useGameSignIn,
const char *webClientId, bool requestAuthCode,
bool forceTokenRefresh, bool requestEmail,
bool requestIdToken, bool hidePopups,
const char **additionalScopes, int scopeCount,
const char *accountName) {
NSString *path = [[NSBundle mainBundle] pathForResource:@"GoogleService-Info" ofType:@"plist"];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
NSString *clientId = [dict objectForKey:@"CLIENT_ID"];
GIDConfiguration* config = [[GIDConfiguration alloc] initWithClientID:clientId];
if (webClientId) {
config = [[GIDConfiguration alloc] initWithClientID:clientId serverClientID:[NSString stringWithUTF8String:webClientId]];
}
[GoogleSignInHandler sharedInstance]->signInConfiguration = config;
int scopeSize = scopeCount;
if (scopeSize) {
NSMutableArray *tmpary = [[NSMutableArray alloc] initWithCapacity:scopeSize];
for (int i = 0; i < scopeCount; i++) {
[tmpary addObject:[NSString stringWithUTF8String:additionalScopes[i]]];
}
[GoogleSignInHandler sharedInstance]->additionalScopes = tmpary;
}
if (accountName) {
[GoogleSignInHandler sharedInstance]->loginHint = [NSString stringWithUTF8String:accountName];
}
return !useGameSignIn;
}
/**
Starts the sign-in process. Returns and error result if error, null otherwise.
*/
static SignInResult *startSignIn() {
bool busy = false;
[resultLock lock];
if (!currentResult_ || currentResult_->finished) {
currentResult_.reset(new SignInResult());
currentResult_->result_code = 0;
currentResult_->finished = false;
} else {
busy = true;
}
[resultLock unlock];
if (busy) {
NSLog(@"ERROR: There is already a pending sign-in operation.");
// Returned to the caller, should be deleted by calling
// GoogleSignIn_DisposeFuture().
return new SignInResult{.result_code = kStatusCodeDeveloperError,
.finished = true};
}
return nullptr;
}
/**
* Sign-In. The return value is a pointer to the currentResult object.
*/
void *GoogleSignIn_SignIn() {
SignInResult *result = startSignIn();
if (!result) {
[[GIDSignIn sharedInstance] signInWithConfiguration:[GoogleSignInHandler sharedInstance]->signInConfiguration
presentingViewController:UnityGetGLViewController()
hint:[GoogleSignInHandler sharedInstance]->loginHint
callback:^(GIDGoogleUser *user, NSError *error) {
[[GoogleSignInHandler sharedInstance] signIn:[GIDSignIn sharedInstance] didSignInForUser:user withError:error];
}];
result = currentResult_.get();
}
return result;
}
/**
* Attempt a silent sign-in. Return value is the pointer to the currentResult
* object.
*/
void *GoogleSignIn_SignInSilently() {
SignInResult *result = startSignIn();
if (!result) {
[[GIDSignIn sharedInstance] restorePreviousSignInWithCallback:^(GIDGoogleUser *user, NSError *error) {
[[GoogleSignInHandler sharedInstance] signIn:[GIDSignIn sharedInstance] didSignInForUser:user withError:error];
}];
result = currentResult_.get();
}
return result;
}
void GoogleSignIn_Signout() {
GIDSignIn *signIn = [GIDSignIn sharedInstance];
[signIn signOut];
}
void GoogleSignIn_Disconnect() {
GIDSignIn *signIn = [GIDSignIn sharedInstance];
[signIn disconnectWithCallback:^(NSError *error) {
[[GoogleSignInHandler sharedInstance] signIn:[GIDSignIn sharedInstance] didDisconnectWithUser:nil withError:error];
}];
}
bool GoogleSignIn_Pending(SignInResult *result) {
volatile bool ret;
[resultLock lock];
ret = !result->finished;
[resultLock unlock];
return ret;
}
GIDGoogleUser *GoogleSignIn_Result(SignInResult *result) {
if (result && result->finished) {
GIDGoogleUser *guser = [GIDSignIn sharedInstance].currentUser;
return guser;
}
return nullptr;
}
int GoogleSignIn_Status(SignInResult *result) {
if (result) {
return result->result_code;
}
return kStatusCodeDeveloperError;
}
void GoogleSignIn_DisposeFuture(SignInResult *result) {
if (result == currentResult_.get()) {
currentResult_.reset(nullptr);
} else {
delete result;
}
}
/**
* Private helper function to copy NSString to char*. If the destination is
* non-null, the contents of src are copied up to len bytes (using strncpy). The
* then len is returned. Otherwise returns length of the string to copy + 1.
*/
static size_t CopyNSString(NSString *src, char *dest, size_t len) {
if (dest && src && len) {
const char *string = [src UTF8String];
strncpy(dest, string, len);
return len;
}
return src ? src.length + 1 : 0;
}
size_t GoogleSignIn_GetServerAuthCode(GIDGoogleUser *guser, char *buf,
size_t len) {
NSString *val = [guser serverAuthCode];
return CopyNSString(val, buf, len);
}
size_t GoogleSignIn_GetDisplayName(GIDGoogleUser *guser, char *buf,
size_t len) {
NSString *val = [guser.profile name];
return CopyNSString(val, buf, len);
}
size_t GoogleSignIn_GetEmail(GIDGoogleUser *guser, char *buf, size_t len) {
NSString *val = [guser.profile email];
return CopyNSString(val, buf, len);
}
size_t GoogleSignIn_GetFamilyName(GIDGoogleUser *guser, char *buf, size_t len) {
NSString *val = [guser.profile familyName];
return CopyNSString(val, buf, len);
}
size_t GoogleSignIn_GetGivenName(GIDGoogleUser *guser, char *buf, size_t len) {
NSString *val = [guser.profile givenName];
return CopyNSString(val, buf, len);
}
size_t GoogleSignIn_GetIdToken(GIDGoogleUser *guser, char *buf, size_t len) {
NSString *val = [guser.authentication idToken];
return CopyNSString(val, buf, len);
}
size_t GoogleSignIn_GetImageUrl(GIDGoogleUser *guser, char *buf, size_t len) {
NSURL *url = [guser.profile imageURLWithDimension:128];
NSString *val = url ? [url absoluteString] : nullptr;
return CopyNSString(val, buf, len);
}
size_t GoogleSignIn_GetUserId(GIDGoogleUser *guser, char *buf, size_t len) {
NSString *val = [guser userID];
return CopyNSString(val, buf, len);
}
} // extern "C"