In my game I use Google Play Game for Achievements and Leaderboards.
I've just noticed (by logging into the Google API Console), that performing a simple action such as displaying a leaderboard, results in 2 API calls. I would have thought this would be only 1.
I'm simply calling the leaderboard like so:
public void displayLeaderBoard(){
if (getGameHelper().isSignedIn()){
if (leaderboardIntent==null){
leaderboardIntent = Games.Leaderboards.getLeaderboardIntent(getApiClient(), leaderboardID);
}
startActivityForResult(myLeaderboard, 1);
}
}
Note it is still 2 API calls even when pressing the leadeboard button a 2nd time (therefore not creating a new 'leaderboardIntent').
Also, when submitting a high score, it uses 3 API calls (one for submitting, then again it calls displayLeaderboard() to show the player her/his new high score.
The thing here is if I then exit back to the app and submit the score again, it uses another 3 API calls. The documentation states:
Both the Android and iOS client libraries will know not to send a
player's score to the server if your score isn't as good as one you
recently submitted.
I know I could simply store a copy of the high score in sharedPreferences and then not submit it if it's not high enough, but I'm not sure about this - what if the device has multiple accounts set up for example.
I would be grateful if someone with more knowledge/experience of the Play Games API could confirm if the number of API calls I'm seeing is correct and how this relates to quote above, or whether there is something more I should be doing in my code?
The number of calls you are seeing could very well be correct. For many APIs, each request has a "cost" related to it. Which means where a read request to a certain API might cost you 1 call, a write request might cost you 5 (just assuming). Hence, depending on the requests you are making your number of calls are going to differ compared to the number of requests made. For example, try this tool to calculate Youtube API quota cost. Unfortunately I couldn't find any such tool or documentation for Play Games Services API but I hope this makes my point clear.
For optimizing your code to perform it's best, take a look here and try to optimize your code to follow Best Practices as much as you can.
Related
This is not a repeated question as all other questions either refer to the REST Api or retrieve step count from the device (usually the phone) on which the app is running. It is also different from android wear questions, as no separate app can be built for the Smartband2.
I want to retrieve the heart rate and the step count. I am successfully able to get the heart-rate via the BLE Api
However, for the step-count I've been having a lot of trouble.
I understand that the best way to get the step-count is through Google Fit APIs.
I tried using the Sensor Api, but only my phone on which the app is running gets listed as a Data Source.
On using the Google fit BLE Api ,the band gets detected only when I search for TYPE_HEART_RATE_BPM, but not when TYPE_STEP_COUNT_CUMULATIVE or TYPE_STEP_COUNT_DELTA
From the comment by noogui here, I understand I'll have to use the Recording and History Api.
On the other hand, there's an option to create Additional Sensorfrom similar questions posted earlier.
I'm utterly confused as how to go about this now.
TLDR: Could someone please list out the steps and the APIs I will need to get step-count data from Smartband2? An example would be very helpful.
The SmartBand2 matches step data against the step counter in the phone to provide a more accurate reading, and because of that you can't get step delta or cadence from the Sensors API. You can either try using the LifeLog SDK (which I haven't tried but it looked like you could use it for semi live sensor data) or grab step data from the History API.
And before you complain about not getting live data let me just say I'm working with a bunch of test units from different makers and the SmartBand 2 has the cleanest and most accurate data stream of any of them. The lack of live step count is a pain but most bands don't deliver that anyway, and the ones that do that I know about have turned out to be overpriced garbage in every other respect.
I was able to get the heart-rate and step-count data using the following method:
Pair the Smartband2 with the official Sony Smartband2 app.
In the app: settings -> Google fit -> Turn on
Write an app that uses the History api from Google Fit. Example: https://github.com/googlesamples/android-fit/tree/master/BasicHistoryApi
Fetch data for every minute for last ten minutes for Heart-rate and step-count
No Ble api or other additional raw sensor creation was required.
Hi I'm using the Google Drive Api to store a database using the AppDataFolder facility. I have a test app running successfully on one device. I am able to upload/update/delete/download the database file and reintegrate it into the program with no problems.
The issue I'm having is when I want to share this database with another device running the same app, then things don't work as expected. Example device A I upload the database, device B - I want to download the database but no file is found (this delay can vary greatly from seconds to hours). Reason for this - when using the Api it decides when it wants to 'sync' data, as it is queued rather than being instantaneously uploaded. So when used on one device this is not a problem because it takes either the 'synced' file from the cloud storage, or file waiting to be synced.
I have tried various things like trying to list all AppDataFolder files or retrieving metadata through a query with searchable filters before making a request to update/delete etc.. However I can't get it to work as desired fundamentally it chooses when to sync.
So my actual question is: How can I force Google Drive to sync my file when I want it to i.e. every time a request is made, so that synchronisation is achieved across multiple devices using the same app. There must be an answer as I would think this is quite a fundamental reason why you would use the AppDataFolder is the first place.
Thanks in advance
EDIT/UPDATE:
I have been able to find an option in the Api to 'sync' the drive content using this code:
// try to sync
Drive.DriveApi.requestSync(mGoogleApiClient).setResultCallback(new ResultCallback<com.google.android.gms.common.api.Status>() {
#Override
public void onResult(com.google.android.gms.common.api.Status status) {
if (!status.getStatus().isSuccess()) {
Log.e("SYNCING", "ERROR" + status.getStatusMessage());
} else {
Log.e("SYNCING", "SUCCESS");
// execute async task to list AppFolderContents
new AppFolderContentsAsyncTask(getActivity()).execute();
}
}
});
This works well for 3/4 attempts in quick succession, however I reach a syncing limit and status message:
ERRORSync request rate limit exceeded.
Is there any way to increase the request rate as this is not really desirable if I have to have a app that prompts the user 'please try again later to sync - not sure how long you'll have to wait until you can though!'
SOLUTION - OF SORTS, AND MY THOUGHTS (for what its worth)
The solution that I am going for (after emailing a app dev whose published a drive app that synchronizes without problems) is to use the Drive REST Api, rather than the newer (and Google preferred) Drive API. I tried limiting the 'requestSync' to only when the user navigated to a fragment with the Drive options (rather than every file transaction). However this would probably solve the requestSync rate limit for the most part, but it still could hit that limit. Also if multiple devices are running the app, and linked to the same Drive account were both syncing/uploading/deleting files at the same time in the app then there is a possibility of losing synchronization - maybe a rare scenario, but still possible. I don't feel that making a user wait to sync files is a viable option in terms of user experience or app design.
Curious though - The actual Drive app lets you refresh (requestSync?) as many times as you like. I created 20 folders on the web interface in quick succession, after each folder was created I refreshed the Drive app on my phone and it synced all 20 times. It seems Google understands the importance of synchronization, but chooses to make this quite difficult ensure this in their new Drive API. As already stated uploading files to the cloud storage happens usually instantly (it is queued, however if you have connectivity it happens almost straight away). I would have thought that this is the costly transaction in terms of moving data/updating drive contents, rather than just querying for synchronization of files. However you can keep adding files to your your drive app one at a time, and it uploads them one at a time almost instantly - where as you request sync more than 4 times in 30 seconds and it then fails and you have to 'cool off' for a while.
I'm very new to programming/Android in general - as soon as I learn something new, I realize how little I actually know, so if their is a better solution out there using the Drive API (rather than REST API) I'd very much welcome it.
DriveApi#requestSync will let you request a "sync down" from the server to the device. This way, on the second device you should be able to see the content uploaded from the first device. However, the calls to request sync are rate limited (per-device) to avoid abuse and guarantee a reasonable amount of data usage by the Drive API. You should ideally call request sync only when you need to. There's no way to increase the rate limit.
Regarding upload completion, after committing an upload, you shouldn't use request sync since that won't help you (it only syncs down but not up). The actual upload will happen as soon as possible (based on the device's connectivity and the preferences specified by your app through DrivePreferencesApi, which by default are unrestricted).
If you want to know when the actual upload happened, you can use CompletionEvents. This way you can test your app with more insights on what's actually going on.
I have a leaderboard with tamper protection ON since it's creation, but a player has added a hacked score of some millions of points (human reachable max of points is less than 1000)
How can I delete only that hacked score? It must be a way to do it... but can't find in documentation: https://developers.google.com/games/services/common/concepts/leaderboards
Check out the players.hide() call. This will hide a player and all of his/her scores from the public leaderboard so that nobody else can see them.
Note that this REST call is not built into the mobile libraries; you'll need to create your own mini web app (or make curl calls directly) to use this call, although there is a management tools sample app that you can use here which might make your life easier.
Oh, and if you know that the human reachable high score has to be 1000 max, specify a "Max score" of 1000 for that leaderboard in the Play Games Console.
What I do is some kind of obfuscation.
I mean, I do replace numbers with an string like
0 = #$%#&*52$*#
1 = #$%#&*84$*$
2 = #$%#&*58$*#
separated for another unique string like: %$$90&*
and so on.
so when I go to post the score, first I do a find and replace
the string for each number, as I am posting the score locally the
data would look like;
122 = #$%#&*84$*$%$$90&*#$%#&*58$*#%$$90&*#$%#&*58$*#
so its a little bit difficult (not impossible) to post a hacked score
You can use Google Developers OAuth 2.0 Playground to do the players.hide() as mentioned by #todd-kerpelman, following the instruction in this article, but it is still a very tedious process.
I used the management-tools before too, but it seems to be not working any more.
i am currently developing a realtime multiplayer game for android and i got the example ButtonClicker working. I think the "quick game"(or maybe it´s called automatch) option is just what i need. But i need something like this: I would like to make the player play a match with a random player from his level. You see, on my game, every player will have a level and these levels and associated players will maybe be stored on a database. I want the player to have a quick game with only the players from his level. Is it possible to inform that criteria on quick game? What must i do?
I also want him to be able to choose his friends(the invite option) but only friends that are on his level.
In order to restrict automatching between players, you can use the variant option when building a room configuration, ie RoomConfig.Builder#setVariant(int).
In order to restrict invitations, I think you will have to create your own "invite player" activity, which I am less familiar with. You will presumably have to query for the player's friends, and then cross-correlate this list with each of their "levels" and filter it down to those of the same level before showing it in the activity. It looks like The Players API has some methods that you will be able to interact with, and hopefully would contain an Id that you use in your level-based DB.
Rob
When you create your RoomConfig for match making you can provide AutoMatchCriteria. The API just uses a bitmask, so it is up to you what data you put in that bit mask:
createAutoMatchCriteria
exclusiveBitMask: Exclusive bitmasks for the automatching request. The
logical AND of each pairing of automatching requests must equal zero
for auto-match. If there are no exclusivity requirements for the game,
this value should just be set to 0.
In your case this could contain a level range to match up with.
You can see it being used in the sample code here: Developing Real-Time Multiplayer Games.
According to the GPGS website-
Use the client libraries. The mobile client libraries employ a number
of strategies to reduce the calls you make to the service. For
instance, achievement and leaderboard data is cached, so a user can
view their achievements as often as they'd like without requiring the
service to make multiple calls. Both the Android and iOS client
libraries will know not to send a player's score to the server if
your score isn't as good as one you recently submitted. The Android
library also automatically combines frequent achievement increment
calls when it detects that you are being rate limited.
I'm specifically interested in 'viewing achievements without requiring the service to make multiple calls'. As far as I can see, the best way to do this would be to use the OnAchievementsLoadedListener and get a reference to the achievementBuffer.
Will that buffer be updated when an achievement state is changed and is it okay not to close that buffer immediately? If not, the OnAchievementsUpdatedListener does not pass a buffer or even and indiviudal achievement reference so how would I get the updated collection of achievements?
I found my answer here:
https://developer.android.com/reference/com/google/android/gms/games/GamesClient.html#loadAchievements%28com.google.android.gms.games.achievement.OnAchievementsLoadedListener,%20boolean%29
Set forceReload argument to false to gain the advantages of data-caching :)