OK, I had this working at one point, but then wanted to add a silent login (optional) at app startup. I now going crazy and am getting following error
com.google.api.client.googleapis.json.GoogleJsonResponseException: 401 Unauthorized
{
"code": 401,
"errors": [
{
"domain": "global",
"location": "Authorization",
"locationType": "header",
"message": "Login Required",
"reason": "required"
}
],
"message": "Login Required"
}
This occurs on the line (should I be using "me"?)
Message messageResult = mGmailService.users().messages().send("me", message).execute();
whenever I try to send (in the case above) or read email (not shown). Ultimately, all I want to do with the app is to read user emails and send on their behalf. The app is registered, has the keys, permissions, etc.
I am setting up the data as (you can see my various comments as I tried to things working):
mGso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail()
.requestScopes(new Scope(Scopes.PROFILE))
.requestScopes(new Scope(GmailScopes.GMAIL_READONLY))
.requestScopes(new Scope(GmailScopes.GMAIL_SEND))
.requestScopes(new Scope(GmailScopes.GMAIL_COMPOSE))
.requestScopes(new Scope(GmailScopes.GMAIL_LABELS))
.requestScopes(Plus.SCOPE_PLUS_LOGIN)
.requestScopes(new Scope(Scopes.EMAIL))
.requestScopes(new Scope(Scopes.PLUS_ME))
.build();
// set up Google client
// mGoogleApiClient = new GoogleApiClient.Builder(this)
// .addConnectionCallbacks(this)
// .addOnConnectionFailedListener(this)
// .addApi(Plus.API)
// .addApi(Auth.GOOGLE_SIGN_IN_API, mGso)
// .build();
mGoogleApiClient = new GoogleApiClient.Builder(this)
// .enableAutoManage(this, this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(Plus.API)
.addApi(Auth.GOOGLE_SIGN_IN_API, mGso)
.build();
//mGoogleApiClient.connect(GoogleApiClient.SIGN_IN_MODE_REQUIRED); // try to connect right away
mGmailService = new com.google.api.services.gmail.Gmail.Builder(
AndroidHttp.newCompatibleTransport(), GsonFactory.getDefaultInstance(), null)
.setApplicationName("com.company.appname")
.build();
The silent login is done in the onStart() method of an embedded fragment as:
// try to silently sign in ...
OptionalPendingResult<GoogleSignInResult> pendingResult =
Auth.GoogleSignInApi.silentSignIn(mGoogleApiClient);
if (pendingResult.isDone()) {
// There's an immediate result available.
mSignInAcct = pendingResult.get().getSignInAccount();
mEmailAcct = mSignInAcct.getEmail();
} else {
// There's no immediate result ready, wait for the async callback.
pendingResult.setResultCallback(new ResultCallback<GoogleSignInResult>() {
#Override
public void onResult(#NonNull GoogleSignInResult result) {
mSignInAcct = result.getSignInAccount();
mEmailAcct = mSignInAcct.getEmail();
} // if fails, forget it and force a real login later.
});
}
mGoogleApiClient.connect(GoogleApiClient.SIGN_IN_MODE_OPTIONAL);
boolean b = mGoogleApiClient.isConnected();
which sometimes connects right away with a valid user account and sometimes not. I do see that b from the ...isConnected() call is false, although isConnected() returns true later in later calls.
Login button code:
gmail_signin_button = (com.google.android.gms.common.SignInButton) rootView.findViewById(R.id.gmail_sign_in_button);
gmail_signin_button.setScopes(mGso.getScopeArray());
gmail_signin_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// User clicked the sign-in button, so begin the sign-in process and automatically
// attempt to resolve any errors that occur.
mShouldResolve = true;
// connect if unconnected...
if (!mGoogleApiClient.isConnected()) {
mGoogleApiClient.connect();
tv_email_status.setText("G+ connecting");
// Show a message to the user that we are signing in.
Log.d(Constants.TAG, "Google API connecting ...");
}
else {
// sign in anew ...
Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
startActivityForResult(signInIntent, Constants.RC_SIGN_IN);
}
}
});
onActivityResult code (in part):
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// G+
if (requestCode == Constants.RC_SIGN_IN) {
// If the error resolution was not successful we should not resolve further.
GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
if (!result.isSuccess()) {
mShouldResolve = false;
Log.d(Constants.TAG, "Error: code: " + resultCode);
tv_email_status.setText("Error: code: " + resultCode);
} else {
mSignInAcct = result.getSignInAccount();
mEmailAcct = mSignInAcct.getEmail();
Log.d(Constants.TAG, "Gmail signin OK - " + mEmailAcct);
tv_email_status.setText("Email: " + mEmailAcct);
}
mIsResolving = false;
if(!mGoogleApiClient.isConnected()) {
mGoogleApiClient.connect(GoogleApiClient.SIGN_IN_MODE_OPTIONAL);
}
} else if (requestCode == Constants.REQUEST_ACCOUNT_PICKER) {
if (resultCode == RESULT_OK && data != null && data.getExtras() != null) {
mEmailAcct = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
if (mEmailAcct != null) {
//mCredential.setSelectedAccountName(accountName);
SharedPreferences settings = mActivity.getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.putString(Constants.PREF_ACCOUNT_NAME, mEmailAcct);
editor.commit();
Log.d(Constants.TAG, "Gmail: account: " + mEmailAcct);
tv_email_status.setText("Gmail: account: " + mEmailAcct);
}
} else if (resultCode == RESULT_CANCELED) {
tv_email_status.setText("Account unspecified.");
}
}
//else if (requestCode == Constants.REQUEST_AUTHORIZATION) {
//if (mCredential != null && resultCode != RESULT_OK) {
// startActivityForResult(mCredential.newChooseAccountIntent(), Constants.REQUEST_ACCOUNT_PICKER);
//}
//}
OK, I will get a good onActivityResult and get valid SignInAcct and EmailAcct values. It seems that we are logged in fine, but then the gMail service always fails with a 401 error as explained above.
To do the silent login made me have to add the GOOGLE_SIGN_IN_API which then seems to require the GoogleApiClient to connect with SIGN_IN_MODE_OPTIONAL which doesn't make sense to me.
I also see that my AccountPicker does nothing now as well. But that is another story/problem, as I said I thought I had this all working until I tried to add that silent feature.
Whew, well I have probably made this harder than it is, can anyone steer me in the right direction? As I said all I want to do is:
silently log in if the user has previously done so
allow the user to log in or re-log in if needed
read the users emails and contacts
send email on their behalf.
OK, I figured this out, as usual its something simple wrong. The credential in the GmailService constructor must be set, but you don't have this until you try to connect. So I have code like this
void setGmailAccess(GoogleSignInResult result) {
mSignInAcct = result.getSignInAccount();
mEmailAcct = mSignInAcct.getEmail();
final Thread task = new Thread()
{
#Override
public void run()
{
try {
// run in background thread
String token = GoogleAuthUtil.getToken(MainActivity.this, Plus.AccountApi.getAccountName(mGoogleApiClient), "oauth2:profile email");
GoogleCredential credential = new GoogleCredential().setAccessToken(token);
mGmailService = new com.google.api.services.gmail.Gmail.Builder(
AndroidHttp.newCompatibleTransport(), GsonFactory.getDefaultInstance(), credential)
.setApplicationName("com.company.appname")
.build();
}
catch (Exception e) {
// ignore errors
String error = e.getMessage();
}
}
};
task.start();
}
To set up the GmailService object. It is called from onConnected, where I have code like this in the result success block:
try {
// try to silently sign in ...
OptionalPendingResult<GoogleSignInResult> pendingResult =
Auth.GoogleSignInApi.silentSignIn(mGoogleApiClient);
if (pendingResult.isDone()) {
// There's an immediate result available.
setGmailAccess(pendingResult.get());
} else {
// There's no immediate result ready, wait for the async callback.
pendingResult.setResultCallback(new ResultCallback<GoogleSignInResult>() {
#Override
public void onResult(#NonNull GoogleSignInResult result) {
setGmailAccess(result);
} // if fails, forget it and force a real login later.
});
}
} catch(Exception e) {
String me = e.getLocalizedMessage();
}
And in the activity result for starting a new login. Seems to work.
Related
When I am trying to pay with the google pay option in my developed android application, I am getting the error
Request failed An unexpected error has occurred Please try again later [R_BIBED_07]
For the merchant, we are using stripe as a payment gateway. Everything is set ok like the stripe key and environment. This is happening only for production. In test mode, it navigates me to the card selection. But only in production after clicking on the button it shows me the error. I don't know exactly what the reason is. I tried many ways.
I am providing the link for the snippet code I used to integrate Google Pay into my android application.
https://www.tabnine.com/web/assistant/code/rs/5c7c2ad92ef5570001da2491#L164
Code
// Inside OnCreate methos
if (paymentmethod.equals("GooglePay")) {
proceed.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
PaymentDataRequest request = createPaymentDataRequest();
Log.e("onclickreq", String.valueOf(request));
if (request != null) {
AutoResolveHelper.resolveTask(
mPaymentsClient.loadPaymentData(request),
DbsSummaryPage.this,
LOAD_PAYMENT_DATA_REQUEST_CODE);
// LOAD_PAYMENT_DATA_REQUEST_CODE is a constant integer of your choice,
// similar to what you would use in startActivityForResult
} else {
Log.e("Enter in", String.valueOf(request));
Toast.makeText(DbsSummaryPage.this, "gap", Toast.LENGTH_SHORT).show();
}
}
});
}
mPaymentsClient =
Wallet.getPaymentsClient(
this,
new Wallet.WalletOptions.Builder()
.setEnvironment(WalletConstants.ENVIRONMENT_PRODUCTION)
.build());
isReadyToPay();
// End of OnCreate method
private void isReadyToPay() {
Log.e("isReadyToPay","isReadyToPay");
IsReadyToPayRequest request =
IsReadyToPayRequest.newBuilder()
.addAllowedPaymentMethod(WalletConstants.PAYMENT_METHOD_CARD)
.addAllowedPaymentMethod(WalletConstants.PAYMENT_METHOD_TOKENIZED_CARD)
.build();
Task<Boolean> task = mPaymentsClient.isReadyToPay(request);
task.addOnCompleteListener(
new OnCompleteListener<Boolean>() {
public void onComplete(#NonNull Task<Boolean> task) {
try {
boolean result = task.getResult(ApiException.class);
if (result) {
// Show Google as payment option.
System.out.println(true);
System.out.println("resttrtdt" + result);
} else {
// Hide Google as payment option.
System.out.println("hide the google button");
}
} catch (ApiException exception) {
System.out.println("hide the google button");
}
}
});
}
private PaymentDataRequest createPaymentDataRequest() {
Log.e("paydat", String.valueOf(totalamount));
ad = String.format("%.2f", totalamount);
Log.e("adgoggole", ad);
Log.e("Insidetotal", String.valueOf(totalamount));
Log.e("PSPRICE Google", "Hi " + psprice);
PaymentDataRequest.Builder request =
PaymentDataRequest.newBuilder()
.setTransactionInfo(
TransactionInfo.newBuilder()
.setTotalPriceStatus(WalletConstants.TOTAL_PRICE_STATUS_FINAL)
.setTotalPrice(ad)
.setCurrencyCode("USD")
.build())
.addAllowedPaymentMethod(WalletConstants.PAYMENT_METHOD_CARD)
.addAllowedPaymentMethod(WalletConstants.PAYMENT_METHOD_TOKENIZED_CARD)
.setCardRequirements(
CardRequirements.newBuilder()
.addAllowedCardNetworks(
Arrays.asList(
WalletConstants.CARD_NETWORK_AMEX,
WalletConstants.CARD_NETWORK_DISCOVER,
WalletConstants.CARD_NETWORK_VISA,
WalletConstants.CARD_NETWORK_MASTERCARD))
.build());
PaymentMethodTokenizationParameters params =
PaymentMethodTokenizationParameters.newBuilder()
.setPaymentMethodTokenizationType(
WalletConstants.PAYMENT_METHOD_TOKENIZATION_TYPE_PAYMENT_GATEWAY)
.addParameter("gateway", "stripe")
.addParameter("stripe:publishableKey", key)
.addParameter("stripe:version", "2018-11-08")
.build();
request.setPaymentMethodTokenizationParameters(params);
System.out.println("Data" + request.build());
return request.build();
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
System.out.println("Entered" + "into onactivity");
switch (requestCode) {
case LOAD_PAYMENT_DATA_REQUEST_CODE:
switch (resultCode) {
case Activity.RESULT_OK:
PaymentData paymentData = PaymentData.getFromIntent(data);
String rawToken = paymentData.getPaymentMethodToken().getToken();
Token stripeToken = Token.fromString(rawToken);
String stripegettoken = stripeToken.getId();
System.out.println(paymentData.getPaymentMethodToken().getToken());
System.out.println(rawToken);
System.out.println(paymentData.getPaymentMethodToken().getToken());
System.out.println("rawToken" + rawToken);
if (stripeToken != null) {
// This chargeToken function is a call to your own server, which should then connect
// to Stripe's API to finish the charge.
pd.show();
executeFormForGooglePay(stripegettoken, totalamount, userphonenumber, proprice, producttax, pscommission, finalpsprice, stripefee, salestax, bodatysurcharge, fffee);
}
break;
case Activity.RESULT_CANCELED:
pd.dismiss();
System.out.println("status" + "Cancelled");
break;
case AutoResolveHelper.RESULT_ERROR:
Status status = AutoResolveHelper.getStatusFromIntent(data);
System.out.println("status" + status);
pd.dismiss();
Toast.makeText(DbsSummaryPage.this,
"Got error " + status.getStatusMessage(), Toast.LENGTH_SHORT).show();
// Log the status for debugging.
// Generally, there is no need to show an error to
// the user as the Google Payment API will do that.
break;
default:
// Do nothing.
}
break;
default:
// Do nothing.
}
}
before you use the PRODUCTION environment, your integration needs to be approved from Google Pay & Wallet Console.
In the meantime, you can test your integration using the TEST environment in your configuration.
In my case, i just forgot to add the relevant meta-data in the manifest file under application tag:
<meta-data
android:name="com.google.android.gms.wallet.api.enabled"
android:value="true" />
I am trying to signin user with google.
I got the demo code to login, and it is working fine.
Scenario 1: User Login with already existing account.
expected solution: after user selected his/her account, and in onActivityResult i should get the details of the selected email account like, name, pic, email etc etc..... and this is coming correct.
Scenario 2: User can add new account and logged in auatomatically.
Expected solution: When user clicks on signin with google, and selected create new account instead of selecting already existing account. the user will go to the add new account page and insert their credentials, and after successfully adding new account, user should automatically login.
Error : But when user adds new account and comes back to the login page. I am able to get the email, id but givenName, and familyName is null. I need the name of the user.
Now the account is added in the device, next when user select the newly added account from login page, at that time in onActivityResult i am getting the NAME of the user.
What i want is when user adds a new account or select an existing account, it should show me the name of the user
Here is the code to handleSignIn
LoginActivity.java
public void onStart() {
super.onStart();
OptionalPendingResult<GoogleSignInResult> opr = Auth.GoogleSignInApi.silentSignIn(mGoogleApiClient);
if (opr.isDone()) {
// If the user's cached credentials are valid, the OptionalPendingResult will be "done"
// and the GoogleSignInResult will be available instantly.
Log.d(TAG, "Got cached sign-in");
GoogleSignInResult result = opr.get();
handleSignInResult(result);
} else {
// If the user has not previously signed in on this device or the sign-in has expired,
// this asynchronous branch will attempt to sign in the user silently. Cross-device
// single sign-on will occur in this branch.
opr.setResultCallback(new ResultCallback<GoogleSignInResult>() {
#Override
public void onResult(GoogleSignInResult googleSignInResult) {
handleSignInResult(googleSignInResult);
}
});
}
}
#Override
protected void onCreate(Bundle savedInstanceState){
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail()
.build();
mGoogleApiClient = new GoogleApiClient.Builder(this)
.enableAutoManage(this /* FragmentActivity */, this /* OnConnectionFailedListener */)
.addApi(Auth.GOOGLE_SIGN_IN_API, gso)
.build();
}
protected void onActivityResult(
final int requestCode,
final int resultCode,
final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RC_SIGN_IN) {
GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
handleSignInResult(result);
} else
callbackManager.onActivityResult(requestCode, resultCode, data);
}
private void handleSignInResult(GoogleSignInResult result) {
Log.d(TAG, "handleSignInResult:" + result.isSuccess());
if (result.isSuccess()) {
// Signed in successfully, show authenticated UI.
GoogleSignInAccount acct = result.getSignInAccount();
Log.e(TAG, "handleSignInResult: getPhotoUrl " + acct.getPhotoUrl());
Log.e(TAG, "handleSignInResult: getServerAuthCode " + acct.getServerAuthCode());
Log.e(TAG, "handleSignInResult: getIdToken " + acct.getIdToken());
Log.e(TAG, "handleSignInResult: getId " + acct.getId());
Log.e(TAG, "handleSignInResult: getGivenName " + acct.getGivenName()); //firstName and lastName=""
Log.e(TAG, "handleSignInResult: getDisplayName " + acct.getDisplayName());
Log.e(TAG, "handleSignInResult: getEmail " + acct.getEmail());
Log.e(TAG, "handleSignInResult: getFamilyName " + acct.getFamilyName());
Log.e(TAG, "handleSignInResult: getAccount " + acct.getAccount());
Set<Scope> scopeSet = acct.getGrantedScopes();
for (Scope scope : scopeSet) {
Log.e(TAG, "handleSignInResult: " + scope.zzpp());
}
}
}
I added this code
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail()
.requestIdToken("Web client (auto created by Google Service)") // added this line
.requestProfile()
.build();
Web client is found at https://console.developers.google.com and go to you app.
Select Credentials at left and you will find Web Client in OAuth 2.0 client IDs section.
I am trying to find the latest solution with GoogleApiClient.Plus.API (android) for getting the email for the gplus profile. In internet, stackoverflow, every example found is obsolete and deprecated and of no use.
If it can be fetched only from Auth.GOOGLE_SIGN_IN_API then is it a two step process to fetch half of info from Auth API and rest from Plus.API ??
Thanks in advance for answer.
First I assume you have a button for google plus sign in
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.sign_in_button:
Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
startActivityForResult(signInIntent, RC_SIGN_IN);
break;
}
}
At your onActivityResult you will catch sign in result
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// Result returned from launching the Intent from and you can extract your user information there
if (requestCode == RC_SIGN_IN) {
GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
if (result.isSuccess()) {
// Signed in successfully, show authenticated UI.
GoogleSignInAccount acct = result.getSignInAccount();
//This line is your need
String yourEmail = acct.getEmail();
}
}
}
Here is how to get the email that is associated with the device, and probably with your app too:
Pattern emailPattern = Patterns.EMAIL_ADDRESS; // API level 8+
Account[] accounts = AccountManager.get(context).getAccounts();
for (Account account : accounts) {
if (emailPattern.matcher(account.name).matches()) { //If email matches the account associated with the device
String possibleEmail = account.name;
...
}
}
Let me know if this worked
After two days of struggle at last this post saved my life ..
How to get profile like gender from google signin in Android?
But the apis are obsolete, did a bit of scratch head and could make it work.
create your google api client like this
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail()
.requestScopes(new Scope(Scopes.PLUS_LOGIN))
.build();
m_GoogleApiClient = new GoogleApiClient.Builder(m_activity)
.enableAutoManage(m_activity, this)
.addApi(Auth.GOOGLE_SIGN_IN_API, gso)
.addApi(Plus.API)
.build();
then on onActivityResult()
GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
Log.d(TAG, "handleSignInResult:" + result.isSuccess());
if (result.isSuccess()) {
GoogleSignInAccount acct = result.getSignInAccount();
fetchConnectedProfileInfo();
}
public void fetchConnectedProfileInfo()
{
Log.d(TAG, "fetchConnectedProfileInfo");
if (m_GoogleApiClient.hasConnectedApi(Plus.API)) {
Plus.PeopleApi.load(m_GoogleApiClient, "me").setResultCallback(this);
}
}
Refer my github page for full code sample https://github.com/sandipsahoo2k2/social-login
Im triying to authenticate with google, I´m currently using the way i´t is recomended on his documentation, but.. is there any EASY way to get the refresh token?, I make the auth and get the token, but it have been impossible for me to take the refresh token , and I need id.
I have tried lots of ways, I have spend more than a week with this issue, is it possible to get that token? I´ve tried with lots of manuals, tutorials... but I can´t.
Anyone Knows any place where I can Know how to get the resfresh_token and it is good explained and that is currently working?.
Thanks a lot!!
Pd: is a native android App.
EDIT:
Ok, for More info, I´m making the auth as is in google´s documentation to auth with GoogleApiClient with little variations( because I´m using it as a cain of manager) . THIS PART RUN´S OK:
Firs instead of calling on create I call:
public void logginGooglePlus(GooglePlusAuthCallback googlePlusAuthCallback) {
gPAuthCallback = googlePlusAuthCallback;
// Initializing google plus api client
String scope = "audience:server:client_id:" + SERVER_CLIENT_ID;
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this).addApi(Plus.API)
.addScope(Plus.SCOPE_PLUS_LOGIN).build();
mGoogleApiClient.connect();
mSignInClicked = true;
signInWithGplus(gPAuthCallback);
}
I continue just with copy&paste with the google´s:
#Override
public void onConnectionFailed(ConnectionResult result) {
if (!result.hasResolution()) {
// GooglePlayServicesUtil.getErrorDialog(result.getErrorCode(),
// this,
// 0).show();
if (gPAuthCallback != null) {
gPAuthCallback.onLoginError(result.toString());
}
return;
}
if (!mIntentInProgress) {
// Store the ConnectionResult for later usage
mConnectionResult = result;
if (mSignInClicked) {
// The user has already clicked 'sign-in' so we attempt to
// resolve all
// errors until the user is signed in, or they cancel.
resolveSignInError();
}
}
}
#Override
public void onActivityResult(int requestCode, int responseCode,
Intent intent) {
super.onActivityResult(requestCode, responseCode, intent);
if (requestCode == RC_SIGN_IN) {
if (responseCode != RESULT_OK) {
mSignInClicked = false;
}
mIntentInProgress = false;
if (!mGoogleApiClient.isConnecting()) {
mGoogleApiClient.connect();
}
}
}
#Override
public void onConnected(Bundle arg0) {
mSignInClicked = false;
// Get user's information
if (gPAuthCallback != null) {
gPAuthCallback.onLoginSuccesful();
}
}
#Override
public void onConnectionSuspended(int arg0) {
mGoogleApiClient.connect();
}
/**
* Sign-in into google
* */
public void signInWithGplus(GooglePlusAuthCallback googlePlusAuthCallback) {
gPAuthCallback = googlePlusAuthCallback;
if (!mGoogleApiClient.isConnecting()) {
mSignInClicked = true;
resolveSignInError();
}
}
/**
* Method to resolve any signin errors
* */
private void resolveSignInError() {
if (mConnectionResult.hasResolution()) {
try {
mIntentInProgress = true;
mConnectionResult.startResolutionForResult(this, RC_SIGN_IN);
} catch (SendIntentException e) {
mIntentInProgress = false;
mGoogleApiClient.connect();
}
}
}
And finally I call to get the persons data:
public void getProfileInformation(
GooglePlusGetPersonCallback getPersonCallback) {
this.googlePlusGetPersonCallback = getPersonCallback;
try {
if (Plus.PeopleApi.getCurrentPerson(mGoogleApiClient) != null) {
currentPerson = Plus.PeopleApi
.getCurrentPerson(mGoogleApiClient);
String personName = currentPerson.getDisplayName();
String personPhotoUrl = currentPerson.getImage().getUrl();
String personGooglePlusProfile = currentPerson.getUrl();
String email = Plus.AccountApi.getAccountName(mGoogleApiClient);
Log.e("GPlus", "Name: " + personName + ", plusProfile: "
+ personGooglePlusProfile + ", email: " + email
+ ", Image: " + personPhotoUrl);
new getTokenAsyncTask().execute();
}
} catch (Exception e) {
if (googlePlusGetPersonCallback != null) {
// googlePlusGetPersonCallback.ongeGooglePersonError(e.getCause()
// .toString());
}
e.printStackTrace();
}
}
Ok, leaving here is easy, now It starts the funny part: I need the Refresh Token because I have to sign in with a server, and I have to pass the access_token, refresh_token and user_id.
reading this: https://developers.google.com/accounts/docs/CrossClientAuth
I understand that I have to make the getToken call with a different Scope, so I change it: the method for get token is :
// GET TOKEN 2o plano
public class getTokenAsyncTask extends AsyncTask<Void, Boolean, String> {
#Override
protected String doInBackground(Void... params) {
try {
String acountname = Plus.AccountApi
.getAccountName(mGoogleApiClient);
// agregamos el scope del server para que me loguee para la app
// "crossclient"
String serverScope = "audience:server:client_id:"
+ SERVER_CLIENT_ID;
String token = GoogleAuthUtil.getToken(GooglePlusManager.this,
acountname, serverScope);
return token;
} catch (UserRecoverableAuthException e) {
// startActivityForResult(e.getIntent(), "NECESITA AUT");
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace(); // TODO: handle the exception
}
return null;
}
#Override
protected void onPostExecute(String code) {
String token = code;
if (googlePlusGetPersonCallback != null) {
googlePlusGetPersonCallback.ongeGooglePersonSuccesful(
currentPerson, token);
}
}
}
According to documentation, with this I´ll get a token that: "The ID token will contain several data fields", and I´m only retrieving a string token ( but it does not give any crash or issue so I suppose it is Ok). I haven´t got access to the Server, but I´ve suppose that it is ok, because the ios app is running ok already ( another company have done it in Ios), do I have to ask them to make in the server any thing so I can authenticate my android app with the server?
The ios app is passing to the server the parameter I´ve already said (acces, refres, id) So I Imagine that I have to pass the same in android, I have acces to the console and I have declared the android app in the same project.
well, from the part I am, that I have an supposed valid token.. how can I get the refresh token? I´m completely lost...
If anyone knows how to get it.. I´ll invite as much beer as you can ( I have lost so many hours with this :S ).
xcuses for the really really big post :( ( it´s my first one!).
According to the google documentation you can exchange tokens.
So, if you post required parameters to below link, then you will obtain a refresh token
https://www.googleapis.com/oauth2/v3/token
Parameters,
var params = {
code: 'FROM ANDROID [ONE TIME CODE]',
client_id: 'FROM YOUR GOOGLE CONSOLE',
client_secret: 'FROM YOUR GOOGLE CONSOLE',
redirect_uri: 'FROM YOUR GOOGLE CONSOLE',
grant_type: 'authorization_code'
access_type:'offline'
};
The only thing you must send from android is one-time code. Other parameters are static, store they in a config file.
var params = {
code: 'FROM ANDROID [ONE TIME CODE]',
Android side, Enable server-side API access for your app, after implementing this, you will have an one-time code
I hope these will be helpful for you.
I'm using Google Drive Android API (as part of Google Play Services) to upload files to cloud.
To connect client I'm using following code (simplified):
apiClient = new GoogleApiClient.Builder(context)
.addApi(Drive.API)
.setAccountName(preferences.getString("GOOGLE_DRIVE_ACCOUNT", null))
.build();
ConnectionResult connectionResult = apiClient.blockingConnect(SERVICES_CONNECTION_TIMEOUT_SEC, TimeUnit.SECONDS);
if (!connectionResult.isSuccess()) {
throw new ApiConnectionException(); //our own exception
}
To upload file I'm using something following code (simplified):
DriveApi.ContentsResult result = Drive.DriveApi.newContents(apiClient).await();
if (!result.getStatus().isSuccess()) {
/* ... code for error handling ... */
return;
}
OutputStream output = result.getContents().getOutputStream();
/* ... writing to output ... */
//create actual file on Google Drive
DriveFolder.DriveFileResult driveFileResult = Drive.DriveApi
.getFolder(apiClient, folderId)
.createFile(apiClient, metadataChangeSet, result.getContents())
.await();
Everything works as expected except for one particular user case. When user removes our app from "Connected apps" (using Google Settings application) this code still returns successful results for all invocations. Although file is never uploaded to Google Drive.
Connection to Google Play Services also succeeds.
Is it a bug of API or it can be somehow detected that user disconnected the application?
I don't know the API inside/out, however this page may help https://support.google.com/drive/answer/2523073?hl=en. I would double check the accounts.google.com page and verify that all permissions are being removed. This won't solve the api behaviour but at least you can verify the permissions.
Aren't you getting a UserRecoverableAuthIOException? Because you should. Any try to read/upload to drive where a user disconnected the app should return this Exception. You are probably catching general Exceptions and missing this. Try debugging to see if you aren't getting this exception.
If you are, all you have to do is re-request
catch (UserRecoverableAuthIOException e) {
startActivityForResult(e.getIntent(), COMPLETE_AUTHORIZATION_REQUEST_CODE);
}
And then deal with the response like this:
case COMPLETE_AUTHORIZATION_REQUEST_CODE:
if (resultCode == RESULT_OK) {
// App is authorized, you can go back to sending the API request
} else {
// User denied access, show him the account chooser again
}
break;
}
For creating a file try sending a IntentSender which according to this
By giving a IntentSender to another application, you are granting it the right to perform the operation you have specified as if the other application was yourself (with the same permissions and identity). Looks like a Pending Intent. You can create a file using
ResultCallback<ContentsResult> onContentsCallback =
new ResultCallback<ContentsResult>() {
#Override
public void onResult(ContentsResult result) {
// TODO: error handling in case of failure
MetadataChangeSet metadataChangeSet = new MetadataChangeSet.Builder()
.setMimeType(MIME_TYPE_TEXT).build();
IntentSender createIntentSender = Drive.DriveApi
.newCreateFileActivityBuilder()
.setInitialMetadata(metadataChangeSet)
.setInitialContents(result.getContents())
.build(mGoogleApiClient);
try {
startIntentSenderForResult(createIntentSender, REQUEST_CODE_CREATOR, null,
0, 0, 0);
} catch (SendIntentException e) {
Log.w(TAG, "Unable to send intent", e);
}
}
};
In here
`startIntentSenderForResult (IntentSender intent, int requestCode, Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags)`
If requestCode >= 0, this code will be returned in onActivityResult() when the activity exits.
in your onActivityResult() you can
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
//REQUEST_CODE_CREATOR == 1
case REQUEST_CODE_CREATOR:
if (resultCode == RESULT_OK) {
DriveId driveId = (DriveId) data.getParcelableExtra(
OpenFileActivityBuilder.EXTRA_RESPONSE_DRIVE_ID);
showMessage("File created with ID: " + driveId);
}
finish();
break;
default:
super.onActivityResult(requestCode, resultCode, data);
break;
}
}
Try to get the apiClient like this
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Drive.API).addScope(Drive.SCOPE_FILE)
.setAccountName(mAccountName).addConnectionCallbacks(this)
.addOnConnectionFailedListener(this).build();
/**
* Called when {#code mGoogleApiClient} is connected.
*/
#Override
public void onConnected(Bundle connectionHint) {
Log.i(TAG, "GoogleApiClient connected");
}
/**
* Called when {#code mGoogleApiClient} is disconnected.
*/
#Override
public void onConnectionSuspended(int cause) {
Log.i(TAG, "GoogleApiClient connection suspended");
}
/**
* Called when {#code mGoogleApiClient} is trying to connect but failed.
* Handle {#code result.getResolution()} if there is a resolution is
* available.
*/
#Override
public void onConnectionFailed(ConnectionResult result) {
Log.i(TAG, "GoogleApiClient connection failed: " + result.toString());
if (!result.hasResolution()) {
GooglePlayServicesUtil.getErrorDialog(result.getErrorCode(), this, 0).show();
return;
}
try {
result.startResolutionForResult(this, REQUEST_CODE_RESOLUTION);
} catch (SendIntentException e) {
Log.e(TAG, "Exception while starting resolution activity", e);
}
}
You can get the mAccountName like this:
Account[] accounts = AccountManager.get(this).getAccountsByType("com.google");
if (accounts.length == 0) {
Log.d(TAG, "Must have a Google account installed");
return;
}
mAccountName = accounts[0].name;
Hope this helps.