diff --git a/README.md b/README.md index a4f6854d..1c0e301d 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,20 @@ $ cordova plugin add https://github.com/EddyVerbruggen/cordova-plugin-googleplus $ cordova prepare ``` +EXTRA VARIABLES: + +If you need to install a specific version of `GoogleSignIn` library using pod you can pass it as a variable. +This is optional, if this variable is not set the default version will be used. +``` +--variable GOOGLE_SIGN_IN_VERSION="~> 6.2.3" +``` + +If you need to install a specific version of `GoogleUtilities` library using pod you can pass it as a variable. +This is optional, if this variable is not set the default version will be used. +``` +--variable GOOGLE_UTILITIES_VERSION="~> 7.4" +``` + IMPORTANT: * _Please note that `myreversedclientid` is a place holder for the reversed clientId you find in your iOS configuration file. Do not surround this value with quotes. **(iOS only Applications)**_ @@ -183,13 +197,10 @@ document.addEventListener('deviceready', deviceReady, false); function deviceReady() { //I get called when everything's ready for the plugin to be called! console.log('Device is ready!'); - window.plugins.googleplus.trySilentLogin(...); + window.plugins.googleplus.login(...); } ``` -### isAvailable -3/31/16: This method is no longer required to be checked first. It is kept for code orthoganality. - ### Login The login function walks the user through the Google Auth process. All parameters are optional, however there are a few caveats. @@ -238,31 +249,6 @@ On Android, the error callback (third argument) receives an error status code if On iOS, the error callback will include an [NSError localizedDescription](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSError_Class/). -### Try silent login -You can call `trySilentLogin` to check if they're already signed in to the app and sign them in silently if they are. - -If it succeeds you will get the same object as the `login` function gets, -but if it fails it will not show the authentication dialog to the user. - -Calling `trySilentLogin` is done the same as `login`, except for the function name. -```javascript -window.plugins.googleplus.trySilentLogin( - { - 'scopes': '... ', // optional - space-separated list of scopes, If not included or empty, defaults to `profile` and `email`. - 'webClientId': 'client id of the web app/server side', // optional - clientId of your Web application from Credentials settings of your project - On Android, this MUST be included to get an idToken. On iOS, it is not required. - 'offline': true, // Optional, but requires the webClientId - if set to true the plugin will also return a serverAuthCode, which can be used to grant offline access to a non-Google server - }, - function (obj) { - alert(JSON.stringify(obj)); // do something useful instead of alerting - }, - function (msg) { - alert('error: ' + msg); - } -); -``` - -It is strongly recommended that trySilentLogin is implemented with the same options as login, to avoid any potential complications. - ### logout This will clear the OAuth2 token. ``` javascript diff --git a/plugin.xml b/plugin.xml index c2039408..d5e44c49 100755 --- a/plugin.xml +++ b/plugin.xml @@ -2,7 +2,7 @@ + version="9.0.0"> Google SignIn @@ -30,9 +30,10 @@ - - - + + + + @@ -102,17 +103,19 @@ + + - - + + - + diff --git a/src/android/GooglePlus.java b/src/android/GooglePlus.java index c89e1450..06bda576 100644 --- a/src/android/GooglePlus.java +++ b/src/android/GooglePlus.java @@ -3,8 +3,6 @@ import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AccountManagerFuture; -import android.accounts.AuthenticatorException; -import android.accounts.OperationCanceledException; import android.app.Activity; import android.content.Intent; import android.content.pm.PackageInfo; @@ -24,7 +22,6 @@ import com.google.android.gms.common.api.Scope; import org.apache.cordova.*; -import org.apache.cordova.engine.SystemWebChromeClient; import org.json.JSONException; import org.json.JSONObject; @@ -38,6 +35,12 @@ import java.security.MessageDigest; import android.content.pm.Signature; +import androidx.activity.result.ActivityResult; +import androidx.activity.result.ActivityResultCallback; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; +import androidx.appcompat.app.AppCompatActivity; + /** * Originally written by Eddy Verbruggen (http://github.com/EddyVerbruggen/cordova-plugin-googleplus) * Forked/Duplicated and Modified by PointSource, LLC, 2016. @@ -70,9 +73,25 @@ public class GooglePlus extends CordovaPlugin implements GoogleApiClient.OnConne private GoogleApiClient mGoogleApiClient; private CallbackContext savedCallbackContext; + private ActivityResultLauncher signInActivityLauncher; + @Override public void initialize(CordovaInterface cordova, CordovaWebView webView) { super.initialize(cordova, webView); + + AppCompatActivity cordovaActivity = cordova.getActivity(); + + this.signInActivityLauncher = cordovaActivity.registerForActivityResult( + new ActivityResultContracts.StartActivityForResult(), + new ActivityResultCallback() { + public void onActivityResult(ActivityResult result) { + Log.i(TAG, "One of our activities finished up"); + // Call handleSignInResult passing in sign in result object + Intent intent = result.getData(); + handleSignInResult(Auth.GoogleSignInApi.getSignInResultFromIntent(intent)); + } + }); + } @Override @@ -89,7 +108,6 @@ public boolean execute(String action, CordovaArgs args, CallbackContext callback // Tries to Log the user in Log.i(TAG, "Trying to Log in!"); - cordova.setActivityResultCallback(this); //sets this class instance to be an activity result listener signIn(); } else if (ACTION_TRY_SILENT_LOGIN.equals(action)) { @@ -194,7 +212,7 @@ private synchronized void buildGoogleApiClient(JSONObject clientOptions) throws */ private void signIn() { Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(this.mGoogleApiClient); - cordova.getActivity().startActivityForResult(signInIntent, RC_GOOGLEPLUS); + this.signInActivityLauncher.launch(signInIntent); } /** @@ -274,31 +292,6 @@ public void onConnectionFailed(ConnectionResult result) { savedCallbackContext.error(result.getErrorCode()); } - /** - * Listens for and responds to an activity result. If the activity result request code matches our own, - * we know that the sign in Intent that we started has completed. - * - * The result is retrieved and send to the handleSignInResult function. - * - * @param requestCode The request code originally supplied to startActivityForResult(), - * @param resultCode The integer result code returned by the child activity through its setResult(). - * @param intent Information returned by the child activity - */ - @Override - public void onActivityResult(int requestCode, final int resultCode, final Intent intent) { - super.onActivityResult(requestCode, resultCode, intent); - - Log.i(TAG, "In onActivityResult"); - - if (requestCode == RC_GOOGLEPLUS) { - Log.i(TAG, "One of our activities finished up"); - //Call handleSignInResult passing in sign in result object - handleSignInResult(Auth.GoogleSignInApi.getSignInResultFromIntent(intent)); - } - else { - Log.i(TAG, "This wasn't one of our activities"); - } - } /** * Function for handling the sign in result diff --git a/src/ios/GooglePlus.h b/src/ios/GooglePlus.h index ff0fb9bc..a85b504d 100644 --- a/src/ios/GooglePlus.h +++ b/src/ios/GooglePlus.h @@ -1,16 +1,13 @@ #import #import -@interface GooglePlus : CDVPlugin +@interface GooglePlus : CDVPlugin @property (nonatomic, copy) NSString* callbackId; @property (nonatomic, assign) BOOL isSigningIn; -- (void) isAvailable:(CDVInvokedUrlCommand*)command; - (void) login:(CDVInvokedUrlCommand*)command; -- (void) trySilentLogin:(CDVInvokedUrlCommand*)command; - (void) logout:(CDVInvokedUrlCommand*)command; - (void) disconnect:(CDVInvokedUrlCommand*)command; -- (void) share_unused:(CDVInvokedUrlCommand*)command; @end diff --git a/src/ios/GooglePlus.m b/src/ios/GooglePlus.m index 9e68985e..4b361d4c 100644 --- a/src/ios/GooglePlus.m +++ b/src/ios/GooglePlus.m @@ -26,164 +26,131 @@ - (void)handleOpenURLWithAppSourceAndAnnotation:(NSNotification*)notification if ([possibleReversedClientId isEqualToString:self.getreversedClientId] && self.isSigningIn) { self.isSigningIn = NO; - [[GIDSignIn sharedInstance] handleURL:url]; + [GIDSignIn.sharedInstance handleURL:url]; } } -// If this returns false, you better not call the login function because of likely app rejection by Apple, -// see https://code.google.com/p/google-plus-platform/issues/detail?id=900 -// Update: should be fine since we use the GoogleSignIn framework instead of the GooglePlus framework -- (void) isAvailable:(CDVInvokedUrlCommand*)command { - CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:YES]; - [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; -} - (void) login:(CDVInvokedUrlCommand*)command { - [[self getGIDSignInObject:command] signIn]; -} - -/** Get Google Sign-In object - @date July 19, 2015 - */ -- (void) trySilentLogin:(CDVInvokedUrlCommand*)command { - [[self getGIDSignInObject:command] restorePreviousSignIn]; -} - -/** Get Google Sign-In object - @date July 19, 2015 - @date updated March 15, 2015 (@author PointSource,LLC) - */ -- (GIDSignIn*) getGIDSignInObject:(CDVInvokedUrlCommand*)command { _callbackId = command.callbackId; - NSDictionary* options = command.arguments[0]; NSString *reversedClientId = [self getreversedClientId]; if (reversedClientId == nil) { - CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Could not find REVERSED_CLIENT_ID url scheme in app .plist"]; + NSDictionary *errorDetails = @{@"status": @"error", @"message": @"Could not find REVERSED_CLIENT_ID url scheme in app .plist"}; + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:[self toJSONString:errorDetails]]; [self.commandDelegate sendPluginResult:pluginResult callbackId:_callbackId]; - return nil; + return; } NSString *clientId = [self reverseUrlScheme:reversedClientId]; - NSString* scopesString = options[@"scopes"]; - NSString* serverClientId = options[@"webClientId"]; - NSString *loginHint = options[@"loginHint"]; - BOOL offline = [options[@"offline"] boolValue]; - NSString* hostedDomain = options[@"hostedDomain"]; - - - GIDSignIn *signIn = [GIDSignIn sharedInstance]; - signIn.clientID = clientId; - - [signIn setLoginHint:loginHint]; - - if (serverClientId != nil && offline) { - signIn.serverClientID = serverClientId; - } - - if (hostedDomain != nil) { - signIn.hostedDomain = hostedDomain; - } - - signIn.presentingViewController = self.viewController; - signIn.delegate = self; - - // default scopes are email and profile - if (scopesString != nil) { - NSArray* scopes = [scopesString componentsSeparatedByString:@" "]; - [signIn setScopes:scopes]; - } - return signIn; + GIDConfiguration *config = [[GIDConfiguration alloc] initWithClientID:clientId]; + + GIDSignIn *signIn = GIDSignIn.sharedInstance; + + [signIn signInWithConfiguration:config presentingViewController:self.viewController callback:^(GIDGoogleUser * _Nullable user, NSError * _Nullable error) { + if (error) { + NSDictionary *errorDetails = @{@"status": @"error", @"message": error.localizedDescription}; + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:[self toJSONString:errorDetails]]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:self->_callbackId]; + } else { + NSString *email = user.profile.email; + NSString *userId = user.userID; + NSURL *imageUrl = [user.profile imageURLWithDimension:120]; // TODO pass in img size as param, and try to sync with Android + NSDictionary *result = @{ + @"email" : email, + @"userId" : userId, + @"idToken" : user.authentication.idToken, + @"displayName" : user.profile.name ? : [NSNull null], + @"givenName" : user.profile.givenName ? : [NSNull null], + @"familyName" : user.profile.familyName ? : [NSNull null], + @"imageUrl" : imageUrl ? imageUrl.absoluteString : [NSNull null], + }; + + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString: [self toJSONString:result]]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:self->_callbackId]; + } + }]; } + - (NSString*) reverseUrlScheme:(NSString*)scheme { - NSArray* originalArray = [scheme componentsSeparatedByString:@"."]; - NSArray* reversedArray = [[originalArray reverseObjectEnumerator] allObjects]; - NSString* reversedString = [reversedArray componentsJoinedByString:@"."]; - return reversedString; + NSArray* originalArray = [scheme componentsSeparatedByString:@"."]; + NSArray* reversedArray = [[originalArray reverseObjectEnumerator] allObjects]; + NSString* reversedString = [reversedArray componentsJoinedByString:@"."]; + return reversedString; } - (NSString*) getreversedClientId { - NSArray* URLTypes = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleURLTypes"]; - - if (URLTypes != nil) { - for (NSDictionary* dict in URLTypes) { - NSString *urlName = dict[@"CFBundleURLName"]; - if ([urlName isEqualToString:@"REVERSED_CLIENT_ID"]) { - NSArray* URLSchemes = dict[@"CFBundleURLSchemes"]; - if (URLSchemes != nil) { - return URLSchemes[0]; + NSArray* URLTypes = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleURLTypes"]; + + if (URLTypes != nil) { + for (NSDictionary* dict in URLTypes) { + NSString *urlName = dict[@"CFBundleURLName"]; + if ([urlName isEqualToString:@"REVERSED_CLIENT_ID"]) { + NSArray* URLSchemes = dict[@"CFBundleURLSchemes"]; + if (URLSchemes != nil) { + return URLSchemes[0]; + } + } } - } } - } - return nil; + return nil; } - (void) logout:(CDVInvokedUrlCommand*)command { - [[GIDSignIn sharedInstance] signOut]; - CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:@"logged out"]; - [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + [GIDSignIn.sharedInstance signOut]; + NSDictionary *details = @{@"status": @"success", @"message": @"Logged out"}; + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[self toJSONString:details]]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; } - (void) disconnect:(CDVInvokedUrlCommand*)command { - [[GIDSignIn sharedInstance] disconnect]; - CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:@"disconnected"]; - [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + [GIDSignIn.sharedInstance disconnectWithCallback:^(NSError * _Nullable error) { + if(error == nil) { + NSDictionary *details = @{@"status": @"success", @"message": @"Disconnected"}; + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[self toJSONString:details]]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } else { + NSDictionary *details = @{@"status": @"error", @"message": [error localizedDescription]}; + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:[self toJSONString:details]]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } + }]; } -- (void) share_unused:(CDVInvokedUrlCommand*)command { - // for a rainy day.. see for a (limited) example https://github.com/vleango/GooglePlus-PhoneGap-iOS/blob/master/src/ios/GPlus.m +- (void) isSignedIn:(CDVInvokedUrlCommand*)command { + bool isSignedIn = [GIDSignIn.sharedInstance currentUser] != nil; + NSDictionary *details = @{@"status": @"success", @"message": (isSignedIn) ? @"true" : @"false"}; + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[self toJSONString:details]]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; } #pragma mark - GIDSignInDelegate -/** Google Sign-In SDK - @date July 19, 2015 - */ - (void)signIn:(GIDSignIn *)signIn didSignInForUser:(GIDGoogleUser *)user withError:(NSError *)error { - if (error) { - CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:error.localizedDescription]; - [self.commandDelegate sendPluginResult:pluginResult callbackId:_callbackId]; - } else { - NSString *email = user.profile.email; - NSString *idToken = user.authentication.idToken; - NSString *accessToken = user.authentication.accessToken; - NSString *refreshToken = user.authentication.refreshToken; - NSString *userId = user.userID; - NSString *serverAuthCode = user.serverAuthCode != nil ? user.serverAuthCode : @""; - NSURL *imageUrl = [user.profile imageURLWithDimension:120]; // TODO pass in img size as param, and try to sync with Android - NSDictionary *result = @{ - @"email" : email, - @"idToken" : idToken, - @"serverAuthCode" : serverAuthCode, - @"accessToken" : accessToken, - @"refreshToken" : refreshToken, - @"userId" : userId, - @"displayName" : user.profile.name ? : [NSNull null], - @"givenName" : user.profile.givenName ? : [NSNull null], - @"familyName" : user.profile.familyName ? : [NSNull null], - @"imageUrl" : imageUrl ? imageUrl.absoluteString : [NSNull null], - }; - - CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:result]; - [self.commandDelegate sendPluginResult:pluginResult callbackId:_callbackId]; - } + } -/** Google Sign-In SDK - @date July 19, 2015 - */ - (void)signIn:(GIDSignIn *)signIn presentViewController:(UIViewController *)viewController { self.isSigningIn = YES; [self.viewController presentViewController:viewController animated:YES completion:nil]; } -/** Google Sign-In SDK - @date July 19, 2015 - */ - (void)signIn:(GIDSignIn *)signIn dismissViewController:(UIViewController *)viewController { [self.viewController dismissViewControllerAnimated:YES completion:nil]; } +- (NSString*)toJSONString:(NSDictionary*)dictionaryOrArray { + NSError *error; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dictionaryOrArray + options:NSJSONWritingPrettyPrinted + error:&error]; + if (! jsonData) { + NSLog(@"%s: error: %@", __func__, error.localizedDescription); + return @"{}"; + } else { + return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + } +} + @end