As far as I can tell, unlock()
will check with the local Play Games app first to see if the achievement is already unlocked, and only if it is not will it send the request to the server. This means that calling unlock()
on an already unlocked achievement should not affect any quotas apart from the first time it is called on a device - since the device's instance of the Play Games may not have synced that achievement from the server yet.
On the other hand, it is possible to check the state of all achievements after signing-in and keeping a local copy of their states - which indeed you should do if the player is NOT signed-in (so that you can unlock any achievements earned at the next sign-in). This process is not documented well anywhere except for the API documentation but there are no full examples, so I will give you one here :)
Ensure that you run this method asynchronously (using AsyncTask
or a separate thread) as it will take some time to complete (waiting for responses from the server). Also, do this only when the player is signed in successfully - onSignInSucceeded()
is a good place for it.
Update for Play Service Games 11.8.0
public void loadAchievements() {
mAchievementsClient.load(true).addOnCompleteListener(new OnCompleteListener<AnnotatedData<AchievementBuffer>>() {
@Override
public void onComplete(@NonNull Task<AnnotatedData<AchievementBuffer>> task) {
AchievementBuffer buff = task.getResult().get();
Log.d("BUFF", "onComplete: ");
int bufSize = buff.getCount();
for (int i=0; i < bufSize; i++) {
Achievement ach = buff.get(i);
String id = ach.getAchievementId();
boolean unlocked = ach.getState() == Achievement.STATE_UNLOCKED;
}
buff.release();
}
});
Older Play Service Games
public void loadAchievements() {
boolean fullLoad = false; // set to 'true' to reload all achievements (ignoring cache)
float waitTime = 60.0f; // seconds to wait for achievements to load before timing out
// load achievements
PendingResult p = Games.Achievements.load( playHelper.getApiClient(), fullLoad );
Achievements.LoadAchievementsResult r = (Achievements.LoadAchievementsResult)p.await( waitTime, TimeUnit.SECONDS );
int status = r.getStatus().getStatusCode();
if ( status != GamesStatusCodes.STATUS_OK ) {
r.release();
return; // Error Occured
}
// cache the loaded achievements
AchievementBuffer buf = r.getAchievements();
int bufSize = buf.getCount();
for ( int i = 0; i < bufSize; i++ ) {
Achievement ach = buf.get( i );
// here you now have access to the achievement's data
String id = ach.getAchievementId(); // the achievement ID string
boolean unlocked = ach.getState == Achievement.STATE_UNLOCKED; // is unlocked
boolean incremental = ach.getType() == Achievement.TYPE_INCREMENTAL; // is incremental
if ( incremental )
int steps = ach.getCurrentSteps(); // current incremental steps
}
buf.close();
r.release();
}
See the API documentation for more info on these processes.
So using this method you could create a local copy of the achievements (just alter the internals of the for loop to save the achievement values to some local structure) and refer to it as needed.