I am using google play services leaderboard to upload the score of a user. Is there a way to retrieve the score of a user programmatically?
My use case is that I want to run multiplier game where users have level which goes up and down based on their wins/loses (sort of like stackoverflow reputation). I need to retrieve the old score to upload the new modified score.
How can I do that? I didn't see this anywhere on the tutorials.
Thanks
In theory, you can by doing something like that:
gameHelper.getGamesClient().loadLeaderboardMetadata(this, false);
/**
* Called when the data is loaded
*/
#Override
public void onLeaderboardMetadataLoaded(int statusCode, LeaderboardBuffer leaderboards) {
// If the fragment is currently added to an activity
if (isAdded()) {
// There's been an error on leaderboards loading
if (statusCode != GamesClient.STATUS_OK) {
if (leaderboards != null) leaderboards.close();
// Error
return;
}
ArrayList<LeaderboardVariant> variants;
Iterator<Leaderboard> it = leaderboards.iterator();
while (it.hasNext()) {
Leaderboard leaderboard = it.next();
final String id = leaderboard.getLeaderboardId();
variants = leaderboard.getVariants();
// For each leaderboard
for (LeaderboardVariant variant: variants) {
if (variant.getCollection() == LeaderboardVariant.COLLECTION_PUBLIC &&
variant.getTimeSpan() == LeaderboardVariant.TIME_SPAN_ALL_TIME) {
int rawScore = (int) variant.getRawPlayerScore();
}
}
}
}
if (leaderboards != null) leaderboards.close();
}
... but the reality is that you can't because the data is outdated, see here: Play games loadLeaderboardMetadata() returns outdated data
It probably is a bug, but as there's no place to submit play-games bugs...
Anyway, why don't you keep a local copy of the scores you submitted? Wouldn't it be the easier way to do it?
[UPDATE]
It seems that you can achieve what you want by using the loadCurrentPlayerLeaderboardScore instead of the loadLeaderboardMetadata method. That one doesn't seem to return outdated data. Check it here
In case someone is still looking for the solution, according to the latest android releases,the method described in the above answer is deprecated now.
The Latest way to do it is as follows:
private LeaderboardsClient mLeaderboardsClient = Games.getLeaderboardsClient(this, googleSignInAccount);
private void updateLeaderboards(final String leaderboardId) {
mLeaderboardsClient.loadCurrentPlayerLeaderboardScore(
leaderboardId,
LeaderboardVariant.TIME_SPAN_ALL_TIME,
LeaderboardVariant.COLLECTION_PUBLIC
).addOnSuccessListener(new OnSuccessListener<AnnotatedData<LeaderboardScore>>() {
#Override
public void onSuccess(AnnotatedData<LeaderboardScore> leaderboardScoreAnnotatedData) {
if (leaderboardScoreAnnotatedData.get() == null)
mLeaderboardsClient.submitScore(leaderboardId, 1);
else {
long currentscore = leaderboardScoreAnnotatedData.get().getRawScore();
mLeaderboardsClient.submitScore(leaderboardId, currentscore + 1);
}
}
});
}
Related
I'm developing an android app with achievements. Is it possible to know if the achievement is unlocked (or if the goal is reached on incremental ones)?
I've found solutions for old implementations which use Games.Achievements and add a callback function, but it is deprecated. This is the code I'm using for unlocking my achievements:
Games.getAchievementsClient(MyActivity.this, acct).unlockImmediate(my_achievement_id));
I'd like to add a listener to know if after the execution of my code the achievement is unlocked.
Solved using load method:
Games.getAchievementsClient(MyActivity.this, acct).load(true).addOnSuccessListener(new OnSuccessListener<AnnotatedData<AchievementBuffer>>() {
#Override
public void onSuccess(AnnotatedData<AchievementBuffer> achievementBufferAnnotatedData) {
AchievementBuffer buffer = achievementBufferAnnotatedData.get();
Achievement achievement;
Iterator<Achievement> iterator = buffer.iterator();
while (iterator.hasNext()) {
achievement = iterator.next();
if (achievement.getAchievementId().equals(myAchievementId) && achievement.getState() == Achievement.STATE_UNLOCKED) {
// The achievement is unlocked
} else {
// The achievement is locked
}
}
}
});
I'm experiencing a really annoying problem which i didn't have in any previous game which used Google Play Games API.
My game is working well, not yet published, but the API is published, and everything is working, from submitting a score to viewing the leaderboard.
Now, i need to explain the issue first.
My API account on the Google dev Console has two Oauth keys, the debug and the realese. When i finished the game and tried to view the leaderboard, i viewed it without any problem, but when tried to submit a score, the leaderboard opens and then close without showing my local/global score, its like a glitch in the leaderboard.
I've fixed this issue by publishing the Game's Service. Now i'm having the same issue but with the released APK, the isseu was fixed in the Debug, and i've tried the same steps with the released APK, nothing work.
Every Google account will have the same issue viewing the leaderboard if a score has been submitted, no issue will occur when the user try to only view the leaderboard.
Both Debug and Release keys are added to the firebase and Oauth in Google Dev API Control, and already added the emails im using as testers.
Long story short: After submitting a score, the leaderboard won't open (the leaderboards menu will show, but when selecting a leaderboard this glitched closing will happen).
No logcat was caught, and my code is as the following:
Connection code / not needed
To view the leaderboards:
private void OpenLeaderboards() {
if(mGoogleApiClient.isConnected()){
try {
UpdateAddictionLeaderboard(ReturnSavedClick());
startActivityForResult(Games.Leaderboards.getAllLeaderboardsIntent(mGoogleApiClient), REQUEST_LEADERBOARD);
} catch (SecurityException e){
mGoogleApiClient.disconnect();
mGoogleApiClient.connect();
}
} else {
isTried = false;
mGoogleApiClient.connect();
}
}
To submit a score:
private void UpdateTopScore(final String LeaderBoard, final String Title, final int mSavedScore, final int mMethod){
Games.Leaderboards.loadCurrentPlayerLeaderboardScore(
mGoogleApiClient,LeaderBoard,
LeaderboardVariant.TIME_SPAN_ALL_TIME,
LeaderboardVariant.COLLECTION_PUBLIC)
.setResultCallback(new ResultCallback<Leaderboards.LoadPlayerScoreResult>() {
#Override
public void onResult(#NonNull Leaderboards.LoadPlayerScoreResult arg0) {
if(isScoreResultValid(arg0)){
long score = arg0.getScore().getRawScore();
Bundle bundle = new Bundle();
bundle.putLong(FirebaseAnalytics.Param.SCORE, score);
bundle.putString(LeaderBoard, Title + " Leaderboard");
mFirebaseAnalytics.logEvent(FirebaseAnalytics.Event.POST_SCORE, bundle);
if(mSavedScore > score){
Games.Leaderboards.submitScore(mGoogleApiClient, LeaderBoard, mSavedScore);
} else {
if(mMethod == 0) {
mEditor.putInt(FIRSTSCORE, mSavedScore).apply();
} else {
mEditor.putInt(SECONDSCORE, mSavedScore).apply();
}
}
} else {
Games.Leaderboards.submitScore(mGoogleApiClient, LeaderBoard, mSavedScore);
}
}
});
}
private boolean isScoreResultValid(final Leaderboards.LoadPlayerScoreResult scoreResult) {
return scoreResult != null && GamesStatusCodes.STATUS_OK == scoreResult.getStatus().getStatusCode() && scoreResult.getScore() != null;
}
I'm trying to implement Facebook sharing in my game using Unity 3D + Facebook Unity SDK. But when I tried testing to post to my wall, this error shows up: "We are Sorry, this post is no longer available. It may have been removed." Can anybody help me? Thanks in advance.
BTW, here's my code:
using UnityEngine;
using System.Collections;
public class FacebookController : MonoBehaviour {
public bool isUsingFacebook = true; //Are we using Facebook SDK? This variable is only
//Feed parameters.
private string link = "market://details?id=com.LaserCookie.Queue"; //The link that will show the user the game's google play store address
private string linkName = "Queue"; //The link name
private string linkCaption = "Wow this game is great! 10/10 must play!"; // The caption of the link
private string linkDescription = "I achieved the score of " + PlayerController.instance.score.ToString() + "! Try to beat me if you can!"; //The description of the link
private string picture = "http://www.drycreekequestriancenter.com/testImage.jpeg"; //This is the image / game icon for the link. For now, it's shamelessly got from a random source. Thank you, random citizen...
void Awake()
{
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
//Init FB
private void Init()
{
if (!FB.IsInitialized)
{
FB.Init(OnInitComplete, OnHideUnity);
}
}
//Callback that will be called when the initialization is completed
private void OnInitComplete()
{
Debug.Log("FB.Init completed: Is user logged in? " + FB.IsLoggedIn);
//Check if we are logged in.
//If not, we will log in.
//If we are, post status.
if (!FB.IsLoggedIn)
{
LoginWithPublish();
}
else
{
PostImpl();
}
}
//Callback that will be called when the game is shown / not
private void OnHideUnity(bool isGameShown)
{
Debug.Log("Is game showing? " + isGameShown);
}
//Post to Facebook. This is the only exposed method because we only use this to post to Facebook.
//The initialization and login will be called when needed.
//It will first detect whether or not we have been initialized. And will init if we haven't.
//Then it will check whether or not we have been logged in with publish. And will log in if not.
public void PostToFacebook()
{
//Are we using facebook SDK?
if (isUsingFacebook)
{
if (!FB.IsInitialized) //Check for initialization
{
Init();
}
else if (!FB.IsLoggedIn) //Check for login
{
LoginWithPublish();
}
else //Post if we are already initia;ized and logged in
{
PostImpl();
}
}
}
//The true implementation of the posting
private void PostImpl()
{
FB.Feed("",link, linkName,linkCaption,linkDescription,picture);
}
//Login to Facebook with publish
private void LoginWithPublish()
{
// It is generally good behavior to split asking for read and publish
// permissions rather than ask for them all at once.
//
// In your own game, consider postponing this call until the moment
// you actually need it.
FB.Login("publish_actions", LoginCallback);
}
//Login callback
void LoginCallback(FBResult result)
{
if (result.Error != null)
{
Debug.Log( "Error Response:\n" + result.Error );
//TODO: Retry login if we have error? Or do we display a pop up?
}
else if (!FB.IsLoggedIn)
{
Debug.Log( "Login cancelled by Player" );
//TODO: Do we display a pop up?
}
else
{
Debug.Log( "Login was successful!" );
PostImpl();
}
}
}
You need to add Key Hash for FB application.
Go to My Apps, select you application, open Setting tab, add platform for android, and add you key hash.
check this link out
Setting a Release Key Hash
I've fixed the issue. It turns out it's because I used my still in development google store address as the link. I thought it would be automatically recognized regardless of my app is live or not. Thank you anyway. :)
I have some incremental achievements on my game and i want to check if one of them is already unlocked. How can i do that?
Originaly i used this method:
getGamesClient().incrementAchievement(achievement, increment);
Then i tried to use this:
mHelper.getGamesClient().incrementAchievementImmediate(new OnAchievementUpdatedListener()
{
#Override
public void onAchievementUpdated(int statusCode, String achievementId)
{
// TODO: Check if the achievement got unlocked
}
}, achievement, increment);
Is this the right way to do what i want? Is there a better way?
I only got two statusCode values on my tests.
Value: 5 = STATUS_NETWORK_ERROR_OPERATION_DEFERRED
Value: 0 = STATUS_OK
Can some one help me with this?
Thanks
Declare this inner class:
class AchievementClass implements ResultCallback<Achievements.LoadAchievementsResult> {
#Override
public void onResult(LoadAchievementsResult arg0) {
Achievement ach;
AchievementBuffer aBuffer = arg0.getAchievements();
Iterator<Achievement> aIterator = aBuffer.iterator();
while (aIterator.hasNext()) {
ach = aIterator.next();
if ("The Achievement Id you are checking".equals(ach.getAchievementId())) {
if (ach.getState() == Achievement.STATE_UNLOCKED) {
// it is unlocked
} else {
//it is not unlocked
}
aBuffer.close();
break;
}
}
}
}
And use it like so:
Games.Achievements.load(ApiClient, false).setResultCallback(new AchievementClass());
This will get achievement data for the currently signed in player, go through all the achievements, and when it reaches the achievement you're concerned with, it will check if it is unlocked.
I am working from the Dungeons example that the Android provides, and it feels like I almost got it correctly, but I can't figure out how to detect if the person pressed the back button and backed out of the purchase. Here is what I have so far:
Button buy = (Button)findViewById(R.id.buy);
buy.setOnClickListener(new Button.OnClickListener()
{
public void onClick(View v)
{
try
{
if (mBillingService.requestPurchase(issueProductId,
Consts.ITEM_TYPE_INAPP , null))
{
// Not sure what to do here. Has the person been sent to the buy screen yet or no?
// There are these strings I need to check for but not entirely certain how:
if(mBillingService.checkBillingSupported(Consts.ITEM_TYPE_INAPP))
{
// OK
}
else
{
// If billing is not supported, give them the item for free
Intent myIntent = new Intent(ExtraHelpActivity.this, PsychologyActivity.class);
ExtraHelpActivity.this.startActivity(myIntent);
}
try
{
if (mBillingService.requestPurchase(issueProductIdPsych,
Consts.ITEM_TYPE_INAPP , null))
{
// HOW DO I CHECK HERE IF THE USER CANCELED OUT OF THE PURCHASE?
// EVEN ON CANCEL, THEY ARE BEING TAKEN TO THE ITEM THAT THEY NEED TO PAY FOR.
// Send them to the screen of the article.
Intent myIntent = new Intent(ExtraHelpActivity.this, PsychologyActivity.class);
ExtraHelpActivity.this.startActivity(myIntent);
}
}
catch ( Exception e )
{
// Report exception.
}
});
I added some inline comments where I am confused. I am not certain how to read whether the person purchased the item, or cancelled. If someone could help me with this, that would be great.
Right now this code takes the person to the buy screen, and some people buy, but when people press cancel, they still get taken to the item which they didn't buy. :)
EDIT:
By the way, if users already bought the item and want to come back to it, the purchase request state does not change, right? So what method ends up being triggered? I tested with one purchase and I was able to buy access to one screen with an article, but now can not get to it again since the payment screen keeps telling me I already bought that item, and not taking me to the next page.
Here is how my new onStateChanged method looks like:
#Override
public void onPurchaseStateChange(PurchaseState purchaseState, String itemId,
int quantity, long purchaseTime, String developerPayload)
{
if (purchaseState == PurchaseState.PURCHASED)
{
mOwnedItems.add(itemId);
if ( itemId != null && itemId.trim().equals("3") )
{
Intent myIntent = new Intent(ExtraHelpActivity.this, PsychologyActivity.class);
ExtraHelpActivity.this.startActivity(myIntent);
}
if ( itemId != null && itemId.trim().equals("4") )
{
Intent myIntent = new Intent(ExtraHelpActivity.this, NumberOfBusinessesActivity.class);
}
}
else
if (purchaseState == PurchaseState.CANCELED)
{
// purchase canceled
}
else
if (purchaseState == PurchaseState.REFUNDED)
{
// user ask for a refund
}
else
{
if ( itemId != null && itemId.equals("3") )
{
Intent myIntent = new Intent(ExtraHelpActivity.this, PsychologyActivity.class);
ExtraHelpActivity.this.startActivity(myIntent);
}
if ( itemId != null && itemId.equals("4") )
{
Intent myIntent = new Intent(ExtraHelpActivity.this, NumberOfBusinessesActivity.class);
ExtraHelpActivity.this.startActivity(myIntent);
}
}
}
and when I tested it and bought an article, it did get here to the if (purchaseState == PurchaseState.PURCHASED) but not the individual if cases inside it even though the itemId id is "3" or "4"
EDIT:
and this is how I declare the product ids at the beginning of the Activity:
String issueProductIdWebMarketing = "1";
String issueProductIdDonate = "2";
String issueProductIdPsych = "3";
String issueProductIdNumberofBusinesses = "4";
Thanks!!
According to the billing integration guide:
when the requested transaction changes state (for example, the purchase is successfully charged to a credit
card or the user cancels the purchase), the Google Play application sends an IN_APP_NOTIFY broadcast intent.
This message contains a notification ID, which you can use to retrieve the transaction details for the
REQUEST_PURCHASE request.
And the intent com.android.vending.billing.PURCHASE_STATE_CHANGED contains purchaseState
The purchase state of the order. Possible values are 0 (purchased), 1 (canceled), 2 (refunded), or 3
(expired, for subscription purchases only).
source (table 4)
EDIT:
In the dungeons sample, there is an inner class extending PurchaseObserver (see Dungeons activity) that does the job, see especially (onPurchaseStateChange)
EDIT2: some pseudo-code
in the activity (Dungeons class in the sdk sample):
Button buy = (Button)findViewById(R.id.buy);
buy.setEnabled(false);
buy.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v)
{
// show purchase dialog and call mBillingService.requestPurchase() on dialog validation
}
});
// request to see if supported
if (!mBillingService.checkBillingSupported()) {
// not supported
}
in the observer (DungeonsPurchaseObserver in the sample):
public void onBillingSupported(boolean supported, String type) {
if (type == null || type.equals(Consts.ITEM_TYPE_INAPP)) {
if (supported) {
// billing supported: enable buy button
} else {
// billing not supported: disable buy button
}
}
}
public void onPurchaseStateChange(PurchaseState purchaseState, String itemId,
int quantity, long purchaseTime, String developerPayload) {
if (purchaseState == PurchaseState.PURCHASED) {
// item purchased
} else if (purchaseState == PurchaseState.CANCELED) {
// purchase canceled
} else if (purchaseState == PurchaseState.REFUND) {
// user ask for a refund
}
}
The problem is that the call mBillingService.requestPurchase is not synchronous. So you can not check right after if the purchase was successful or not.
I recommend you use the library AndroidBillingLibrary. With it you can easily get all the Google Play's events. For that you can use onRequestPurchaseResponse() and onPurchaseStateChanged() from the helpers AbstractBillingActivity or AbstractBillingFragment.