Scroll to top

Google Play Game Services provide the opportunity to add social features to your games through users' Google+ accounts. In this tutorial, we will demonstrate how you can add leaderboards to an Android app, submitting user scores, and presenting the current leaderboard standings within the game.

Using leaderboards involves preparing your IDE, configuring the leaderboard in the Google Play Developer Console, and adding functionality to your app.

If you completed the recent tutorial on adding achievements to Android apps, you will be able to skip some of the steps in this one. The attached source code includes the same app we used for the achievements tutorial, with both achievements and leaderboards functionality added.

1. Prepare Your IDE

Step 1

To use Google Play Services, you need certain utilities installed in your development environment. In addition, since we are using Game Services, we will install the BaseGameUtils library, which reduces the amount of coding we need to implement features such as Google+ sign-in.

To get started, create a new app or use an existing one. If you followed the achievements tutorial, you can use the app you built for that tutorial. If you are creating your own game, decide what you want to use leaderboards for and when you plan on submitting a user score. Each leaderboard score will be a number. You can configure the leaderboard to regard lower or higher number values as better in terms of position in the leaderboard, but naturally this will depend on the purpose of your game.

The code in the download folder includes a simple game in which the user guesses a number. We will use the number of guesses required to get the correct answer as the leaderboard score. In this case, fewer guesses are better, so the leaderboard will present the lowest scores first. For simplicity, we will limit the number of guesses a user can take. This is a trivial example to demonstrate the leaderboard concept and functionality. Your own games will likely involve more complexity.

Step 2

Let's get Eclipse ready for developing with Google Play Game Services. Open the Android SDK Manager and scroll to the Extras folder. Expand the folder and select Google Play Services plus the Google Repository. Install the Google APIs Platform from one of the recent Android versions as well if you want to test on the emulator. Install the chosen packages.

Step 3

Eclipse will also need to reference some additional resources in the workspace. On your computer, navigate to the location of the Google Play Services Library, which should be in the Android SDK folder, at extras/google/google_play_services/libproject/google-play-services_lib/. Copy and paste the library somewhere else on your computer.

We now need a reference to this copy in Eclipse. Choose Import > Android > Import Existing Android Code into Workspace from the File menu. Select the location of the copy you made. The imported library should now show up as a new project in Eclipse. Right-click it and choose Properties. In the Android section, choose a Google APIs build target and check the Is Library checkbox.

Step 4

Importing the BaseGameUtils resource is slightly different. The library is hosted on GitHub. You can find it in the Downloads section, under Sample Games. Download the library and save it to your computer.

As you did for the Google Play Services library, choose Import > Android > Import Existing Android Code into Workspace from the File menu to bring the BaseGameUtils library into Eclipse. Right-click to navigate to the new project properties and make sure the project is marked as a library by checking Is Library.

Step 5

We can now make the app refer to these two resources within the workspace. Right-click your app in the Package Explorer and choose Properties. Navigate to the Android section and select Add in the Library section. Choose both the Google Play Services library and BaseGameUtils, and add them to your app.

2. Prepare Your Game in the Developer Console

Step 1

Before you can create a leaderboard, the app needs to be listed in the Google Play Developer Console. Log in and click the Game Services button to the left. If you already did this for your app in the achievements tutorial, you do not need to do it again. You can jump to section 3 on creating a leaderboard.

Click Set up Google Play game services.

Click to add a new game, select I don't use any Google APIs in my game yet, and choose a name and category for your game. Click Continue to go to the next step.

Add your game's title. You can add other details later.

Step 2

Let's now link the app so that we can refer to this Developer Console listing in the app itself. Click the Linked Apps entry in the list on the left and choose Android.

Enter your app info including the package name, making sure it's the same as the one you are using in your project.

Save and click Authorize your app now. For the moment, you can just add the app name, but you can enter more details later. Choose Installed Application in the Client ID area, with Android as the type and enter your package name. You now need to use the keytool utility to generate a signing certificate. You can use the following command in a terminal or command prompt in combination with the debug certificate:

1
keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore -list -v

The terminal or command prompt will write out the fingerprint for the certificate. Copy what you see after SHA1 and paste it into the Developer Console in the Signing Certificate Fingerprint text area.

Select Create Client and copy the ID for the application, which is listed next to the app name in the Developer Console. You will be adding the ID to your app along with the ID for the leaderboard we are about to create.

3. Create a Leaderboard

Step 1

Still in the Developer Console, let's now create a new leaderboard. Select the Leaderboards section in your app listing and click Add leaderboard.

Make sure you understand the concept of Leaderboards on Android—and in Google Play Game Services generally. You can read an overview on the Google Play Game Services website. You can actually do a lot of different things with leaderboards, so consider what we do in this tutorial just a starting point.

Enter the details for your new leaderboard. For our sample code, we use the name Least Guesses and select Smaller is Better in the Ordering section.

Add an icon if you like. If you don't a standard image will be used. Save your new leaderboard and copy its ID.

Step 2

In the Testing section for your app in the Developer Console, you can add accounts that will be granted access to test the game. By default, you will see your own Google account email listed there, so you should be able to use it for testing your app.

4. Prepare Your Game for Accessing Games Services

Step 1

It's time to get the app ready for leaderboard access in Eclipse. If you completed the achievements tutorial you can skip some of this section. Let's first add the IDs for the app and the leaderboard. Open or create a res/values/ids.xml resource file. Use the following syntax to enter the IDs you copied for the app and the new leaderboard when you created them in the Developer Console:

1
<string name="app_id">1234567890</string>
2
<string name="number_guesses_leaderboard">abcdefg1234567</string>

Save the file and open the project's Manifest. Add the following inside the application element:

1
<meta-data
2
 android:name="com.google.android.gms.games.APP_ID"
3
 android:value="@string/app_id" />
4
<meta-data
5
 android:name="com.google.android.gms.version"
6
 android:value="@integer/google_play_services_version" />

The app is now set up to link to the listings we added in the Developer Console.

Step 2

When you utilize Google Services in your Android apps, you need your users to sign into their Google accounts. You can take a number of approaches to implement this, but we are going to automate parts of this process by using the BaseGameActivity class together with standard buttons for signing in and out. Additionally, when the activity starts, the app will attempt to log the user in straight away.

Open your app's main layout file and add buttons for sign in/out:

1
<!-- sign-in button -->
2
3
<com.google.android.gms.common.SignInButton
4
 android:id="@+id/sign_in_button"
5
 android:layout_width="wrap_content"
6
 android:layout_height="wrap_content" />
7
8
<!-- sign-out button -->
9
10
<Button
11
 android:id="@+id/sign_out_button"
12
 android:layout_width="wrap_content"
13
 android:layout_height="wrap_content"
14
 android:text="Sign Out"
15
 android:visibility="gone" />

Step 3

Add these imports to your main Activity class:

1
import com.google.android.gms.games.Games;
2
import com.google.example.games.basegameutils.BaseGameActivity;

Make the Activity class extend the BaseGameActivity class and listen for clicks:

1
public class MainActivity extends BaseGameActivity implements View.OnClickListener

Prepare to respond to clicks on the sign in/out buttons in onCreate:

1
findViewById(R.id.sign_in_button).setOnClickListener(this);
2
findViewById(R.id.sign_out_button).setOnClickListener(this); 

Now add the following standard methods to an onClick method in the class:

1
@Override
2
public void onClick(View view) {
3
    if (view.getId() == R.id.sign_in_button) {
4
		beginUserInitiatedSignIn();
5
	}
6
	else if (view.getId() == R.id.sign_out_button) {
7
		signOut();
8
		findViewById(R.id.sign_in_button).setVisibility(View.VISIBLE);
9
		findViewById(R.id.sign_out_button).setVisibility(View.GONE);
10
	}
11
}

The methods we call here are provided by the BaseGameActivity class our Activity class is inheriting from, so we don't need to handle the details manually. Finally, we add a couple of standard callbacks:

1
public void onSignInSucceeded() {
2
    findViewById(R.id.sign_in_button).setVisibility(View.GONE);
3
	findViewById(R.id.sign_out_button).setVisibility(View.VISIBLE);
4
}
5
6
@Override
7
public void onSignInFailed() {
8
	findViewById(R.id.sign_in_button).setVisibility(View.VISIBLE);
9
	findViewById(R.id.sign_out_button).setVisibility(View.GONE);
10
}

When we call on the leaderboard functionality, we will first check that the app has a connection to Google Services. You could alternatively add code to these methods to manage your app's awareness of whether or not Play Services can be called on.

5. Implement Your Leaderboard

Step 1

Now we can let the app use the leaderboard. The code in the sample app uses the following layout. I won't go into detail explaining the layout as your own apps will have a different layout.

1
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
2
    xmlns:tools="http://schemas.android.com/tools"
3
	android:layout_width="match_parent"
4
	android:layout_height="match_parent"
5
	android:paddingBottom="@dimen/activity_vertical_margin"
6
	android:paddingLeft="@dimen/activity_horizontal_margin"
7
	android:paddingRight="@dimen/activity_horizontal_margin"
8
	android:paddingTop="@dimen/activity_vertical_margin"
9
	tools:context="com.example.fungame.MainActivity" >
10
11
	<LinearLayout
12
		android:id="@+id/sign_in_buttons"
13
		android:layout_width="fill_parent"
14
		android:layout_height="wrap_content"
15
		android:layout_alignParentTop="true"
16
		android:orientation="horizontal" >
17
18
		<!-- sign-in button -->
19
20
		<com.google.android.gms.common.SignInButton
21
			android:id="@+id/sign_in_button"
22
			android:layout_width="wrap_content"
23
			android:layout_height="wrap_content" />
24
25
		<!-- sign-out button -->
26
27
		<Button
28
			android:id="@+id/sign_out_button"
29
			android:layout_width="wrap_content"
30
			android:layout_height="wrap_content"
31
			android:text="Sign Out"
32
			android:visibility="gone" />
33
	</LinearLayout>
34
35
	<RelativeLayout
36
		android:id="@+id/gameArea"
37
		android:layout_width="fill_parent"
38
		android:layout_height="wrap_content"
39
		android:layout_below="@id/sign_in_buttons" >
40
41
		<TextView
42
			android:id="@+id/guess_text"
43
			android:layout_width="fill_parent"
44
			android:layout_height="wrap_content"
45
			android:layout_centerHorizontal="true"
46
			android:text="Guess the number!"
47
			android:textSize="30sp"
48
			android:textStyle="bold" />
49
50
		<LinearLayout
51
			android:layout_width="fill_parent"
52
			android:layout_height="wrap_content"
53
			android:layout_below="@id/guess_text"
54
			android:orientation="vertical" >
55
56
			<LinearLayout
57
				android:layout_width="fill_parent"
58
				android:layout_height="wrap_content"
59
				android:orientation="horizontal" >
60
61
				<Button
62
					android:id="@+id/btn7"
63
					android:layout_width="0dp"
64
					android:layout_height="match_parent"
65
					android:layout_margin="1dp"
66
					android:layout_weight="1"
67
					android:background="#ff000033"
68
					android:gravity="center"
69
					android:onClick="btnPressed"
70
					android:padding="5dp"
71
					android:tag="7"
72
					android:text="7"
73
					android:textColor="#ffffffff"
74
					android:textSize="30sp"
75
					android:textStyle="bold" />
76
77
				<Button
78
					android:id="@+id/btn8"
79
					android:layout_width="0dp"
80
					android:layout_height="match_parent"
81
					android:layout_margin="1dp"
82
					android:layout_weight="1"
83
					android:background="#ff000033"
84
					android:gravity="center"
85
					android:onClick="btnPressed"
86
					android:padding="5dp"
87
					android:tag="8"
88
					android:text="8"
89
					android:textColor="#ffffffff"
90
					android:textSize="30sp"
91
					android:textStyle="bold" />
92
93
				<Button
94
					android:id="@+id/btn9"
95
					android:layout_width="0dp"
96
					android:layout_height="match_parent"
97
					android:layout_margin="1dp"
98
					android:layout_weight="1"
99
					android:background="#ff000033"
100
					android:gravity="center"
101
					android:onClick="btnPressed"
102
					android:padding="5dp"
103
					android:tag="9"
104
					android:text="9"
105
					android:textColor="#ffffffff"
106
					android:textSize="30sp"
107
					android:textStyle="bold" />
108
			</LinearLayout>
109
110
			<LinearLayout
111
				android:layout_width="fill_parent"
112
				android:layout_height="wrap_content"
113
				android:layout_weight="1"
114
				android:orientation="horizontal" >
115
116
				<Button
117
					android:id="@+id/btn4"
118
					android:layout_width="0dp"
119
					android:layout_height="match_parent"
120
					android:layout_margin="1dp"
121
					android:layout_weight="1"
122
					android:background="#ff000033"
123
					android:gravity="center"
124
					android:onClick="btnPressed"
125
					android:padding="5dp"
126
					android:tag="4"
127
					android:text="4"
128
					android:textColor="#ffffffff"
129
					android:textSize="30sp"
130
					android:textStyle="bold" />
131
132
				<Button
133
					android:id="@+id/btn5"
134
					android:layout_width="0dp"
135
					android:layout_height="match_parent"
136
					android:layout_margin="1dp"
137
					android:layout_weight="1"
138
					android:background="#ff000033"
139
					android:gravity="center"
140
					android:onClick="btnPressed"
141
					android:padding="5dp"
142
					android:tag="5"
143
					android:text="5"
144
					android:textColor="#ffffffff"
145
					android:textSize="30sp"
146
					android:textStyle="bold" />
147
148
				<Button
149
					android:id="@+id/btn6"
150
					android:layout_width="0dp"
151
					android:layout_height="match_parent"
152
					android:layout_margin="1dp"
153
					android:layout_weight="1"
154
					android:background="#ff000033"
155
					android:gravity="center"
156
					android:onClick="btnPressed"
157
					android:padding="5dp"
158
					android:tag="6"
159
					android:text="6"
160
					android:textColor="#ffffffff"
161
					android:textSize="30sp"
162
					android:textStyle="bold" />
163
			</LinearLayout>
164
165
			<LinearLayout
166
				android:layout_width="fill_parent"
167
				android:layout_height="wrap_content"
168
				android:layout_weight="1"
169
				android:orientation="horizontal" >
170
171
				<Button
172
					android:id="@+id/btn1"
173
					android:layout_width="0dp"
174
					android:layout_height="match_parent"
175
					android:layout_margin="1dp"
176
					android:layout_weight="1"
177
					android:background="#ff000033"
178
					android:gravity="center"
179
					android:onClick="btnPressed"
180
					android:padding="5dp"
181
					android:tag="1"
182
					android:text="1"
183
					android:textColor="#ffffffff"
184
					android:textSize="30sp"
185
					android:textStyle="bold" />
186
187
				<Button
188
					android:id="@+id/btn2"
189
					android:layout_width="0dp"
190
					android:layout_height="match_parent"
191
					android:layout_margin="1dp"
192
					android:layout_weight="1"
193
					android:background="#ff000033"
194
					android:gravity="center"
195
					android:onClick="btnPressed"
196
					android:padding="5dp"
197
					android:tag="2"
198
					android:text="2"
199
					android:textColor="#ffffffff"
200
					android:textSize="30sp"
201
					android:textStyle="bold" />
202
203
				<Button
204
					android:id="@+id/btn3"
205
					android:layout_width="0dp"
206
					android:layout_height="match_parent"
207
					android:layout_margin="1dp"
208
					android:layout_weight="1"
209
					android:background="#ff000033"
210
					android:gravity="center"
211
					android:onClick="btnPressed"
212
					android:padding="5dp"
213
					android:tag="3"
214
					android:text="3"
215
					android:textColor="#ffffffff"
216
					android:textSize="30sp"
217
					android:textStyle="bold" />
218
			</LinearLayout>
219
220
			<LinearLayout
221
				android:layout_width="fill_parent"
222
				android:layout_height="wrap_content"
223
				android:layout_weight="1"
224
				android:orientation="horizontal" >
225
226
				<Button
227
					android:id="@+id/btn0"
228
					android:layout_width="0dp"
229
					android:layout_height="match_parent"
230
					android:layout_margin="1dp"
231
					android:layout_weight="1"
232
					android:background="#ff000033"
233
					android:gravity="center"
234
					android:onClick="btnPressed"
235
					android:padding="5dp"
236
					android:tag="0"
237
					android:text="0"
238
					android:textColor="#ffffffff"
239
					android:textSize="30sp"
240
					android:textStyle="bold" />
241
242
				<Button
243
					android:id="@+id/btnAgain"
244
					android:layout_width="0dp"
245
					android:layout_height="match_parent"
246
					android:layout_margin="1dp"
247
					android:layout_weight="1"
248
					android:background="#ffffff00"
249
					android:enabled="false"
250
					android:gravity="center"
251
					android:onClick="btnPressed"
252
					android:padding="5dp"
253
					android:tag="-1"
254
					android:text="Again"
255
					android:textColor="#ffffff00"
256
					android:textSize="30sp"
257
					android:textStyle="bold" />
258
			</LinearLayout>
259
		</LinearLayout>
260
	</RelativeLayout>
261
262
	<LinearLayout
263
		android:id="@+id/play_buttons"
264
		android:layout_width="fill_parent"
265
		android:layout_height="wrap_content"
266
		android:layout_below="@id/gameArea"
267
		android:gravity="center"
268
		android:orientation="horizontal" >
269
270
		<!-- show achievements -->
271
272
		<Button
273
			android:id="@+id/show_achievements"
274
			android:layout_width="wrap_content"
275
			android:layout_height="wrap_content"
276
			android:text="Achievements" />
277
278
		<!-- show leaderboards -->
279
280
		<Button
281
			android:id="@+id/show_leaderboard"
282
			android:layout_width="wrap_content"
283
			android:layout_height="wrap_content"
284
			android:text="Leaderboard" />
285
	</LinearLayout>
286
287
</RelativeLayout>

We add buttons to access both achievements and leaderboards for the app. If you haven't completed the achievements tutorial, then you can remove the achievements button.

Back in your application's Activity class, we will be using these instance variables:

1
private Button button0, button1, button2, button3, button4, button5,
2
    button6, button7, button8, button9, buttonAgain;
3
private int number;
4
private Random rand;
5
private TextView info;
6
private int numGuesses=0;

If you completed the achievements tutorial you may notice an additional variable, numGuesses, to keep track of the number of user guesses each time they play the game.

You will need the following additional code in the onCreate method of the Activity class. If you're not using the achievements button, then remove the line that references it.

1
findViewById(R.id.show_achievements).setOnClickListener(this);
2
findViewById(R.id.show_leaderboard).setOnClickListener(this);
3
4
button0=(Button)findViewById(R.id.btn0);
5
button1=(Button)findViewById(R.id.btn1);
6
button2=(Button)findViewById(R.id.btn2);
7
button3=(Button)findViewById(R.id.btn3);
8
button4=(Button)findViewById(R.id.btn4);
9
button5=(Button)findViewById(R.id.btn5);
10
button6=(Button)findViewById(R.id.btn6);
11
button7=(Button)findViewById(R.id.btn7);
12
button8=(Button)findViewById(R.id.btn8);
13
button9=(Button)findViewById(R.id.btn9);
14
buttonAgain=(Button)findViewById(R.id.btnAgain);
15
16
info=(TextView)findViewById(R.id.guess_text);
17
18
rand=new Random();
19
number=rand.nextInt(10);

We also use the following methods for disabling and enabling the buttons during gameplay:

1
private void disableNumbers(){
2
    button0.setEnabled(false); button0.setTextColor(Color.parseColor("#ff000033"));
3
	button1.setEnabled(false); button1.setTextColor(Color.parseColor("#ff000033"));
4
	button2.setEnabled(false); button2.setTextColor(Color.parseColor("#ff000033"));
5
	button3.setEnabled(false); button3.setTextColor(Color.parseColor("#ff000033"));
6
	button4.setEnabled(false); button4.setTextColor(Color.parseColor("#ff000033"));
7
	button5.setEnabled(false); button5.setTextColor(Color.parseColor("#ff000033"));
8
	button6.setEnabled(false); button6.setTextColor(Color.parseColor("#ff000033"));
9
	button7.setEnabled(false); button7.setTextColor(Color.parseColor("#ff000033"));
10
	button8.setEnabled(false); button8.setTextColor(Color.parseColor("#ff000033"));
11
	button9.setEnabled(false); button9.setTextColor(Color.parseColor("#ff000033"));
12
	buttonAgain.setEnabled(true); buttonAgain.setTextColor(Color.parseColor("#ff000033"));
13
}
14
private void enableNumbers(){
15
	button0.setEnabled(true); button0.setTextColor(Color.WHITE);
16
	button1.setEnabled(true); button1.setTextColor(Color.WHITE);
17
	button2.setEnabled(true); button2.setTextColor(Color.WHITE);
18
	button3.setEnabled(true); button3.setTextColor(Color.WHITE);
19
	button4.setEnabled(true); button4.setTextColor(Color.WHITE);
20
	button5.setEnabled(true); button5.setTextColor(Color.WHITE);
21
	button6.setEnabled(true); button6.setTextColor(Color.WHITE);
22
	button7.setEnabled(true); button7.setTextColor(Color.WHITE);
23
	button8.setEnabled(true); button8.setTextColor(Color.WHITE);
24
	button9.setEnabled(true); button9.setTextColor(Color.WHITE);
25
	buttonAgain.setEnabled(false); buttonAgain.setTextColor(Color.parseColor("#ffffff00"));
26
}

We also need the following method we specified as onClick attribute for the number buttons in the layout. The player taps one of these to make a guess:

1
public void btnPressed(View v){
2
    int btn = Integer.parseInt(v.getTag().toString());
3
	if(btn<0){
4
		//again btn

5
		numGuesses=0;
6
		number=rand.nextInt(10);
7
		enableNumbers();
8
		info.setText("Set the number!");
9
	}
10
	else{
11
		//number button

12
		numGuesses++;
13
		if(btn==number){
14
			info.setText("Yes! It was "+number);
15
			if(getApiClient().isConnected()){
16
				Games.Achievements.unlock(getApiClient(), 
17
						getString(R.string.correct_guess_achievement));
18
				Games.Leaderboards.submitScore(getApiClient(), 
19
						getString(R.string.number_guesses_leaderboard), 
20
						numGuesses);
21
			}
22
			disableNumbers();
23
		}
24
		else if(numGuesses==5){
25
			info.setText("No! It was "+number);
26
			disableNumbers();
27
		}
28
		else
29
			info.setText("Try again!");
30
	}
31
}

Take a moment to look over the code. Even if you completed the app in the achievements tutorial, there are some changes to the logic in addition to the extra leaderboard code. If the player taps the Again button, we reset the numGuesses variable to 0. If the user taps a number button, we increment numGuesses. If you aren't using achievements, you can remove any code that references achievements.

We submit the score to the leaderboard when the user guessed correctly. The user can make up to five guesses.

The key line here is submitScore. We pass the number of guesses the player took to get the correct number. If the number of guesses is lower than any existing entry for the user in the leaderboard, their entry will be replaced with this new value. Notice that we use the string resource value we defined for the leaderboard. 

Step 2

Before we finish, let's allow the user to view the game leaderboard by tapping the Leaderboard button we added. We used the following code in onClick for the achievements:

1
else if (view.getId() == R.id.show_achievements){
2
    startActivityForResult(Games.Achievements.getAchievementsIntent(
3
		getApiClient()), 1);
4
}

Presenting the leaderboard is similar:

1
else if(view.getId() == R.id.show_leaderboard){
2
    startActivityForResult(Games.Leaderboards.getLeaderboardIntent(
3
		getApiClient(), getString(R.string.number_guesses_leaderboard)), 
4
		2);
5
}

This will let the user see the current standings within the leaderboard. The integer parameter is arbitrary.

When you run the app, it will attempt to log the user in, checking for permissions, and confirming login if successful:

The user is free to choose to sign out and back in whenever they like, but if they leave the app, it will attempt to automatically log them back in when they open it again. When the user guesses correctly, their score will be submitted to the leaderboard. Pressing the Leaderboard button will present the current standings:

From here, the user can access social features of Google Services via their Google account. You can set your apps up to use public and social leaderboards. Social leaderboards present listings of people in the user's circles, which can be managed for the game itself. For a public leaderboard, the user must have opted to share their scores publicly.

Conclusion

In this tutorial, we have implemented basic leaderboard functionality with Google Play Game Services. Note that you can do much more with leaderboards in your apps. For example, you can request leaderboard data for particular time-scales such as daily, weekly, and all-time. If a leaderboard contains a lot of scores, it is possible for your app to only fetch the top scores or the scores closest to the current player. Try experimenting with some of these enhancements in your own apps.