Setup Google Play Services in LibGDX

This tutorial is based on Android Studio / IntelliJ, but you can follow along with your favourite Java IDE.

0. Requirements

To follow this tutorial, you need:

  • A Google Play Developer account - more info here.
  • A LibGDX project created through gdx-setup up and running.

1. Create your game in Google Play Console

First of all, you need to create a game in Google Play Console.

a) Sign in to the Google Play Console.

You can do so here.

b) Go to ‘Game Services’ and select ‘Add New Game’.

The ‘Game Services’ tab can be found on the left menu. Enter the required information as shown below. Play Services Setup

c) Add an Android Linked App

On the left menu, go to ‘Linked Apps’, select ‘Android’ and fill in the information as shown below. Don’t worry, you can edit this later. Note: make sure the Package Name is the same as the one on your LibGDX project. Play Services Setup

d) Authorize your App

Open a terminal and execute the following commands:

- Windows
cd C:\Users\<USERNAME>\.android\
keytool -export -alias androiddebugkey -keystore debug.keystore -list -v
- macOS / Linux
cd ~/.android
keytool -export -alias androiddebugkey -keystore debug.keystore -list -v

Note: the default password is ‘android’.

This will create an output similar to this: Keytool Example Copy the key just after SHA1.

Back on Google Play Console, hit ‘Authorise your app now’ and paste the key you just copied. PlayService Setup

Note: This key is now your debug key and will be mentioned as so throughout the rest of the tutorial. For more information on why this step is required, go here.

e) Create a Sample Achievement

For testing purposes, we’ll create a sample incremental achievement. You can choose your own achievement to create, just know that you must use the incrementAchievement method for incremental achievements, and the unlockAchievement method otherwise.

In this case, we created an achievement to reward players that played more than 100 times. Create Achievement

f) Add games-ids.xml

After saving your achievement/leaderboard, click on Get resources, and copy the given text. Get resources

Next, save this file as res/values/games-ids.xml in your android module.

2. Project Setup

a) Download missing packages

SDK Manager

On Android Studio, open the SDK Manager as shown above, and install/update the following packages:

  • Google Play Services
  • Google Repository

Update Packages If you have trouble setting this part up, checkout this guide.

b) Download helper libraries

Next, download the android-basic-samples repository. It contains several sample projects to demonstrate how to interact and use PlayServices, check it out or save it for later as it will certainly be useful to you.

For now, all we need is the BaseGameUtils module as it contains a set of helper classes that communicate with Google’s API for you. In Android Studio, go to File > New > Import Module and direct it to the BaseGameUtils folder in BasicSamples/libraries/BaseGameUtils Importing BaseGameUtils Android Studio should handle the setup properly, but make sure you see “BaseGameUtils” in your settings.gradle as such:

include 'desktop', 'android', 'ios', 'html', 'core', "BaseGameUtils"

Next, in your root build.gradle file, add the following android dependencies:

project(":android") {
    ...
    dependencies {
        ...
        compile 'com.google.android.gms:play-services-games:11.0.4'
        // 11.0.4 is the latest as of now, keep it updated
        compile project(':BaseGameUtils')
        ...
    }
}

c) Add dependencies and Permissions

Add the following lines to your AndroidManifest.xml file:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" ... >
    ...
    <application ... >
        ...
        <meta-data android:name="com.google.android.gms.games.APP_ID" android:value="@string/app_id" />
        <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
    </application>
    ...
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest>

Don’t forget to replace ‘APP_ID’ with your own Application ID.

You can find a properly setup manifest here.

Notes: Gradle Synch

After syncrhonizing with Gradle you might get the error below, and as such you’ll need to update the minSdkVersion to the version in the error message (in the android module’s build.gradle).

Error: Execution failed for task ':android:processDebugManifest'.
Manifest merger failed : uses-sdk:minSdkVersion 9 cannot be smaller than version 15 declared in library [libgdx-GPGS:BaseGameUtils:1.0]

3. Implementation

With everything setup, we can finally begin implementing the Google Play Services API. Because it depends on android specific resources, we can’t call the methods directly in our core module, instead we need to create an interface in our core module and implement it in the android module (generally, you should do this for any platform specific code).

a) Create Interface

So, in our core module we’ll create an interface called PlayServices as follows:

public interface PlayServices {
    void signIn();
    void signOut();
    boolean isSignedIn();
    void submitScore(int score);
    void incrementAchievement(String achievementID);
    void unlockAchievement(String achievementID);
    void showScores();
    void showAchievements();

    /* Add getters for your achievements' IDs here */
    String getReallyBoredAchievementId(); // sample getter
}

b) Implement it

Next, we’ll need to implement it in the android module. So, open up the default Activity (libGDX calls it AndroidLauncher by default) and add the following implementations:

public class AndroidLauncher extends AndroidApplication implements PlayServices, GameHelper.GameHelperListener

Declare these two members inside the class:

private GameHelper gameHelper;
private final static int UNUSED_REQUEST_CODE = 1;

Next, inside the overwritten onCreate method, initialize and setup the gameHelper:

gameHelper = new GameHelper(this, GameHelper.CLIENT_GAMES);
gameHelper.setup(this);

If you’re using Android Studio, you should see a red underlining in the class declaration. Right click it, go the Generate -> Implement Methods, and select all the methods from PlayServices and GameHelperListener.

Implementing Methods

If you’re not using Android Studio, you’ll have to declare the methods manually.

Besided the PlayServices’ methods, you’ll also need to override the methods onStart, onStop and onActivityResult.

To do so, implement the methods as follows:

@Override
protected void onStart() {
    super.onStart();
    gameHelper.onStart(this);
}

@Override
protected void onStop() {
    super.onStop();
    gameHelper.onStop();
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    gameHelper.onActivityResult(requestCode, resultCode, data);
}

@Override
public void signIn() {
    try {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                gameHelper.beginUserInitiatedSignIn();
            }
        });
    } catch (Exception e) {
        Gdx.app.log("MainActivity", "Log in failed: " + e.getMessage() + ".");
    }
}

@Override
public void signOut() {
    try {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                gameHelper.signOut();
            }
        });
    } catch (Exception e) {
        Gdx.app.log("MainActivity", "Log out failed: " + e.getMessage() + ".");
    }
}

@Override
public boolean isSignedIn() {
    return gameHelper.isSignedIn();
}

@Override
public void submitScore(int score) {
    if (isSignedIn()) {
        Games.Leaderboards.submitScore(gameHelper.getApiClient(),
        getString(R.string.leaderboard_highscores), score);
    }
    // uses a leaderboard called "highscores", change it to yours
}

// You can choose to receive the increment as an argument, as opposed to always incrementing by 1
@Override
public void incrementAchievement(String achievementID) {
    if (isSignedIn())
        Games.Achievements.increment(gameHelper.getApiClient(), achievementID, 1);
}

@Override
public void unlockAchievement(String achievementID) {
    if (isSignedIn())
        Games.Achievements.unlock(gameHelper.getApiClient(), achievementID);
}

@Override
public void showScores() {
    if (isSignedIn()) {
        startActivityForResult(
        Games.Leaderboards.getLeaderboardIntent(gameHelper.getApiClient(),
        getString(R.string.leaderboard_highscores)), UNUSED_REQUEST_CODE);
    } else if (!gameHelper.isConnecting()) {
        signIn();
    }
}

@Override
public void showAchievements() {
    if (isSignedIn()) {
        startActivityForResult(Games.Achievements.getAchievementsIntent(
        gameHelper.getApiClient()), UNUSED_REQUEST_CODE);
    }
}

// Sample achievement id getter
@Override
public String getReallyBoredAchievementId() {
    return getString(R.string.achievement_really_bored___);
}

@Override
public void onSignInFailed() {}

@Override
public void onSignInSucceeded() {}

Note: the leaderboards and achievements’ IDs can be found in your games-ids.xml

You can find a properly implemented PlayServices here and a properly setup AndroidLauncher here.

To test the achievement we created you can run the following line at the start of the game (for instance, at the end of the overridden onCreate method in AndroidLauncher):

@Override
protected void onCreate (Bundle savedInstanceState) {
    ...
    incrementAchievement(getReallyBoredAchievementId());
}

c) Pass reference to core’s MainClass

With all that setup, we still need to be able to reference this methods from the core module. Change the constructor of core’s main class (by default its called MyGdxGame) to require an implementation of PlayServices:

public class MyGdxGame extends ApplicationAdapter {
    ...
    private final PlayServices playServices;

    public MyGdxGame(PlayServices playServices) {
        this.playServices = playServices;
    }

    public PlayServices getPlayServices() {
        return playServices;
    }
}

Next, change the call to MyGdxApp’s constructor in the AndroidLauncher to pass it a reference to the PlayServices implementation.

protected void onCreate (Bundle savedInstanceState) {
    ...
    initialize(new MyGdxGame(this), config);
}

Now you can submit scores, unlock achievements and everything else from the core module, just pass a reference to MyGdxApp to where you want to use these. You can also make the playServices member variable public static to access it more easily, but with less restraints.

d) Use Null Object

Finally, we’ll use a design pattern called Null Object on the modules that won’t use play services.

In the core module, create a new class called NullPlayServices that implements PlayServices.

public class NullPlayServices implements PlayServices {}

Implement all of the required methods and leave their bodies blank (as seen here).

In the module’s launcher, pass a new object of NullPlayServices to MyGdxGame (you’ll need to import it). Here’s an example for the desktop module:

new LwjglApplication(new MyGdxGame(new NullPlayServices()), config);