I have an Android application I am writing that uses the Google Drive API. I am using the Drive.SCOPE_APPFOLDER to have a private location to put my application data, if the user chooses to sync the app with their Google drive. For the most part everything works fine and I can sync contents to the Google drive. However, I am having major difficulties on the initialization front. First of all, I can recognize when the users has not used the Google drive and launch the sign in activity. Here is how I build my google sign in client. I don't actually think I need the .requestEmail() and .requestProfile() but it is there for now.
private GoogleSignInClient buildGoogleSignInClient(AppCompatActivity activity) {
GoogleSignInOptions signInOptions =
new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestScopes(Drive.SCOPE_APPFOLDER)
.requestEmail()
.requestProfile()
.build();
return GoogleSignIn.getClient(activity, signInOptions);
}
To recognize that the user has not signed into to Google Drive or used the Drive with the app I have the following code.
private void initGoogleClient() {
mGoogleSignInAccount = GoogleSignIn.getLastSignedInAccount(getActivity());
if (mGoogleSignInAccount == null) {
launchSigninActivity(getActivity());
} else {
mDriveResourceClient = Drive.getDriveResourceClient(getActivity().getApplicationContext(), mGoogleSignInAccount);
//.... doing stuff here that works fine
}
}
To launch the sign in activity I have the following code.
private void launchSigninActivity(AppCompatActivity activity) {
mGoogleSignInClient = buildGoogleSignInClient(activity);
Intent signinIntent = mGoogleSignInClient.getSignInIntent();
activity.startActivityForResult(signinIntent, SIGNIN_INTENT_CODE);
}
Now the sign in activity gets launched just fine and users can sign in to their Google account. However, any time the Google UI is presented to sign in the user, the error code, code=8 INTERNAL_ERROR is thrown. If the user has previously signed into their drive account before using my app, the error code 8 is not thrown.
public void handleSignIn(int requestCode, int resultCode, Intent data) {
Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data);
try {
mGoogleSignInAccount = task.getResult(ApiException.class);
mDriveResourceClient = Drive.getDriveResourceClient(getActivity().getApplicationContext(), mGoogleSignInAccount);
//... other App specific stuff
} catch (ApiException e) {
Log.w(TAG, "************* signInResult:failed code=" + e.getStatusCode());
}
}
(onActivityResults() in my activity calls the code above) The getActivity() method just returns the activity that handled the onActivityResults().
The second issue I have noticed is that there are some timing issues when using the GoogleResourceClient on first time use with my app. The first few times I try to read the drive, it seems to be empty, but at some point the remote files show up. I have not characterized how long it takes before reads find files but it seems that the drive API is returning before any calls to the remote drive on initial/first time app use calls.
These issues only happen on first time use of the Android app. All subsequent launches of the app run smooth. Does anyone have any ideas about these issues?
Google had deprecated this library. Move the the Google Drive REST API.
Related
So there's lots of questions on this, and seemingly lots of (somewhat contradictory) documentation.
I'm simply trying to get a user to sign in to submit a score to a Google Play Services leaderboard in my app.
So they press "show leaderboard" and I begin the sign-in checking process:
public void displayLeaderboard()
{
if (checkPlayServices() == true)
{
// First, sign in.
GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(this);
if (account == null)
{
// Need to get sign-in client?
if (_googleSignInClient == null)
{
// Configure sign-in to request the user's ID, email address, and basic profile. ID and basic profile are included in DEFAULT_SIGN_IN.
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN).build();
_googleSignInClient = GoogleSignIn.getClient(this, gso);
}
Intent signInIntent = _googleSignInClient.getSignInIntent();
startActivityForResult(signInIntent, CALLBACK_SIGN_IN_TO_GOOGLE_ACCOUNT);
}
else
{
actualDisplayLeaderboard(account);
}
}
}
So then onActivityResult() does catch the callback, so far so good:
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == CALLBACK_SIGN_IN_TO_GOOGLE_ACCOUNT)
{
// Result returned from launching the Intent from GoogleSignInClient.getSignInIntent()
Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data);
try
{
GoogleSignInAccount account = task.getResult(ApiException.class);
// THIS IS WHERE IT FAILS
// **********************
// Signed in successfully.
// ...
}
catch (ApiException e)
{
// The ApiException status code indicates the detailed failure reason.
// Please refer to the GoogleSignInStatusCodes class reference for more information.
System.out.println("Failed to sign in.");
String message = e.getMessage();
System.out.println(message);
e.printStackTrace();
}
}
}
As indicated above, that is where it fails.
12-10 17:24:54.877 12571-12571/*.*.* W/System.err: com.google.android.gms.common.api.ApiException: 12501:
12-10 17:24:54.877 12571-12571/*.*.* W/System.err: at com.google.android.gms.common.internal.ApiExceptionUtil.fromStatus(Unknown Source)
12-10 17:24:54.877 12571-12571/*.*.* W/System.err: at com.google.android.gms.auth.api.signin.GoogleSignIn.getSignedInAccountFromIntent(Unknown Source)
12-10 17:24:54.887 12571-12571/org.fortheloss.sticknodespro W/System.err: at *.*.*.AndroidLauncher.onActivityResult(AndroidLauncher.java:1013)
Looking at the samples for signing in and leaderboards, my code looks identical.
I feel like I'm going astray with all the Firebase console and/or Google Play Services console (who knows anymore, it's such a mess) OAUTH and SHA-1 stuff I don't know what I need, what I don't need, it's all over the place and it still doesn't work. Some things are for a "web application" - this isn't a web application - I have the credentials there anyway, put in the app, etc - doesn't matter, still doesn't work.
Any pointing in the right direction appreciated.
In typical fashion, the answer comes about after posting the question
This is the fix
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN).requestIdToken(getString(R.string.server_client_id)).requestScopes(Games.SCOPE_GAMES_LITE).build();
Where R.string.server_client_id is as described here https://developers.google.com/identity/sign-in/android/start-integrating#get_your_backend_servers_oauth_20_client_id
(At first, the page it brought me to was for another version of my app, so when I was using that credential, it obviously wasn't working. Then, I realized I needed to create a release build (with my release keystore) to get it to work.)
Things the sample projects fail to show/mention ^
They doesn't even use requestIdToken(...)
EDIT: And the reason...that it doesn't use requestIDToken, despite it being used in nearly every similar StackOverflow answer, is because you're not supposed to. For Android applications. This is because you're supposed to have this in the Android Manifest (app_id taken from Google Play Services console linked app section)
<meta-data android:name="com.google.android.gms.games.APP_ID" android:value="#string/app_id" />
<meta-data android:name="com.google.android.gms.version" android:value="#integer/google_play_services_version"/>
Would be GREAT if Google Play Services would yell at you if you didn't have those included...
My app has been live using Google login for a few years now. I reworked the implementation a few months ago with no issues until recently.
Now, a few weeks ago, login has stopped working. When I attempt it, the login never finishes, but stays in an infinite loop, displaying the "connecting" screen over and over.
internet connectivity is ok
this happens to many users (not sure if it affects ALL Google login users)
I have tried clearing the cache of Google Play services
the Google API client is still connected
I have tried updating all relevant google libraries to the most recent versions
I see this on Android 6 - not sure if other versions are affected
Before I post my code, I want to point out that the login never returns. The callback is never reached until I actively cancel the login (at which point it behaves as expected. I also want to emphasise again that my code has been in place for quite some time before this occurred - maybe something changed on Google side...
I'm out of ideas and in dire need of help. Any ideas?
API client creation in onCreate:
GoogleSignInOptions googleSignInOptions = googleSignInOptionsBuilder
.build();
googleApiClient = new GoogleApiClient.Builder(this)
.enableAutoManage(this,
new GoogleApiClient.OnConnectionFailedListener() {
#Override
public void onConnectionFailed(
#NonNull ConnectionResult connectionResult) {
Logger.error("Google API connection failed");
}
})
.addApi(Auth.GOOGLE_SIGN_IN_API, googleSignInOptions)
.addApi(Games.API)
.build();
Actual Sign-In method:
public void signInWithGoogle() {
logToServer("Google Sign-In Started");
Intent signInIntent = Auth.GoogleSignInApi
.getSignInIntent(googleApiClient);
startActivityForResult(signInIntent,
GameConstants.GOOGLE_REQUEST_CODE_SIGN_IN);
}
Callback (never touched)
#Override
protected void onActivityResult(final int requestCode, int response,
Intent data) {
super.onActivityResult(requestCode, response, data);
if (requestCode == GameConstants.GOOGLE_REQUEST_CODE_SIGN_IN) {
GoogleSignInResult result = Auth.GoogleSignInApi
.getSignInResultFromIntent(data);
handleGoogleSignInResult(result);
}
}
...
If someone still has this issue, this is how to fix it:
You probably forgot to switch the Google authentification on Firebase
You simply need to login to console.firebase.google.com --> Click on Authentification on the left --> Click on Sign-in Method --> Select Google and toggle the switch to activate.
While my app was in alpha and unpublished, anytime a user signed in to google play games i would get their id Games.Players.getCurrentPlayer(mGoogleApiClient).getPlayerId() and it would return an id in this format
g07610263060548408114
Now that I am in an open beta however, using the same call gives me an id in this format
117053902313732480537
(I'm not saying that the progression from alpha to beta or that i published caused this, but it's when i started noticing a change.)
From reading a few issues in the unity plugin project on github,
Native Android Google+ User ID different than using Unity Plugin #1277 and the issue it references, it seems that the second version of the id i'm now getting is an outdated version that is linked to the user's google plus account. The new version (with the prefixed g) is supposedly available so that the players don't need to have a google plus account in order to play my game. See this post for the google announcement about the replacing of ids. It sounds like the unity plugin returns the new id while the native android libraries do not.
So my question is, why in the newest version of google play games services (10.2.1) is the id that i get for my users the legacy id? And how can i get the new version-- the one that is the same as the participant ids in a match?
I've tried using google's new sign in api's but that also gives me the legacy id. Even for users where before i was getting the new id format.
I could use this legacy id everywhere and it would at least be consistent, however the only id i get from
turnBasedMatch.getParticipants.get(0).getPlayerId() is the new id, so i would never be able to map the two. I want to use the new id, but i can't get it anymore for new users.
My activity currently extends BaseGameActivity
and this is some of the code i use to initialize the client before i tried the new sign in
mPresenter = GameSetupPresenter.getInstance(getApiClient());
getApiClient().registerConnectionCallbacks(mPresenter);
getApiClient().registerConnectionFailedListener(mPresenter);
#Override
public void onSignInSucceeded() {
Games.Players.getCurrentPlayer(mPresenter.getGoogleApiClient()).getPlayerId();
}
This line where i get the playerId either returns the id in the legacy or the new format.
Using the new sign in process my code looks like this:
GoogleSignInOptions options = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN)
.requestId()
.requestIdToken(getString(R.string.server_client_id))
.requestProfile()
.requestScopes(new Scope(Scopes.GAMES), new Scope(Scopes.PROFILE), new Scope(Scopes.PLUS_ME))
.requestServerAuthCode(getString(R.string.server_client_id), false)
.build();
mPresenter = GameSetupPresenter.getInstance(null);
GoogleApiClient client = new GoogleApiClient.Builder(this)
.enableAutoManage(this, mPresenter)
.addApi(Auth.GOOGLE_SIGN_IN_API, options)
.addApi(Games.API)
.build();
mPresenter.bindGoogleApiClient(client);
client.registerConnectionCallbacks(mPresenter);
client.registerConnectionFailedListener(mPresenter);
Intent intent = Auth.GoogleSignInApi.getSignInIntent(client);
startActivityForResult(intent, RC_SIGN_IN);
#Override
protected void onActivityResult(int request, int response, Intent data) {
super.onActivityResult(request, response, data);
if (request == RC_SIGN_IN) {
if(response == RESULT_OK) {
Games.Players.getCurrentPlayer(mPresenter.getGoogleApiClient()).getPlayerId();//returns different id's based on whether user was created before or after game was published.
GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
result.getSignInAccount().getId();//returns legacy id
}
}
You can look at the samples in https://github.com/playgameservices/clientserverskeleton
One thing to be aware of is if you only use the games config and requestAuthCode(), there are no additional consent prompts for users.
The GoogleSignIn API does not use the games ID if you are requesting additional items and scopes. To get the Games specific ID use:
GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
GoogleSignInAccount acct = result.getSignInAccount();
Log.d(TAG,"Legacy account = " + acct.getId());
Log.d(TAG,"Games Id:" + Games.Players.getCurrentPlayer
(mGoogleApiClient).getPlayerId());
I can sign in my app with Google account at first several times.
Everything is fine.
But if I sign in and out about 20 times in a one or two minutes.
Google sign in failed and in onActivityResult function, it returns error code 12501, resultCode = 0;
I'm using the phone: Nexus 6, Android 5.1.1
Here is my codeļ¼
private GoogleSignInOptions mGso;
private GoogleApiClient mGac;
public void init(#NonNull final BaseActivity activity) {
mGso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(activity.getString(R.string.default_web_client_id))
.requestEmail()
.build();
mGac = new GoogleApiClient.Builder(activity)
.enableAutoManage(activity /* FragmentActivity */, new GoogleApiClient.OnConnectionFailedListener() {
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
ToastUtils.show(activity, R.string.login_failed);
}
})
.addApi(Auth.GOOGLE_SIGN_IN_API, mGso)
.build();
}
public void signIn(#NonNull final BaseActivity activity,
#NonNull GoogleSignInCallback callback,
#NonNull final OnLoadingListener<PlatformUserEntity> listener) {
callback.registerCallback(listener);
Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGac);
activity.startActivityForResult(signInIntent, REQUEST_GOOGLE_SIGNIN);
// disconnect the client
mGac.stopAutoManage(activity);
mGac.disconnect();
}
Here is gradle:
compile 'com.google.android.gms:play-services-base:9.6.1'
compile 'com.google.android.gms:play-services-gcm:9.6.1'
compile 'com.google.android.gms:play-services-auth:9.6.1'
Fisrt, I init the GoogleApiClient with a FragmentActivity, then signIn function starts the Acitvity. GoogleSignInCallback is registered in the onActivityResult function. Then disconnect the Client because every time the sign in button is clicked, the init function will be invoked.
I doubt that I use stopAutoManage() too early but it seems like not true.
So I'm confused, which part might be wrong?
I noticed the log:
Could not set socket write timeout: null
12-03 17:21:43.859 264-264/? W/SurfaceFlinger: couldn't log to binary event log: overflow.
12-03 17:21:43.902 1946-12870/? W/Conscrypt: Could not set socket write timeout: null
12-03 17:21:44.327 21168-21168/? W/AccountChipsFragment: Recording consent failed.
12-03 17:21:44.657 29359-29782/? E/TokenRequestor: You have wrong OAuth2 related configurations, please check. Detailed error: UNREGISTERED_ON_API_CONSOLE
12-03 17:21:44.664 812-1072/? W/ActivityManager: getRunningAppProcesses: caller 10145 does not hold REAL_GET_TASKS; limiting output
12-03 17:21:44.697 21168-21168/? W/AutoManageHelper: Unresolved error while connecting client. Stopping auto-manage.
It said "You have wrong OAuth2 related configuration", but I could use the web client id to request the IdToken at the first time.
It just makes me more confused.
I also found a strange thing. If I install the apk which is built locally, this error never happened. If I download from google play store, this error ocurred. But there is no difference between these two apks because I deliver google store with the local one.
Finally I found the reason.
My apk was signed again by our company's release procedure.
The procedure used another keystore so my sha1 key was changed.
I configure the new sha1 key in google develop console, this bug is solved.
But I'm still confused that, if I use the debug keystore apk, got sign in successfully and uninstall it, then I install the google play apk which has different sha1 key, google sign in can work some times.It won't tell me wrong immediately.
if your code working well at first time then
you can try that way,
private GoogleSignInOptions mGso;
private GoogleApiClient mGac;
public void signIn(#NonNull final BaseActivity activity,
#NonNull GoogleSignInCallback callback,
#NonNull final OnLoadingListener<PlatformUserEntity> listener) {
mGso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(activity.getString(R.string.default_web_client_id))
.requestEmail()
.build();
mGac = new GoogleApiClient.Builder(activity)
.enableAutoManage(activity /* FragmentActivity */, new GoogleApiClient.OnConnectionFailedListener() {
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
ToastUtils.show(activity, R.string.login_failed);
}
})
.addApi(Auth.GOOGLE_SIGN_IN_API, mGso)
.build();
callback.registerCallback(listener);
Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGac);
activity.startActivityForResult(signInIntent, REQUEST_GOOGLE_SIGNIN);
// disconnect the client
mGac.stopAutoManage(activity);
mGac.disconnect();
}
or
as your log mention:
You have wrong OAuth2 related configurations, please check. Detailed error: UNREGISTERED_ON_API_CONSOLE
So, Problem can be:
1. Sh1 key add in your api
2. Api key type like for android or web
3. Check Internet Connection
Sorry, for my english.
I hope this will help you. thanks
Try the solution in this SO question, by changing your code and using this code
mGso = newGoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(AuthenticatedActivity.this.getResources()
.getString(R.string.server_client_id))
.requestEmail()
.build();
For more information, check this another SO question if it can help you.
When using Firebase invites and accessing the dynamic links at the startup of the app on Android, is there a way to know whether the user just installed the app thanks to the invite or whether it was already installed?
Many thanks,
Borja
EDIT: Thanks to Catalin Morosan for the answer
It turns out that you can find this out using method AppInviteReferral.isOpenedFromPlayStore(result.getInvitationIntent()). In the activity that runs when you click on the invitation:
// Create an auto-managed GoogleApiClient with access to App Invites.
mGoogleApiClientInvite = new GoogleApiClient.Builder(this)
.addApi(AppInvite.API)
.enableAutoManage(this, this)
.build();
// Check for App Invite invitations and launch deep-link activity if possible.
// Requires that an Activity is registered in AndroidManifest.xml to handle
// deep-link URLs.
boolean autoLaunchDeepLink = false;
AppInvite.AppInviteApi.getInvitation(mGoogleApiClientInvite, this, autoLaunchDeepLink)
.setResultCallback(
new ResultCallback<AppInviteInvitationResult>() {
#Override
public void onResult(AppInviteInvitationResult result) {
if (result.getStatus().isSuccess()) {
// Extract information from the intent
Intent intent = result.getInvitationIntent();
String invitationId = AppInviteReferral.getInvitationId(intent);
boolean alreadyUser = AppInviteReferral.isOpenedFromPlayStore(result.getInvitationIntent());
if (alreadyUser) {
// Do stuff...
} else {
// Do other stuff...
}
}
}
});
Based on this Google product form post, the Firebase Dynamic Links library will only check for incoming deep links once per app lifetime, meaning you'd need to uninstall and reinstall the app for it to check again. This feeds into the behavior of the getInvitation() method, and it appears you can imply whether the app was previously installed based on the results of this method.
To me, this seems awfully confusing. At Branch.io we do it completely differently: your link data object will always contain an is_first_session boolean, which you can programmatically handle in any way you choose.