Uploading Player Data with the Http Get Method

The player data is submitted to the application server by using the HTTP GET method via the HttpGet class. Because you are posting form query variables, you want to use the URLEncodedUtils utility with a List container of BasicNameValuePair objects to assist with creating the final URL for the request. Finally, the whole network operation must be wrapped inside the AsyncTask object so that the user interface can continue to respond while the network request is being handled.

Creating an AsyncTask Class to Handle Uploads

To upload the user settings information to the application server, you need to add another AsyncTask subclass called AccountTask to QuizSettingsActivity. The AccountTask class can be run using the execute() method whenever the player changes a setting on the settings screen (see Figure 16.1). You can also run this task when the user first launches the application, to set up a unique player identifier on the application server for this player on this particular device, even if the person has not entered his or her nickname or email address. This could also allow you to retrieve the settings data from the server (via two-way syncing).

Been There, Done That!

Settings

Pa s s wo rd:

/ ■

« i _ ■

pirth Date:

a %

No Date Set

Gender:

y? ' 'V

Prefer NotTo Say

Prefer NotTo Say

Favorite Place

FIGURE 16.1

Using an indeterminate progress indicator in the top right of the menu bar (faint circle).

The details of the AccountTask subclass are very similar to those of the other AsyncTask subclasses you have written over the past two hours. (In the following example, exception handling has been removed for clarity and brevity.) The only interesting method here is doInBackground(), which communicates player settings to the application server:

@Override protected Boolean doInBackground(Object... params) {

Boolean succeeded = false;

Integer playerId =

mGameSettings .getInt(GAME_PREFERENCES_PLAYER_ID, -1);

String nickname =

mGameSettings,getString(GAME_PREFERENCES_NICKNAME, "");

String email =

mGameSettings,getString(GAME_PREFERENCES_EMAIL, "");

String password =

mGameSettings,getString(GAME_PREFERENCES_PASSWORD, "");

Integer score =

mGameSettings.getInt(GAME_PREFERENCES_SCORE, -1);

Integer gender =

mGameSettings.getInt(GAME_PREFERENCES_GENDER, -1);

Long birthdate =

mGameSettings,getLong(GAME_PREFERENCES_DOB, 0);

String favePlaceName =

mGameSettings ,getString(GAME_PREFERENCES_FAV_PLACE_NAME, "");

Vector<NameValuePair> vars = new Vector<NameValuePair>();

TelephonyManager telManager =

(TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); String uniqueld = telManager.getDeviceId(); vars.add(new BasicNameValuePair("uniqueId", uniqueld));

vars.add(

new BasicNameValuePair("updateId", playerId.toString())); vars.add(

new BasicNameValuePair("score", score.toString()));

vars.add(new BasicNameValuePair("nickname", nickname)); vars.add(new BasicNameValuePair(Memail", email)); vars.add(new BasicNameValuePair(Mpassword", password)); vars.add(new BasicNameValuePair("gender", gender.toString())); vars.add(new BasicNameValuePair("faveplace", favePlaceName)); vars.add(new BasicNameValuePair("dob", birthdate.toString()));

String url =

TRIVIA_SERVER_ACCOUNT_EDIT+ "?" + URLEncodedUtils. format (vars, null)

HttpGet request = new HttpGet(url); ResponseHandler<String> responseHandler =

new BasicResponseHandler(); HttpClient client = new DefaultHttpClient();

String responseBody = client.execute(request, responseHandler);

if (responseBody != null && responseBody.length() > 0) { Integer resultId = Integer.parseInt(responseBody); Editor editor = mGameSettings.edit(); editor.putInt(GAME_PREFERENCES_PLAYER_ID, resultId); editor.commit(); succeeded = true;

return succeeded;

Don't be overwhelmed by this code. It is actually pretty straightforward: You begin by creating a vector of name/value pairs for each of the player settings (saved in SharedPreferences) that you want to communicate to the server.

If this is the first time these player settings are being communicated to the server, a new record will be created. The new record is basically an empty player record on the server, with only one value: the device's unique identifier. This allows you to have a reasonable identifier for the player, even before he or she begins to enter other settings information. It also makes it possible for the player to change other information, such as his or her nickname, without unforeseen consequences. You should store the resulting identifier because it is used in all future requests.

When you have all the appropriate query variables set, you are ready to generate the request. You can use the handy URLEncodedUtils.format() method to format the vector of variables for the GET request. Finally, you initialize an HttpGet object, pass the GET request string you formulated, and execute it by using an HttpClient instance.

The HttpClient class provides helper utilities, in the form of the ResponseHandler classes, to parse the response. In this case, BasicReponseHandler is used to easily retrieve the body of the response as a String object. In this case, the String object contains the unique player ID, which can be stored in SharedPreferences for future use.

Handling Player Score Uploads

To upload the player score information to the application server, you might want to add another AsyncTask subclass to the QuizGameActivity class. But why not just update the existing QuizTask to communicate the player's score to the application server each time the game downloads new questions? This can help reduce latency and increase network efficiency.

You can simply add the score information as a query variable of the request being sent to the server within the doInBackground() method of the QuizTask class. You use the following code to accomplish this:

SharedPreferences settings =

getSharedPreferences(GAME_PREFERENCES, Context.MODE_PRIVATE); Integer playerld = settings.getInt(GAME_PREFERENCES_PLAYER_ID, -1); if (playerld != -1) {

Log.d(DEBUG_TAG, "Updating score");

Integer score = settings ,getInt(GAME_PREFERENCES_SCORE, -1); if (score != -1) {

pathToQuestions +=

"&updateScore=yes&updateId="+playerId+"&score="+score;

The code is added right after the pathToQuestions URL is created but before it's used so it can be updated.

It is typically not a good idea to send sensitive data across networks in plain text. For example, the device identifier could be used for nefarious purposes. Because all this application needs is some sort of unique but repeatable identifier, you could use a one-way hashing function, such as Secure Hash Algorithm (SHA), to do secure the device identifier data. The MessageDigest class, part of the java.security package, conveniently provides this ability: MessageDigest sha = MessageDigest.getInstance("SHA"); byte[] enc = sha.digest(uniqueId.getBytes());

The book's website contains the full implementation of this example and sends the hashed value to the server.

Did you Know?

You could also use a List container of BasicNameValuePair objects in conjunction with the URLEncodedUtils.format() method. However, for primitive values, such as integers, no additional encoding is needed.

0 0

Post a comment