// // Copyright (C) 2014 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #if UNITY_ANDROID #pragma warning disable 0642 // Possible mistaken empty statement namespace GooglePlayGames.Android { using GooglePlayGames.BasicApi; using GooglePlayGames.BasicApi.SavedGame; using GooglePlayGames.OurUtils; using System; using System.Collections.Generic; using GooglePlayGames.BasicApi.Events; using GooglePlayGames.BasicApi.Video; using UnityEngine; using UnityEngine.SocialPlatforms; public class AndroidClient : IPlayGamesClient { private enum AuthState { Unauthenticated, Authenticated } private readonly object GameServicesLock = new object(); private readonly object AuthStateLock = new object(); private readonly PlayGamesClientConfiguration mConfiguration; private volatile ISavedGameClient mSavedGameClient; private volatile IEventsClient mEventsClient; private volatile IVideoClient mVideoClient; private volatile AndroidTokenClient mTokenClient; private volatile Player mUser = null; private volatile AuthState mAuthState = AuthState.Unauthenticated; private IUserProfile[] mFriends = new IUserProfile[0]; private LoadFriendsStatus mLastLoadFriendsStatus = LoadFriendsStatus.Unknown; AndroidJavaClass mGamesClass = new AndroidJavaClass("com.google.android.gms.games.Games"); private static string TasksClassName = "com.google.android.gms.tasks.Tasks"; private AndroidJavaObject mFriendsResolutionException = null; private readonly int mLeaderboardMaxResults = 25; // can be from 1 to 25 private readonly int mFriendsMaxResults = 200; // the maximum load friends page size internal AndroidClient(PlayGamesClientConfiguration configuration) { PlayGamesHelperObject.CreateObject(); this.mConfiguration = Misc.CheckNotNull(configuration); } /// /// public void Authenticate(bool silent, Action callback) { lock (AuthStateLock) { // If the user is already authenticated, just fire the callback, we don't need // any additional work. if (mAuthState == AuthState.Authenticated) { Debug.Log("Already authenticated."); InvokeCallbackOnGameThread(callback, SignInStatus.Success); return; } } InitializeTokenClient(); Debug.Log("Starting Auth with token client."); mTokenClient.FetchTokens(silent, (int result) => { bool succeed = result == 0 /* CommonStatusCodes.SUCCEED */; InitializeGameServices(); if (succeed) { using (var signInTasks = new AndroidJavaObject("java.util.ArrayList")) { AndroidJavaObject taskGetPlayer = getPlayersClient().Call("getCurrentPlayer"); AndroidJavaObject taskGetActivationHint = getGamesClient().Call("getActivationHint"); AndroidJavaObject taskIsCaptureSupported = getVideosClient().Call("isCaptureSupported"); if (!mConfiguration.IsHidingPopups) { AndroidJavaObject taskSetViewForPopups; using (var popupView = AndroidHelperFragment.GetDefaultPopupView()) { taskSetViewForPopups = getGamesClient().Call("setViewForPopups", popupView); } signInTasks.Call("add", taskSetViewForPopups); } signInTasks.Call("add", taskGetPlayer); signInTasks.Call("add", taskGetActivationHint); signInTasks.Call("add", taskIsCaptureSupported); using (var tasks = new AndroidJavaClass(TasksClassName)) using (var allTask = tasks.CallStatic("whenAll", signInTasks)) { AndroidTaskUtils.AddOnCompleteListener( allTask, completeTask => { if (completeTask.Call("isSuccessful")) { using (var resultObject = taskGetPlayer.Call("getResult")) { mUser = AndroidJavaConverter.ToPlayer(resultObject); } var account = mTokenClient.GetAccount(); lock (GameServicesLock) { mSavedGameClient = new AndroidSavedGameClient(this, account); mEventsClient = new AndroidEventsClient(account); bool isCaptureSupported; using (var resultObject = taskIsCaptureSupported.Call("getResult")) { isCaptureSupported = resultObject.Call("booleanValue"); } mVideoClient = new AndroidVideoClient(isCaptureSupported, account); } mAuthState = AuthState.Authenticated; InvokeCallbackOnGameThread(callback, SignInStatus.Success); GooglePlayGames.OurUtils.Logger.d("Authentication succeeded"); LoadAchievements(ignore => { }); } else { SignOut(); if (completeTask.Call("isCanceled")) { InvokeCallbackOnGameThread(callback, SignInStatus.Canceled); return; } using (var exception = completeTask.Call("getException")) { GooglePlayGames.OurUtils.Logger.e( "Authentication failed - " + exception.Call("toString")); InvokeCallbackOnGameThread(callback, SignInStatus.InternalError); } } } ); } } } else { lock (AuthStateLock) { Debug.Log("Returning an error code."); InvokeCallbackOnGameThread(callback, SignInHelper.ToSignInStatus(result)); } } }); } private static Action AsOnGameThreadCallback(Action callback) { if (callback == null) { return delegate { }; } return result => InvokeCallbackOnGameThread(callback, result); } private static void InvokeCallbackOnGameThread(Action callback) { if (callback == null) { return; } PlayGamesHelperObject.RunOnGameThread(() => { callback(); }); } private static void InvokeCallbackOnGameThread(Action callback, T data) { if (callback == null) { return; } PlayGamesHelperObject.RunOnGameThread(() => { callback(data); }); } private static Action AsOnGameThreadCallback( Action toInvokeOnGameThread) { return (result1, result2) => { if (toInvokeOnGameThread == null) { return; } PlayGamesHelperObject.RunOnGameThread(() => toInvokeOnGameThread(result1, result2)); }; } private static void InvokeCallbackOnGameThread(Action callback, T1 t1, T2 t2) { if (callback == null) { return; } PlayGamesHelperObject.RunOnGameThread(() => { callback(t1, t2); }); } private void InitializeGameServices() { if (mTokenClient != null) { return; } InitializeTokenClient(); } private void InitializeTokenClient() { if (mTokenClient != null) { return; } mTokenClient = new AndroidTokenClient(); if (!GameInfo.WebClientIdInitialized() && (mConfiguration.IsRequestingIdToken || mConfiguration.IsRequestingAuthCode)) { OurUtils.Logger.e("Server Auth Code and ID Token require web clientId to configured."); } string[] scopes = mConfiguration.Scopes; // Set the auth flags in the token client. mTokenClient.SetWebClientId(GameInfo.WebClientId); mTokenClient.SetRequestAuthCode(mConfiguration.IsRequestingAuthCode, mConfiguration.IsForcingRefresh); mTokenClient.SetRequestEmail(mConfiguration.IsRequestingEmail); mTokenClient.SetRequestIdToken(mConfiguration.IsRequestingIdToken); mTokenClient.SetHidePopups(mConfiguration.IsHidingPopups); mTokenClient.AddOauthScopes("https://www.googleapis.com/auth/games_lite"); if (mConfiguration.EnableSavedGames) { mTokenClient.AddOauthScopes("https://www.googleapis.com/auth/drive.appdata"); } mTokenClient.AddOauthScopes(scopes); mTokenClient.SetAccountName(mConfiguration.AccountName); } /// /// Gets the user's email. /// /// The email address returned is selected by the user from the accounts present /// on the device. There is no guarantee this uniquely identifies the player. /// For unique identification use the id property of the local player. /// The user can also choose to not select any email address, meaning it is not /// available. /// The user email or null if not authenticated or the permission is /// not available. public string GetUserEmail() { if (!this.IsAuthenticated()) { Debug.Log("Cannot get API client - not authenticated"); return null; } return mTokenClient.GetEmail(); } /// /// Returns an id token, which can be verified server side, if they are logged in. /// /// A callback to be invoked after token is retrieved. Will be passed null value /// on failure. /// The identifier token. public string GetIdToken() { if (!this.IsAuthenticated()) { Debug.Log("Cannot get API client - not authenticated"); return null; } return mTokenClient.GetIdToken(); } /// /// Asynchronously retrieves the server auth code for this client. /// /// Note: This function is currently only implemented for Android. /// The Client ID. /// Callback for response. public string GetServerAuthCode() { if (!this.IsAuthenticated()) { Debug.Log("Cannot get API client - not authenticated"); return null; } return mTokenClient.GetAuthCode(); } public void GetAnotherServerAuthCode(bool reAuthenticateIfNeeded, Action callback) { mTokenClient.GetAnotherServerAuthCode(reAuthenticateIfNeeded, AsOnGameThreadCallback(callback)); } /// /// public bool IsAuthenticated() { lock (AuthStateLock) { return mAuthState == AuthState.Authenticated; } } public void LoadFriends(Action callback) { LoadAllFriends(mFriendsMaxResults, /* forceReload= */ false, /* loadMore= */ false, callback); } private void LoadAllFriends(int pageSize, bool forceReload, bool loadMore, Action callback) { LoadFriendsPaginated(pageSize, loadMore, forceReload, result => { mLastLoadFriendsStatus = result; switch (result) { case LoadFriendsStatus.Completed: InvokeCallbackOnGameThread(callback, true); break; case LoadFriendsStatus.LoadMore: // There are more friends to load. LoadAllFriends(pageSize, /* forceReload= */ false, /* loadMore= */ true, callback); break; case LoadFriendsStatus.ResolutionRequired: case LoadFriendsStatus.InternalError: case LoadFriendsStatus.NotAuthorized: InvokeCallbackOnGameThread(callback, false); break; default: GooglePlayGames.OurUtils.Logger.d("There was an error when loading friends." + result); InvokeCallbackOnGameThread(callback, false); break; } }); } public void LoadFriends(int pageSize, bool forceReload, Action callback) { LoadFriendsPaginated(pageSize, /* isLoadMore= */ false, /* forceReload= */ forceReload, callback); } public void LoadMoreFriends(int pageSize, Action callback) { LoadFriendsPaginated(pageSize, /* isLoadMore= */ true, /* forceReload= */ false, callback); } private void LoadFriendsPaginated(int pageSize, bool isLoadMore, bool forceReload, Action callback) { mFriendsResolutionException = null; using (var playersClient = getPlayersClient()) using (var task = isLoadMore ? playersClient.Call("loadMoreFriends", pageSize) : playersClient.Call("loadFriends", pageSize, forceReload)) { AndroidTaskUtils.AddOnSuccessListener( task, annotatedData => { using (var playersBuffer = annotatedData.Call("get")) { AndroidJavaObject metadata = playersBuffer.Call("getMetadata"); var areMoreFriendsToLoad = metadata != null && metadata.Call("getString", "next_page_token") != null; mFriends = AndroidJavaConverter.playersBufferToArray(playersBuffer); mLastLoadFriendsStatus = areMoreFriendsToLoad ? LoadFriendsStatus.LoadMore : LoadFriendsStatus.Completed; InvokeCallbackOnGameThread(callback, mLastLoadFriendsStatus); } }); AndroidTaskUtils.AddOnFailureListener(task, exception => { AndroidHelperFragment.IsResolutionRequired(exception, resolutionRequired => { if (resolutionRequired) { mFriendsResolutionException = exception.Call("getResolution"); mLastLoadFriendsStatus = LoadFriendsStatus.ResolutionRequired; mFriends = new IUserProfile[0]; InvokeCallbackOnGameThread(callback, LoadFriendsStatus.ResolutionRequired); } else { mFriendsResolutionException = null; if (Misc.IsApiException(exception)) { var statusCode = exception.Call("getStatusCode"); if (statusCode == /* GamesClientStatusCodes.NETWORK_ERROR_NO_DATA */ 26504) { mLastLoadFriendsStatus = LoadFriendsStatus.NetworkError; InvokeCallbackOnGameThread(callback, LoadFriendsStatus.NetworkError); return; } } mLastLoadFriendsStatus = LoadFriendsStatus.InternalError; OurUtils.Logger.e("LoadFriends failed: " + exception.Call("toString")); InvokeCallbackOnGameThread(callback, LoadFriendsStatus.InternalError); } }); return; }); } } public LoadFriendsStatus GetLastLoadFriendsStatus() { return mLastLoadFriendsStatus; } public void AskForLoadFriendsResolution(Action callback) { if (mFriendsResolutionException == null) { GooglePlayGames.OurUtils.Logger.d("The developer asked for access to the friends " + "list but there is no intent to trigger the UI. This may be because the user " + "has granted access already or the game has not called loadFriends() before."); using (var playersClient = getPlayersClient()) using ( var task = playersClient.Call("loadFriends", /* pageSize= */ 1, /* forceReload= */ false)) { AndroidTaskUtils.AddOnSuccessListener( task, annotatedData => { InvokeCallbackOnGameThread(callback, UIStatus.Valid); }); AndroidTaskUtils.AddOnFailureListener(task, exception => { AndroidHelperFragment.IsResolutionRequired(exception, resolutionRequired => { if (resolutionRequired) { mFriendsResolutionException = exception.Call("getResolution"); AndroidHelperFragment.AskForLoadFriendsResolution( mFriendsResolutionException, AsOnGameThreadCallback(callback)); } else { var statusCode = exception.Call("getStatusCode"); if (statusCode == /* GamesClientStatusCodes.NETWORK_ERROR_NO_DATA */ 26504) { InvokeCallbackOnGameThread(callback, UIStatus.NetworkError); return; } Debug.Log("LoadFriends failed with status code: " + statusCode); InvokeCallbackOnGameThread(callback, UIStatus.InternalError); } }); return; }); } } else { AndroidHelperFragment.AskForLoadFriendsResolution(mFriendsResolutionException, AsOnGameThreadCallback(callback)); } } public void ShowCompareProfileWithAlternativeNameHintsUI(string playerId, string otherPlayerInGameName, string currentPlayerInGameName, Action callback) { AndroidHelperFragment.ShowCompareProfileWithAlternativeNameHintsUI( playerId, otherPlayerInGameName, currentPlayerInGameName, GetUiSignOutCallbackOnGameThread(callback)); } public void GetFriendsListVisibility(bool forceReload, Action callback) { using (var playersClient = getPlayersClient()) using ( var task = playersClient.Call("getCurrentPlayer", forceReload)) { AndroidTaskUtils.AddOnSuccessListener(task, annotatedData => { AndroidJavaObject currentPlayerInfo = annotatedData.Call("get").Call( "getCurrentPlayerInfo"); int playerListVisibility = currentPlayerInfo.Call("getFriendsListVisibilityStatus"); InvokeCallbackOnGameThread(callback, AndroidJavaConverter.ToFriendsListVisibilityStatus(playerListVisibility)); }); AndroidTaskUtils.AddOnFailureListener(task, exception => { InvokeCallbackOnGameThread(callback, FriendsListVisibilityStatus.NetworkError); return; }); } } public IUserProfile[] GetFriends() { return mFriends; } /// /// public void SignOut() { SignOut( /* uiCallback= */ null); } public void SignOut(Action uiCallback) { if (mTokenClient == null) { InvokeCallbackOnGameThread(uiCallback); return; } mTokenClient.Signout(); mAuthState = AuthState.Unauthenticated; if (uiCallback != null) { InvokeCallbackOnGameThread(uiCallback); } PlayGamesHelperObject.RunOnGameThread(() => SignInHelper.SetPromptUiSignIn(true)); } /// /// public string GetUserId() { if (mUser == null) { return null; } return mUser.id; } /// /// public string GetUserDisplayName() { if (mUser == null) { return null; } return mUser.userName; } /// /// public string GetUserImageUrl() { if (mUser == null) { return null; } return mUser.AvatarURL; } public void SetGravityForPopups(Gravity gravity) { if (!IsAuthenticated()) { GooglePlayGames.OurUtils.Logger.d("Cannot call SetGravityForPopups when not authenticated"); } using (var gamesClient = getGamesClient()) using (gamesClient.Call("setGravityForPopups", (int) gravity | (int) Gravity.CENTER_HORIZONTAL)) ; } /// /// public void GetPlayerStats(Action callback) { using (var playerStatsClient = getPlayerStatsClient()) using (var task = playerStatsClient.Call("loadPlayerStats", /* forceReload= */ false)) { AndroidTaskUtils.AddOnSuccessListener( task, annotatedData => { using (var playerStatsJava = annotatedData.Call("get")) { int numberOfPurchases = playerStatsJava.Call("getNumberOfPurchases"); float avgSessionLength = playerStatsJava.Call("getAverageSessionLength"); int daysSinceLastPlayed = playerStatsJava.Call("getDaysSinceLastPlayed"); int numberOfSessions = playerStatsJava.Call("getNumberOfSessions"); float sessionPercentile = playerStatsJava.Call("getSessionPercentile"); float spendPercentile = playerStatsJava.Call("getSpendPercentile"); float spendProbability = playerStatsJava.Call("getSpendProbability"); float churnProbability = playerStatsJava.Call("getChurnProbability"); float highSpenderProbability = playerStatsJava.Call("getHighSpenderProbability"); float totalSpendNext28Days = playerStatsJava.Call("getTotalSpendNext28Days"); PlayerStats result = new PlayerStats( numberOfPurchases, avgSessionLength, daysSinceLastPlayed, numberOfSessions, sessionPercentile, spendPercentile, spendProbability, churnProbability, highSpenderProbability, totalSpendNext28Days); InvokeCallbackOnGameThread(callback, CommonStatusCodes.Success, result); } }); AddOnFailureListenerWithSignOut( task, e => { Debug.Log("GetPlayerStats failed: " + e.Call("toString")); var statusCode = IsAuthenticated() ? CommonStatusCodes.InternalError : CommonStatusCodes.SignInRequired; InvokeCallbackOnGameThread(callback, statusCode, new PlayerStats()); }); } } /// /// public void LoadUsers(string[] userIds, Action callback) { if (!IsAuthenticated()) { InvokeCallbackOnGameThread(callback, new IUserProfile[0]); return; } using (var playersClient = getPlayersClient()) { object countLock = new object(); int count = userIds.Length; int resultCount = 0; IUserProfile[] users = new IUserProfile[count]; for (int i = 0; i < count; ++i) { using (var task = playersClient.Call("loadPlayer", userIds[i])) { AndroidTaskUtils.AddOnSuccessListener( task, annotatedData => { using (var player = annotatedData.Call("get")) { string playerId = player.Call("getPlayerId"); for (int j = 0; j < count; ++j) { if (playerId == userIds[j]) { users[j] = AndroidJavaConverter.ToPlayer(player); break; } } lock (countLock) { ++resultCount; if (resultCount == count) { InvokeCallbackOnGameThread(callback, users); } } } }); AddOnFailureListenerWithSignOut(task, exception => { Debug.Log("LoadUsers failed for index " + i + " with: " + exception.Call("toString")); lock (countLock) { ++resultCount; if (resultCount == count) { InvokeCallbackOnGameThread(callback, users); } } }); } } } } /// /// public void LoadAchievements(Action callback) { using (var achievementsClient = getAchievementsClient()) using (var task = achievementsClient.Call("load", /* forceReload= */ false)) { AndroidTaskUtils.AddOnSuccessListener( task, annotatedData => { using (var achievementBuffer = annotatedData.Call("get")) { int count = achievementBuffer.Call("getCount"); Achievement[] result = new Achievement[count]; for (int i = 0; i < count; ++i) { Achievement achievement = new Achievement(); using (var javaAchievement = achievementBuffer.Call("get", i)) { achievement.Id = javaAchievement.Call("getAchievementId"); achievement.Description = javaAchievement.Call("getDescription"); achievement.Name = javaAchievement.Call("getName"); achievement.Points = javaAchievement.Call("getXpValue"); long timestamp = javaAchievement.Call("getLastUpdatedTimestamp"); achievement.LastModifiedTime = AndroidJavaConverter.ToDateTime(timestamp); achievement.RevealedImageUrl = javaAchievement.Call("getRevealedImageUrl"); achievement.UnlockedImageUrl = javaAchievement.Call("getUnlockedImageUrl"); achievement.IsIncremental = javaAchievement.Call("getType") == 1 /* TYPE_INCREMENTAL */; if (achievement.IsIncremental) { achievement.CurrentSteps = javaAchievement.Call("getCurrentSteps"); achievement.TotalSteps = javaAchievement.Call("getTotalSteps"); } int state = javaAchievement.Call("getState"); achievement.IsUnlocked = state == 0 /* STATE_UNLOCKED */; achievement.IsRevealed = state == 1 /* STATE_REVEALED */; } result[i] = achievement; } achievementBuffer.Call("release"); InvokeCallbackOnGameThread(callback, result); } }); AddOnFailureListenerWithSignOut( task, exception => { Debug.Log("LoadAchievements failed: " + exception.Call("toString")); InvokeCallbackOnGameThread(callback, new Achievement[0]); }); } } /// /// public void UnlockAchievement(string achId, Action callback) { if (!IsAuthenticated()) { InvokeCallbackOnGameThread(callback, false); return; } using (var achievementsClient = getAchievementsClient()) { achievementsClient.Call("unlock", achId); InvokeCallbackOnGameThread(callback, true); } } /// /// public void RevealAchievement(string achId, Action callback) { if (!IsAuthenticated()) { InvokeCallbackOnGameThread(callback, false); return; } using (var achievementsClient = getAchievementsClient()) { achievementsClient.Call("reveal", achId); InvokeCallbackOnGameThread(callback, true); } } /// /// public void IncrementAchievement(string achId, int steps, Action callback) { if (!IsAuthenticated()) { InvokeCallbackOnGameThread(callback, false); return; } using (var achievementsClient = getAchievementsClient()) { achievementsClient.Call("increment", achId, steps); InvokeCallbackOnGameThread(callback, true); } } /// /// public void SetStepsAtLeast(string achId, int steps, Action callback) { if (!IsAuthenticated()) { InvokeCallbackOnGameThread(callback, false); return; } using (var achievementsClient = getAchievementsClient()) { achievementsClient.Call("setSteps", achId, steps); InvokeCallbackOnGameThread(callback, true); } } /// /// public void ShowAchievementsUI(Action callback) { if (!IsAuthenticated()) { InvokeCallbackOnGameThread(callback, UIStatus.NotAuthorized); return; } AndroidHelperFragment.ShowAchievementsUI(GetUiSignOutCallbackOnGameThread(callback)); } /// /// public int LeaderboardMaxResults() { return mLeaderboardMaxResults; } /// /// public void ShowLeaderboardUI(string leaderboardId, LeaderboardTimeSpan span, Action callback) { if (!IsAuthenticated()) { InvokeCallbackOnGameThread(callback, UIStatus.NotAuthorized); return; } if (leaderboardId == null) { AndroidHelperFragment.ShowAllLeaderboardsUI(GetUiSignOutCallbackOnGameThread(callback)); } else { AndroidHelperFragment.ShowLeaderboardUI(leaderboardId, span, GetUiSignOutCallbackOnGameThread(callback)); } } private void AddOnFailureListenerWithSignOut(AndroidJavaObject task, Action callback) { AndroidTaskUtils.AddOnFailureListener( task, exception => { var statusCode = exception.Call("getStatusCode"); if (statusCode == /* CommonStatusCodes.SignInRequired */ 4 || statusCode == /* GamesClientStatusCodes.CLIENT_RECONNECT_REQUIRED */ 26502) { SignOut(); } callback(exception); }); } private Action GetUiSignOutCallbackOnGameThread(Action callback) { Action uiCallback = (status) => { if (status == UIStatus.NotAuthorized) { SignOut(() => { if (callback != null) { callback(status); } }); } else { if (callback != null) { callback(status); } } }; return AsOnGameThreadCallback(uiCallback); } /// /// public void LoadScores(string leaderboardId, LeaderboardStart start, int rowCount, LeaderboardCollection collection, LeaderboardTimeSpan timeSpan, Action callback) { using (var client = getLeaderboardsClient()) { string loadScoresMethod = start == LeaderboardStart.TopScores ? "loadTopScores" : "loadPlayerCenteredScores"; using (var task = client.Call( loadScoresMethod, leaderboardId, AndroidJavaConverter.ToLeaderboardVariantTimeSpan(timeSpan), AndroidJavaConverter.ToLeaderboardVariantCollection(collection), rowCount)) { AndroidTaskUtils.AddOnSuccessListener( task, annotatedData => { using (var leaderboardScores = annotatedData.Call("get")) { InvokeCallbackOnGameThread(callback, CreateLeaderboardScoreData( leaderboardId, collection, timeSpan, annotatedData.Call("isStale") ? ResponseStatus.SuccessWithStale : ResponseStatus.Success, leaderboardScores)); leaderboardScores.Call("release"); } }); AddOnFailureListenerWithSignOut(task, exception => { AndroidHelperFragment.IsResolutionRequired( exception, resolutionRequired => { if (resolutionRequired) { mFriendsResolutionException = exception.Call( "getResolution"); InvokeCallbackOnGameThread( callback, new LeaderboardScoreData(leaderboardId, ResponseStatus.ResolutionRequired)); } else { mFriendsResolutionException = null; } }); Debug.Log("LoadScores failed: " + exception.Call("toString")); InvokeCallbackOnGameThread( callback, new LeaderboardScoreData(leaderboardId, ResponseStatus.InternalError)); }); } } } /// /// public void LoadMoreScores(ScorePageToken token, int rowCount, Action callback) { using (var client = getLeaderboardsClient()) using (var task = client.Call("loadMoreScores", token.InternalObject, rowCount, AndroidJavaConverter.ToPageDirection(token.Direction))) { AndroidTaskUtils.AddOnSuccessListener( task, annotatedData => { using (var leaderboardScores = annotatedData.Call("get")) { InvokeCallbackOnGameThread(callback, CreateLeaderboardScoreData( token.LeaderboardId, token.Collection, token.TimeSpan, annotatedData.Call("isStale") ? ResponseStatus.SuccessWithStale : ResponseStatus.Success, leaderboardScores)); leaderboardScores.Call("release"); } }); AddOnFailureListenerWithSignOut(task, exception => { AndroidHelperFragment.IsResolutionRequired(exception, resolutionRequired => { if (resolutionRequired) { mFriendsResolutionException = exception.Call("getResolution"); InvokeCallbackOnGameThread( callback, new LeaderboardScoreData(token.LeaderboardId, ResponseStatus.ResolutionRequired)); } else { mFriendsResolutionException = null; } }); Debug.Log("LoadMoreScores failed: " + exception.Call("toString")); InvokeCallbackOnGameThread( callback, new LeaderboardScoreData(token.LeaderboardId, ResponseStatus.InternalError)); }); } } private LeaderboardScoreData CreateLeaderboardScoreData( string leaderboardId, LeaderboardCollection collection, LeaderboardTimeSpan timespan, ResponseStatus status, AndroidJavaObject leaderboardScoresJava) { LeaderboardScoreData leaderboardScoreData = new LeaderboardScoreData(leaderboardId, status); var scoresBuffer = leaderboardScoresJava.Call("getScores"); int count = scoresBuffer.Call("getCount"); for (int i = 0; i < count; ++i) { using (var leaderboardScore = scoresBuffer.Call("get", i)) { long timestamp = leaderboardScore.Call("getTimestampMillis"); System.DateTime date = AndroidJavaConverter.ToDateTime(timestamp); ulong rank = (ulong) leaderboardScore.Call("getRank"); string scoreHolderId = ""; using (var scoreHolder = leaderboardScore.Call("getScoreHolder")) { scoreHolderId = scoreHolder.Call("getPlayerId"); } ulong score = (ulong) leaderboardScore.Call("getRawScore"); string metadata = leaderboardScore.Call("getScoreTag"); leaderboardScoreData.AddScore(new PlayGamesScore(date, leaderboardId, rank, scoreHolderId, score, metadata)); } } leaderboardScoreData.NextPageToken = new ScorePageToken(scoresBuffer, leaderboardId, collection, timespan, ScorePageDirection.Forward); leaderboardScoreData.PrevPageToken = new ScorePageToken(scoresBuffer, leaderboardId, collection, timespan, ScorePageDirection.Backward); using (var leaderboard = leaderboardScoresJava.Call("getLeaderboard")) using (var variants = leaderboard.Call("getVariants")) using (var variant = variants.Call("get", 0)) { leaderboardScoreData.Title = leaderboard.Call("getDisplayName"); if (variant.Call("hasPlayerInfo")) { System.DateTime date = AndroidJavaConverter.ToDateTime(0); ulong rank = (ulong) variant.Call("getPlayerRank"); ulong score = (ulong) variant.Call("getRawPlayerScore"); string metadata = variant.Call("getPlayerScoreTag"); leaderboardScoreData.PlayerScore = new PlayGamesScore(date, leaderboardId, rank, mUser.id, score, metadata); } leaderboardScoreData.ApproximateCount = (ulong) variant.Call("getNumScores"); } return leaderboardScoreData; } /// /// public void SubmitScore(string leaderboardId, long score, Action callback) { if (!IsAuthenticated()) { InvokeCallbackOnGameThread(callback, false); } using (var client = getLeaderboardsClient()) { client.Call("submitScore", leaderboardId, score); InvokeCallbackOnGameThread(callback, true); } } /// /// public void SubmitScore(string leaderboardId, long score, string metadata, Action callback) { if (!IsAuthenticated()) { InvokeCallbackOnGameThread(callback, false); } using (var client = getLeaderboardsClient()) { client.Call("submitScore", leaderboardId, score, metadata); InvokeCallbackOnGameThread(callback, true); } } public void RequestPermissions(string[] scopes, Action callback) { callback = AsOnGameThreadCallback(callback); mTokenClient.RequestPermissions(scopes, code => { UpdateClients(); callback(code); }); } private void UpdateClients() { lock (GameServicesLock) { var account = mTokenClient.GetAccount(); mSavedGameClient = new AndroidSavedGameClient(this, account); mEventsClient = new AndroidEventsClient(account); mVideoClient = new AndroidVideoClient(mVideoClient.IsCaptureSupported(), account); } } /// Returns whether or not user has given permissions for given scopes. /// public bool HasPermissions(string[] scopes) { return mTokenClient.HasPermissions(scopes); } /// /// public ISavedGameClient GetSavedGameClient() { lock (GameServicesLock) { return mSavedGameClient; } } /// /// public IEventsClient GetEventsClient() { lock (GameServicesLock) { return mEventsClient; } } /// /// public IVideoClient GetVideoClient() { lock (GameServicesLock) { return mVideoClient; } } private AndroidJavaObject getAchievementsClient() { return mGamesClass.CallStatic("getAchievementsClient", AndroidHelperFragment.GetActivity(), mTokenClient.GetAccount()); } private AndroidJavaObject getGamesClient() { return mGamesClass.CallStatic("getGamesClient", AndroidHelperFragment.GetActivity(), mTokenClient.GetAccount()); } private AndroidJavaObject getPlayersClient() { return mGamesClass.CallStatic("getPlayersClient", AndroidHelperFragment.GetActivity(), mTokenClient.GetAccount()); } private AndroidJavaObject getLeaderboardsClient() { return mGamesClass.CallStatic("getLeaderboardsClient", AndroidHelperFragment.GetActivity(), mTokenClient.GetAccount()); } private AndroidJavaObject getPlayerStatsClient() { return mGamesClass.CallStatic("getPlayerStatsClient", AndroidHelperFragment.GetActivity(), mTokenClient.GetAccount()); } private AndroidJavaObject getVideosClient() { return mGamesClass.CallStatic("getVideosClient", AndroidHelperFragment.GetActivity(), mTokenClient.GetAccount()); } } } #endif