I'm using google play game services in my app. The one thing that bothers me is that when the user signs out and later back in again, he is not asked to approve the access anymore.
This is my sign out method, pretty much from the examples:
public void signOut() {
if (!mGoogleApiClient.isConnected()) {
// nothing to do
debugLog("signOut: was already disconnected, ignoring.");
return;
}
// for Plus, "signing out" means clearing the default account and
// then disconnecting
if (0 != (mRequestedClients & GameHelper.CLIENT_PLUS)) {
debugLog("Clearing default account on PlusClient.");
Plus.AccountApi.clearDefaultAccount(mGoogleApiClient);
}
// For the games client, signing out means calling signOut and
// disconnecting
if (0 != (mRequestedClients & GameHelper.CLIENT_GAMES)) {
debugLog("Signing out from the Google API Client.");
Games.signOut(mGoogleApiClient);
}
// Ready to disconnect
debugLog("Disconnecting client.");
mGoogleApiClient.disconnect();
}
After this, the app is still connected as I can see in the google settings app. The next time I call mGoogleApiClient.connect();, it connects automatically without asking the user again.
Is there a way to either completely disconnect the app programmatically, or another way to have google play game services ask the user for permission the next time I connect?
Thanks
Simon
Related
My attempts to integrate Google Play Games Sign-In are mostly working, however there is one oddity:
If the user does not already have a Play Games account and declines to sign-in via the automatic sign-in process, I provide a "sign-in" button that does a manual sign-in. When they click it in this case, they are seamlessly prompted to create a Google Play Games account. Seems great. It works... they are signed in, they can participate in my leaderboard, everything seems functional...
HOWEVER: The Google Play Games account that is created by this particular flow seems to (sometimes) be malformed. If you attempt to view the profile for this player (within the Google Play Games app, using another account), an error is displayed. An example of such a "malformed" Play Games account is: LadyAnnie123
If you try to view that Play Games profile using the Play Games app (please do), you'll see an error message that says "Yikes, there's a problem... It's not you, it's us." (Cute.) The account is set to "Everyone can see your game activity" and "Receive friend invites" is activated.
[EDIT:] As of 7/31/22, only about HALF of the accounts that I try creating using the flow described above are broken like this. Some of them seem to be perfectly normal.
Is this even my fault? Can somebody show me an example of a functional manual sign-in button (that doesn't sometimes create disfunctional Play Games accounts in the case where the user's account does not already exist)?
In onCreate():
gamesSignInClient = PlayGames.getGamesSignInClient(this);
gamesSignInClient.isAuthenticated().addOnCompleteListener(isAuthenticatedTask -> {
boolean isAuthenticated = (isAuthenticatedTask.isSuccessful() &&
isAuthenticatedTask.getResult().isAuthenticated());
if (isAuthenticated) {
signInStatus = true;
setupLeaderboard();
} else {
signInStatus = false;
}
});
My sign-in button executes signIn() like this:
gamesSignInClient.signIn().addOnCompleteListener(isAuthenticatedTask -> {
boolean isAuthenticated = (isAuthenticatedTask.isSuccessful() &&
isAuthenticatedTask.getResult().isAuthenticated());
if (isAuthenticated) {
signInStatus = true;
setupLeaderboard();
}
});
THANKS!
I have an app with a subscription in Google Play.
When the user starts the app, I need to know if the user has an active subscription. This would seem an obvious thing to do, but from searching and trying to implement it, it seems impossible?
I am using Google's newer billing 2/3, following Google's tutorials,
class BillingManager implements PurchasesUpdatedListener
...
public void checkAsync() {
Log.e(TAG, "checkAsync");
billingClient.queryPurchaseHistoryAsync(BillingClient.SkuType.SUBS, new PurchaseHistoryResponseListener() {
#Override
public void onPurchaseHistoryResponse(BillingResult billingResult, List<PurchaseHistoryRecord> list) {
Log.e(TAG, "checkCached result: " + list);
if (list == null) {
return;
}
for (PurchaseHistoryRecord ps : list) {
//System.out.println("PAYMENT: " + ps.getSku() + " : " + ps.getPurchaseTime());
}
}
});
}
public void checkCached() {
Log.e(TAG, "checkCached");
List<Purchase> result = billingClient.queryPurchases(BillingClient.SkuType.SUBS).getPurchasesList();
Log.e(TAG, "checkCached result: " + result);
if (result == null) {
return;
}
for (Purchase purchase : result) {
handlePurchase(purchase);
}
}
This is how I think you're supposed to get a user's purchases. But it does not work at all, both calls return null always. It only returns the correct purchases when you reinstall the app or clear the data.
So how exactly is an app supposed to do this?
Purchasing works for the app once I enter internal testing, and download it through the Google Play link. (before that subscriptions do not work at all).
*** updated
So to further clarify:
I am using a valid test user, and subscriptions are working correctly. My question is on the what the API queryPurchases() or queryPurchaseHistoryAsync() are suppose to do.
What I am seeing, is that these only return purchases that have not be processed by the app. They seem to store that the purchase was processed in the apps data.
After the purchase these return null, after the app restarts these return null.
If I clear the app datam or reinstall the app then they return the purchase (once), then again null after restart.
From what I see, these are only useful to detect when a user reinstalls your app, or installs on a different phone. They cannot be used to determine the status of a subscription.
So my question is,
1 - is this something that just does not work in internal testing and will magically work differently when the app is release?
2 - is there a different API that your suppose to use to check the status of a subscription?
3 - are you suppose to manage subscriptions yourself in your app by storing a user preference/cookie when you acknowledge the subscription the first time so you know when the subscription expires?
You need "licenced testers". They would allow you to "sideload" your app on devices, even for debug builds. My interpretation of sideload in this case would cover installing from Android Studio build tools as well as adb install .... and other methods that don't involve the play store.
https://developer.android.com/google/play/billing/test
Ordinarily, the Google Play Billing API is blocked for apps that aren't signed and uploaded to Google Play. License testers can bypass this check, meaning you can sideload apps for testing, even for apps using debug builds with debug signatures without the need to upload to the new version of your app. Note that the package name must match that of the app that is configured for Google Play, and the Google account must be a license tester for the Google Play Console account.
I also don't see how you're using startConnection. Until that's completed successfully I wouldn't be sure you have the latest data. I wouldn't be surprised if that makes you get stale values. I would check that carefully to make sure there's no silent errors happening, by both looking at onBillingSetupFinished and onBillingServiceDisconnected. And for the time being avoid trusting queryPurchases():
https://medium.com/#NandagopalR/integrating-google-play-billing-into-an-android-application-b6eb6af176a7
The queryPurchases() method uses a cache of the Google Play Store app without initiating a network request. If you need to check the most recent purchase made by the user for each product ID, you can use queryPurchaseHistoryAsync(), passing the purchase type and a PurchaseHistoryResponseListener to handle the query result.
By the way what's the value of isReady() right before queryPurchaseHistoryAsync, and what's the value of BillingResult::getDebugMessage and BillingResult::getResponseCode?
Also, use isFeatureSupported, though it seems it's not like your problem is coming from here. But I'd advise not testing with subscriptions until you get all the moving parts working: https://developer.android.com/reference/com/android/billingclient/api/BillingClient#isFeatureSupported(java.lang.String)
Okay, figured it out, was my mistake.
I was calling queryPurchases() in my main activity onCreate(), but the BillingClient was not ready yet.
I moved it to onBillingSetupFinished() and it now returns the correct purchases.
Everything is now working as expected. You get the active subscriptions when you call queryPurchases() after an app restart.
I'm integrating Google Smart Lock into my app. It seems to work very well except in the case where the user logs out. First, some background ...
On app startup, when the user is not logged in, I make this call:
CredentialRequest r = new CredentialRequest.Builder()
.setPasswordLoginSupported(true)
.setAccountTypes(IdentityProviders.FACEBOOK)
.build();
Auth.CredentialsApi.request(googleApiClient, r)
.setResultCallback(result -> {
Status status = result.getStatus();
if (status.isSuccess()) {
onCredentialReceived(result.getCredential());
} else if (status.getStatusCode() == CommonStatusCodes.RESOLUTION_REQUIRED) {
try {
status.startResolutionForResult(activity, REQUEST_CREDENTIALS);
} catch (IntentSender.SendIntentException e) {
Timber.e(e, "Couldn't start a resolution request for the user's credentials");
}
}
});
Upon logout, my app makes this call:
Auth.CredentialsApi.disableAutoSignIn(googleApiClient)
.setResultCallback(status -> {
if (!status.isSuccess()) Timber.e("Error disabling auto sign in");
});
This works well on the first startup of the app; if the user has a single saved credential, I grab it from Google and log the user in seamlessly. If the user has multiple saved credentials, I start the account picker and grab the credential that way. When the user logs out, I disable auto sign in.
Unfortunately, if the user opens the app again after logging out, the result returned from Auth.CredentialsApi.request() tells me a resolution is required, so I ultimately show the account picker again, and again, and again each time the app is restarted. I had assumed that disabling auto sign in would cause subsequent calls to Auth.CredentialsApi.request() to return a status code != CommonStatusCodes.RESOLUTION_REQUIRED; I expected something like CommonStatusCodes.SIGN_IN_REQUIRED (meaning the user needs to explicitly sign in again and agree to save credentials with Smart Lock) or CommonStatusCodes.CANCELED (meaning Smart Lock is disabled at the moment).
Is this a bug in the Smart Lock API or am I doing something wrong? Thanks!
How best to handle this depends on the UX of the app (e.g. is it sign-in required or optional UX?), so it's up to the developer: if it doesn't make sense to always show the credential selection dialog, simply don't resolve the credentialRequestResult (i.e. don't call startResolutionForResult) if the status is RESOLUTION_REQUIRED.
For example, you can call the request API when the app starts, but if you find the user cancels the dialog, set some persistent local state (e.g. in shared preferences) not to show it again (i.e. don't resolve) until they take explicit action to sign in (e.g. navigates to the sign-in UI).
I'd recommend keeping track of whether user canceled the dialog rather than signed out since user might not want to sign in at all in the first place.
I followed the below link to implement a "sign out" button in my android app, which uses a Google API client. However, upon connecting the google api again, the user is not presented with an account picker. It looks like the value of her/his original choice is somehow still cached perhaps. I've been trying to figure this out for a few hours.
Any and all ideas very welcome. Thank you.
https://developers.google.com/+/mobile/android/sign-in
if (mGoogleApiClient.isConnected()) {
Plus.AccountApi.clearDefaultAccount(mGoogleApiClient);
mGoogleApiClient.disconnect();
}
I've had many problems using clearDefaultAccount and trying to reconnect as well. Finally I've decided to separate the account selection process by using the AccountPicker class (which, by the way, doesn't require global permissions in manifest).
So, when the user wants to connect, always show the AccountPicker and then use the selected account to build your GoogleApiClient (see .setAccountName in GoogleApiClient.Builder).
Everything works smoothly now.
This works for me - use revoke to remove all data in the google client:
public void logout()
{
if (mPlusClient.isConnected())
{
Plus.AccountApi.clearDefaultAccount(mPlusClient);
Plus.AccountApi.revokeAccessAndDisconnect(mPlusClient);
}
}
Afterwards, if you try to login again, you'll be presented an account selector again
You are not being presented with an account picker because you didn't call
mGoogleApiClient.connect() after reconnecting.
I'm updating my game to the new Google Play Serviced library, leaderboards and achievements were already working flawlessly, but now when I try to open the achievement activity, it immediately closes without showing any exception in logcat.
I'm logged in with my google account, which is correctly configured as a test user.
public void gameServicesGetAchievements() {
if (gameHelper == null)
return;
Intent i = Games.Achievements.getAchievementsIntent(gameHelper.getApiClient());
((Activity) ctx).startActivityForResult(i, REQUEST_CODE_ACHIEVEMENTS);
}
The activity opens, but closes immediately before showing the achievements list. This is what I get in logcat
D/GameHelper(30459): GameHelper: onActivityResult: req=9802, resp=RESULT_RECONNECT_REQUIRED
D/GameHelper(30459): GameHelper: onActivityResult: request code not meant for us. Ignoring.
I tried handling the RESULT_RECONNECT_REQUIRED code in my onActivityResult, but nothing changes.
#Override
public void onActivityResult(int request, int response, Intent data) {
super.onActivityResult(request, response, data);
GameHelper helper = resolver.getGameServicesHelper();
if (helper != null) {
helper.onActivityResult(request, response, data);
}
if (response == GamesActivityResultCodes.RESULT_RECONNECT_REQUIRED) {
helper.disconnect();
}
}
The leaderboards activities start correctly and work flawlessly.
Found the problem... it wasn't a problem in my app, just the Google Play Games app on my device had some corrupted data or something.
I followed the steps here and that fixed it.
http://howlukeseesit.blogspot.it/2014/08/fixing-google-play-games.html
First navigate to settings > apps and find and tap on Google play
games.
Next tap uninstall updates, then tap clear data.
Next go back
to the apps section and find Google Play Services, tap that then tap
Manage space and then tap on clear all data.
Now all you have to do
is go into the Play store and manually update Google Play Games.