Skip to content

Instantly share code, notes, and snippets.

@Krumelur
Created April 2, 2018 17:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Krumelur/842f7aed2becde403fbb77e80322d265 to your computer and use it in GitHub Desktop.
Save Krumelur/842f7aed2becde403fbb77e80322d265 to your computer and use it in GitHub Desktop.
using System;
using Android.Gms.Common;
using Android.Gms.Common.Apis;
using Android.Gms.Games.Achievement;
using Android.Gms.Games.LeaderBoard;
using Android.Gms.Games;
using Android.App;
using Android.Content;
using Android.Views;
using Java.Interop;
using System.Collections.Generic;
namespace GooglePlay.Services.Helpers
{
/// <summary>
/// Basic wrapper for interfacing with the GooglePlayServices Game API's
/// </summary>
public class GameHelper: Java.Lang.Object, GoogleApiClient.IConnectionCallbacks, GoogleApiClient.IOnConnectionFailedListener
{
GoogleApiClient client;
Activity activity;
bool signedOut = true;
bool signingin = false;
bool resolving = false;
List<IAchievement> achievments = new List<IAchievement>();
Dictionary<string, List<ILeaderboardScore>> scores = new Dictionary<string, List<ILeaderboardScore>> ();
AchievementsCallback achievmentsCallback;
LeaderBoardsCallback leaderboardsCallback;
const int REQUEST_LEADERBOARD = 9002;
const int REQUEST_ALL_LEADERBOARDS = 9003;
const int REQUEST_ACHIEVEMENTS = 9004;
const int RC_RESOLVE = 9001;
/// <summary>
/// Gets or sets a value indicating whether the user is signed out or not.
/// </summary>
/// <value><c>true</c> if signed out; otherwise, <c>false</c>.</value>
public bool SignedOut {
get { return signedOut; }
set {
if (signedOut != value) {
signedOut = value;
// Store if we Signed Out so we don't bug the player next time.
using (var settings = this.activity.GetSharedPreferences ("googleplayservicessettings", FileCreationMode.Private)) {
using (var e = settings.Edit ()) {
e.PutBoolean ("SignedOut", signedOut);
e.Commit ();
}
}
}
}
}
/// <summary>
/// Gets or sets the gravity for the GooglePlay Popups.
/// Defaults to Bottom|Center
/// </summary>
/// <value>The gravity for popups.</value>
public GravityFlags GravityForPopups { get; set; }
/// <summary>
/// The View on which the Popups should show
/// </summary>
/// <value>The view for popups.</value>
public View ViewForPopups {get;set;}
/// <summary>
/// This event is fired when a user successfully signs in
/// </summary>
public event EventHandler OnSignedIn;
/// <summary>
/// This event is fired when the Sign in fails for any reason
/// </summary>
public event EventHandler OnSignInFailed;
/// <summary>
/// This event is fired when the user Signs out
/// </summary>
public event EventHandler OnSignedOut;
/// <summary>
/// List of Achievements. Populated by LoadAchievements
/// </summary>
/// <value>The achievements.</value>
public List<IAchievement> Achievements {
get { return achievments; }
}
public GameHelper (Activity activity)
{
this.activity = activity;
this.GravityForPopups = GravityFlags.Bottom | GravityFlags.Center;
achievmentsCallback = new AchievementsCallback (this);
leaderboardsCallback = new LeaderBoardsCallback (this);
}
public void Initialize() {
var settings = this.activity.GetSharedPreferences ("googleplayservicessettings", FileCreationMode.Private);
signedOut = settings.GetBoolean ("SignedOut", true);
if (!signedOut)
CreateClient ();
}
private void CreateClient() {
// did we log in with a player id already? If so we don't want to ask which account to use
var settings = this.activity.GetSharedPreferences ("googleplayservicessettings", FileCreationMode.Private);
var id = settings.GetString ("playerid", String.Empty);
var builder = new GoogleApiClient.Builder (activity, this, this);
builder.AddApi (Android.Gms.Games.GamesClass.Api);
builder.AddScope (Android.Gms.Games.GamesClass.ScopeGames);
builder.SetGravityForPopups ((int)GravityForPopups);
if (ViewForPopups != null)
builder.SetViewForPopups (ViewForPopups);
if (!string.IsNullOrEmpty (id)) {
builder.SetAccountName (id);
}
client = builder.Build ();
}
/// <summary>
/// Start the GooglePlayClient. This should be called from your Activity Start
/// </summary>
public void Start() {
if(SignedOut && !signingin)
return;
if (client != null && !client.IsConnected) {
client.Connect ();
}
}
/// <summary>
/// Disconnects from the GooglePlayClient. This should be called from your Activity Stop
/// </summary>
public void Stop() {
if (client != null && client.IsConnected) {
client.Disconnect ();
}
}
/// <summary>
/// Reconnect to google play.
/// </summary>
public void Reconnect() {
if (client != null)
client.Reconnect ();
}
/// <summary>
/// Sign out of Google Play and make sure we don't try to auto sign in on the next startup
/// </summary>
public void SignOut() {
SignedOut = true;
if (client.IsConnected) {
GamesClass.SignOut (client);
Stop ();
using (var settings = this.activity.GetSharedPreferences ("googleplayservicessettings", FileCreationMode.Private)) {
using (var e = settings.Edit ()) {
e.PutString ("playerid",String.Empty);
e.Commit ();
}
}
client.Dispose ();
client = null;
if (OnSignedOut != null)
OnSignedOut (this, EventArgs.Empty);
}
}
/// <summary>
/// Attempt to Sign in to Google Play
/// </summary>
public void SignIn() {
signingin = true;
if (client == null)
CreateClient ();
if (client.IsConnected)
return;
if (client.IsConnecting)
return;
var result = GooglePlayServicesUtil.IsGooglePlayServicesAvailable (activity);
if (result != ConnectionResult.Success) {
return;
}
Start ();
}
/// <summary>
/// Unlocks the achievement.
/// </summary>
/// <param name="achievementCode">Achievement code from you applications Google Play Game Services Achievements Page</param>
public void UnlockAchievement(string achievementCode) {
GamesClass.Achievements.Unlock (client, achievementCode);
}
public void IncrementAchievement(string achievementCode, int progress) {
GamesClass.Achievements.Increment (client, achievementCode, progress);
}
/// <summary>
/// Show the built in google Achievements Activity. This will cause your application to go into a Paused State
/// </summary>
public void ShowAchievements() {
var intent = GamesClass.Achievements.GetAchievementsIntent (client);
activity.StartActivityForResult (intent, REQUEST_ACHIEVEMENTS);
}
/// <summary>
/// Submit a score to google play. The score will only be updated if it is greater than the existing score.
/// This is not immediate but will occur at the next sync of the google play client.
/// </summary>
/// <param name="leaderboardCode">Leaderboard code from you applications Google Play Game Services Leaderboards Page</param>
/// <param name="value">The value of the score</param>
public void SubmitScore(string leaderboardCode, long value) {
GamesClass.Leaderboards.SubmitScore (client, leaderboardCode, value);
}
/// <summary>
/// Submit a score to google play. The score will only be updated if it is greater than the existing score.
/// This is not immediate but will occur at the next sync of the google play client.
/// </summary>
/// <param name="leaderboardCode">Leaderboard code from you applications Google Play Game Services Leaderboards Page</param>
/// <param name="value">The value of the score</param>
/// <param name="value">Additional MetaData to attach. Must be a URI safe string with a max length of 64 characters</param>
public void SubmitScore(string leaderboardCode, long value, string metadata) {
GamesClass.Leaderboards.SubmitScore (client, leaderboardCode, value, metadata);
}
/// <summary>
/// Show the built in leaderboard activity for the leaderboard code.
/// </summary>
/// <param name="leaderboardCode">Leaderboard code from you applications Google Play Game Services Leaderboards Page</param>
public void ShowLeaderBoardIntentForLeaderboard(string leaderboardCode) {
var intent = GamesClass.Leaderboards.GetLeaderboardIntent (client, leaderboardCode);
activity.StartActivityForResult (intent, REQUEST_LEADERBOARD);
}
/// <summary>
/// Show the built in leaderboard activity for all the leaderboards setup for your application
/// </summary>
public void ShowAllLeaderBoardsIntent() {
var intent = GamesClass.Leaderboards.GetAllLeaderboardsIntent (client);
activity.StartActivityForResult (intent, REQUEST_ALL_LEADERBOARDS);
}
/// <summary>
/// Load the Achievments. This populates the Achievements property
/// </summary>
public void LoadAchievements() {
var pendingResult = GamesClass.Achievements.Load (client, false);
pendingResult.SetResultCallback (achievmentsCallback);
}
public void LoadTopScores(string leaderboardCode) {
var pendingResult = GamesClass.Leaderboards.LoadTopScores (client, leaderboardCode, 2, 0, 25);
pendingResult.SetResultCallback (leaderboardsCallback);
}
#region IGoogleApiClientConnectionCallbacks implementation
public void OnConnected (Android.OS.Bundle connectionHint)
{
resolving = false;
SignedOut = false;
signingin = false;
using (var settings = this.activity.GetSharedPreferences ("googleplayservicessettings", FileCreationMode.Private)) {
using (var e = settings.Edit ()) {
e.PutString ("playerid",GamesClass.GetCurrentAccountName(client));
e.Commit ();
}
}
if (OnSignedIn != null)
OnSignedIn (this, EventArgs.Empty);
}
public void OnConnectionSuspended (int resultCode)
{
resolving = false;
SignedOut = false;
signingin = false;
client.Disconnect ();
if (OnSignInFailed != null)
OnSignInFailed (this, EventArgs.Empty);
}
public void OnConnectionFailed (ConnectionResult result)
{
if (resolving)
return;
if (result.HasResolution) {
resolving = true;
result.StartResolutionForResult (activity, RC_RESOLVE);
return;
}
resolving = false;
SignedOut = false;
signingin = false;
if (OnSignInFailed != null)
OnSignInFailed (this, EventArgs.Empty);
}
#endregion
/// <summary>
/// Processes the Activity Results from the Signin process. MUST be called from your activity OnActivityResult override.
/// </summary>
/// <param name="requestCode">Request code.</param>
/// <param name="resultCode">Result code.</param>
/// <param name="data">Data.</param>
public void OnActivityResult (int requestCode, Result resultCode, Intent data) {
if (requestCode == RC_RESOLVE) {
if (resultCode == Result.Ok) {
Start ();
} else {
if (OnSignInFailed != null)
OnSignInFailed (this, EventArgs.Empty);
}
}
}
internal class AchievementsCallback : Java.Lang.Object, IResultCallback {
GameHelper helper;
public AchievementsCallback (GameHelper helper): base()
{
this.helper = helper;
}
#region IResultCallback implementation
public void OnResult (Java.Lang.Object result)
{
var ar = result.JavaCast<IAchievementsLoadAchievementsResult>();
if (ar != null) {
helper.achievments.Clear ();
var count = ar.Achievements.Count;
for (int i = 0; i < count; i++) {
var item = ar.Achievements.Get (i);
var a = item.JavaCast<IAchievement> ();
helper.achievments.Add (a);
}
}
}
#endregion
}
internal class LeaderBoardsCallback : Java.Lang.Object, IResultCallback {
GameHelper helper;
public LeaderBoardsCallback (GameHelper helper): base()
{
this.helper = helper;
}
#region IResultCallback implementation
public void OnResult (Java.Lang.Object result)
{
var ar = result.JavaCast<ILeaderboardsLoadScoresResult>();
if (ar != null) {
var id = ar.Leaderboard.LeaderboardId;
if (!helper.scores.ContainsKey (id)) {
helper.scores.Add (id, new List<ILeaderboardScore> ());
}
helper.scores [id].Clear ();
var count = ar.Scores.Count;
for (int i = 0; i < count; i++) {
var score = ar.Scores.Get(i).JavaCast<ILeaderboardScore> ();
helper.scores [id].Add (score);
}
}
}
#endregion
}
}
}
using System;
using System.Collections.Generic;
using TowerOffense.Resources;
#if __IOS__ || __TVOS__
using UIKit;
using Foundation;
using GameKit;
#endif
#if __ANDROID__
using TowerOffense.Droid;
#endif
namespace TowerOffense
{
public static class GameServices
{
public enum ACHIEVEMENTS
{
UnlockDarkKnight,
UnlockSkeleton,
EpicFail,
FiveInARow,
TenInARow,
BatMan,
LifeSaver,
SlimeEvader
}
public static void AuthenticateGameService ()
{
#if __IOS__ || __TVOS__
var localPlayer = GKLocalPlayer.LocalPlayer;
if (localPlayer.Authenticated)
{
Util.Log ("Player already authenticated.");
return;
}
// If we have a game center authentication controller, show it.
if (localPlayer.AuthenticateHandler != null)
{
Util.Log ("GameCenter authentication handler already set.");
return;
}
// Assign an authentication handler.
localPlayer.AuthenticateHandler = AppleGameCenterAuthenticationHandler;
#elif __FIRETV__
// TODO: FireTV
#elif __ANDROID__
try
{
MainActivity.Instance.GameHelper.SignIn ();
}
catch(Exception ex)
{
Util.Log("Failed to sign in: {0}", ex);
Tracking.Report(ex, true);
}
#endif
}
public static void SignOut ()
{
#if __IOS__ || __TVOS__
// NOP
#elif __FIRETV__
// TODO: FireTV
#elif __ANDROID__
try
{
MainActivity.Instance.GameHelper.SignOut();
}
catch(Exception ex)
{
Util.Log("Failed to sign out: {0}", ex);
Tracking.Report(ex, true);
}
#endif
}
#if __IOS__ || __TVOS__
private async static void AppleGameCenterAuthenticationHandler (UIViewController controller, NSError error)
{
if (error != null)
{
Util.Log ("Error authenticating GameCenter: {0}", error);
Tracking.Track ("Error authenticating GameCenter", new Dictionary<string, string> {
{ "Error", error.ToString () }
});
}
if (controller != null)
{
Util.Log ("Got game center view controller.");
try
{
UIApplication.SharedApplication.Windows [0].RootViewController.PresentViewController (controller, true, null);
}
catch (Exception ex)
{
Util.Log ("Presenting GameCenter controller failed: {0}", ex);
Tracking.Report (ex, true);
}
}
else
{
if (GKLocalPlayer.LocalPlayer.Authenticated)
{
Util.Log ("Player is authenticated in Game Center");
try
{
await GKLocalPlayer.LocalPlayer.SetDefaultLeaderboardIdentifierAsync ("net.csharx.sirlanceandhop.highscores");
}
catch (Exception ex)
{
Util.Log ("Setting default leaderbord ID failed: {0}", ex);
Tracking.Report (ex, true);
}
}
else
{
Util.Log ("Player is not authenticated in Game Center");
}
}
}
#endif
public static void ReportAchievement (ACHIEVEMENTS achievement)
{
#if __IOS__ || __TVOS__
string achievementId = "net.csharx.sirlanceandhop." + achievement.ToString ().ToLower ();
try
{
Util.Log ("Reporting achievement {0}", achievementId);
var reportAchievement = new GKAchievement (achievementId) {
ShowsCompletionBanner = false,
PercentComplete = 100f
};
GKAchievement.ReportAchievements (new GKAchievement[] { reportAchievement }, error => {
if (error != null)
{
Util.Log ("Submitting achievement failed with error: {0}", error);
Tracking.Track ("Reporting achievement failed", new Dictionary<string, string> () {
{ "Error", error.ToString () },
});
}
});
}
catch (Exception ex)
{
Util.Log ("Submitting achievement failed: {0}", ex);
Tracking.Report (ex, new Dictionary<string, string> () {
{ "Achievement", achievement.ToString () },
{ "ID", achievementId },
}, true);
}
#elif __FIRETV__
// TODO: FireTV
#elif __ANDROID__
int achievementId = -1;
string achievementCode = null;
switch(achievement)
{
case ACHIEVEMENTS.BatMan:
achievementId = TowerOffense.Droid.Resource.String.achievement_batman;
break;
case ACHIEVEMENTS.EpicFail:
achievementId = TowerOffense.Droid.Resource.String.achievement_epic_fail;
break;
case ACHIEVEMENTS.LifeSaver:
achievementId = TowerOffense.Droid.Resource.String.achievement_lifesaver;
break;
case ACHIEVEMENTS.SlimeEvader:
achievementId = TowerOffense.Droid.Resource.String.achievement_slime_evader;
break;
case ACHIEVEMENTS.TenInARow:
achievementId = TowerOffense.Droid.Resource.String.achievement_ten_in_a_row;
break;
case ACHIEVEMENTS.UnlockDarkKnight:
achievementId = TowerOffense.Droid.Resource.String.achievement_unlock_first_bonus_character;
break;
case ACHIEVEMENTS.UnlockSkeleton:
achievementId = TowerOffense.Droid.Resource.String.achievement_unlock_second_bonus_character;
break;
default:
Util.Log("Achievement '{0}' not found.", achievement);
return;
}
try
{
achievementCode = MainActivity.Instance.GetString(achievementId);
MainActivity.Instance.GameHelper.UnlockAchievement(achievementCode);
}
catch(Exception ex)
{
Util.Log("Unlocking achievement '{0}' failed (achievement ID: '{1}'): {2}", achievement, achievementCode, ex);
Tracking.Report(ex, new Dictionary<string, string>() {
{"Achievement", achievement.ToString()},
{"ID", achievementCode.ToString()}
}, true);
}
#endif
}
public static void ReportGameServiceScore (int score, int level)
{
#if __IOS__ || __TVOS__
var gkScorePoints = new GKScore ("net.csharx.sirlanceandhop.highscores") {
Value = score
};
var gkScoreLevel = new GKScore ("net.csharx.sirlanceandhop.level") {
Value = level
};
try
{
GKScore.ReportScores (new GKScore[] { gkScorePoints, gkScoreLevel }, error => {
if (error != null)
{
Util.Log ("Submitting scores failed with error: {0}", error);
Tracking.Track ("Reporting score failed", new Dictionary<string, string> () {
{ "Error", error.ToString () },
});
}
});
}
catch (Exception ex)
{
Util.Log ("Submitting scores failed: {0}", ex);
Tracking.Report (ex, new Dictionary<string, string> () {
{ "Score", score.ToString () },
{ "Level", level.ToString () }
}, true);
}
#elif __FIRETV__
// TODO: FireTV
#elif __ANDROID__
string scoreCode = MainActivity.Instance.GetString(TowerOffense.Droid.Resource.String.leaderboard_score);
try
{
MainActivity.Instance.GameHelper.SubmitScore (scoreCode, score);
}
catch(Exception ex)
{
Util.Log("Submitting scores failed: {0}", ex);
Tracking.Report(ex, new Dictionary<string, string>() {
{"Score", score.ToString()},
{"Level", level.ToString()},
{"ID", scoreCode }
}, true);
}
string levelCode = MainActivity.Instance.GetString(TowerOffense.Droid.Resource.String.leaderboard_level);
try
{
MainActivity.Instance.GameHelper.SubmitScore (levelCode, level);
}
catch(Exception ex)
{
Util.Log("Submitting level failed: {0}", ex);
Tracking.Report(ex, new Dictionary<string, string>() {
{"Score", score.ToString()},
{"Level", level.ToString()},
{"ID", levelCode }
}, true);
}
#endif
}
public static void ShowLeaderboards ()
{
#if __IOS__ || __TVOS__
// If game center cannot be used, we cannot show records.
if (!GKLocalPlayer.LocalPlayer.Authenticated)
{
UI.ShowMessageBox (Strings.GameCenterLoginFailed, Strings.OK);
return;
}
var leaderboardController = new GKGameCenterViewController ();
// Unavailable on tvOS
#if __IOS__
leaderboardController.ViewState = GKGameCenterViewControllerState.Default;
leaderboardController.LeaderboardIdentifier = "net.csharx.sirlanceandhop.highscores";
#endif
leaderboardController.Finished += (sender, e) => leaderboardController.DismissViewController (true, null);
UIApplication.SharedApplication.Windows [0].RootViewController.PresentViewController (leaderboardController, true, null);
#elif __FIRETV__
// TODO: FireTV
#elif __ANDROID__
try
{
if(MainActivity.Instance.GameHelper.SignedOut)
{
UI.ShowMessageBox(Strings.GooglePlayGamesRequired, Strings.OK);
return;
}
MainActivity.Instance.GameHelper.ShowAllLeaderBoardsIntent ();
}
catch(Exception ex)
{
Util.Log("Showing leaderboards failed: {0}", ex);
Tracking.Report(ex, true);
}
#endif
}
public static void ShowAchievements ()
{
#if __IOS__ || __TVOS__
// Not required on iOS. Achievements can be reached from the records screen.
#elif __FIRETV__
// TODO: FireTV
#elif __ANDROID__
try
{
if(MainActivity.Instance.GameHelper.SignedOut)
{
UI.ShowMessageBox(Strings.GooglePlayGamesRequired, Strings.OK);
return;
}
MainActivity.Instance.GameHelper.ShowAchievements();
}
catch(Exception ex)
{
Util.Log("Showing achievements failed: {0}", ex);
Tracking.Report(ex, true);
}
#endif
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment