Java tutorial
/* * Copyright 2014-2015 GameUp * * 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. */ package io.gameup.android; import android.net.Uri; import com.google.gson.JsonParseException; import com.squareup.okhttp.Request; import com.squareup.okhttp.Response; import java.io.IOException; import java.io.Reader; import java.net.HttpURLConnection; import java.util.List; import io.gameup.android.entity.Achievement; import io.gameup.android.entity.Gamer; import io.gameup.android.entity.LeaderboardAndRank; import io.gameup.android.entity.Rank; import io.gameup.android.http.OkHttpClientFactory; import io.gameup.android.http.RequestFactory; import io.gameup.android.entity.AchievementList; import io.gameup.android.json.AchievementProgress; import io.gameup.android.json.GsonFactory; import io.gameup.android.json.LeaderboardSubmission; import io.gameup.android.json.StorageGetWrapper; import lombok.Getter; import lombok.NonNull; /** * Represents a session for an authenticated user. * * All operations are thread-safe. * * Since this represents a particular user, the token is fixed - if it needs to * change then a new session should be created via the GameUp.login() method. * * Call the ping() method to check that the remote service is reachable, and * that the session is accepted with the given API key. */ public class GameUpSession { /** The user identification token for this session. */ private final String token; /** Rough local device timestamp when this session was created. */ @Getter private final long createdAt; /** * Initialise with the given token. * * @param token The token key to use, must not be null. */ GameUpSession(final @NonNull String token) { this.token = token; this.createdAt = System.currentTimeMillis(); } /** * Serialise this instance to a String, safe for storage or transmission. * The resulting String is ~230 bytes long. * * @return A String representation of this instance. */ @Override public String toString() { return GsonFactory.get().toJson(this); } /** * Load a session from the given String. Will fail hard if the input does * not represent a GameUpSession instance. * * @param gameUpSession The String to attempt to load from. * @return The retrieved GameUpSession instance. * @throws IOException if the given string cannot be read as a compatible * GameUpSession instance. */ public static GameUpSession fromString(final @NonNull String gameUpSession) throws IOException { try { return GsonFactory.get().fromJson(gameUpSession, GameUpSession.class); } catch (final JsonParseException e) { throw new IOException("Input is not a valid GameUpSession"); } } /** * Ping the GameUp service to check it is reachable and ready to handle * requests. * * @param apiKey The API key to use. * @return true if the service is reachable, responds correctly, and accepts * the API key and token combination; false otherwise. * @throws IOException when a network or communication error occurs. */ public boolean ping(final @NonNull String apiKey) throws IOException { return GameUp.ping(apiKey, token); } /** * Get information about the gamer who owns this session. * * @param apiKey The API key to use. * @return An entity containing gamer information. * @throws IOException when a network or communication error occurs. */ public Gamer gamer(final @NonNull String apiKey) throws IOException { Reader in = null; try { final Request request = RequestFactory.get(new Uri.Builder().scheme(GameUp.SCHEME) .encodedAuthority(GameUp.API_SERVER).appendPath("v0").appendPath("gamer").build(), apiKey, token); final Response response = OkHttpClientFactory.getClient().newCall(request).execute(); if (response.code() != HttpURLConnection.HTTP_OK) { throw new IOException("Operation returned HTTP " + response.code()); } in = response.body().charStream(); try { return GsonFactory.get().fromJson(in, Gamer.class); } catch (final JsonParseException e) { throw new IOException("Response data does not match expected entity"); } } finally { Utils.closeQuietly(in); } } /** * Perform a key-value storage write operation, storing data as JSON. Data * is private per-user and per-game. * * NOTE: This is not designed to store confidential data, such as payment * information etc. * * @param apiKey The API key to use. * @param key The key to store the given data under. * @param value The object to serialise and store. * @throws IOException when a network or communication error occurs. */ public void storagePut(final @NonNull String apiKey, final @NonNull String key, final @NonNull Object value) throws IOException { final Request request = RequestFactory.put( new Uri.Builder().scheme(GameUp.SCHEME).encodedAuthority(GameUp.API_SERVER).appendPath("v0") .appendPath("gamer").appendPath("storage").appendPath(key).build(), apiKey, token, GsonFactory.get().toJson(value)); final Response response = OkHttpClientFactory.getClient().newCall(request).execute(); if (response.code() != HttpURLConnection.HTTP_NO_CONTENT) { throw new IOException("Operation returned HTTP " + response.code()); } } /** * Perform a key-value storage read operation. * * @param apiKey The API key to use. * @param key The key to attempt to read data from. * @param type The class literal to attempt to deserialise as. * @return The entity requested, or null if there was no data. * @throws IOException when a network or communication error occurs. */ public <T> T storageGet(final @NonNull String apiKey, final @NonNull String key, final @NonNull Class<T> type) throws IOException { Reader in = null; try { final Request request = RequestFactory.get( new Uri.Builder().scheme(GameUp.SCHEME).encodedAuthority(GameUp.API_SERVER).appendPath("v0") .appendPath("gamer").appendPath("storage").appendPath(key).build(), apiKey, token); final Response response = OkHttpClientFactory.getClient().newCall(request).execute(); switch (response.code()) { case HttpURLConnection.HTTP_OK: in = response.body().charStream(); try { final StorageGetWrapper responseWrapper = GsonFactory.get().fromJson(in, StorageGetWrapper.class); return GsonFactory.get().fromJson(responseWrapper.getValue(), type); } catch (final JsonParseException e) { throw new IOException("Response data does not match expected entity"); } case HttpURLConnection.HTTP_NOT_FOUND: return null; default: throw new IOException("Operation returned HTTP " + response.code()); } } finally { Utils.closeQuietly(in); } } /** * Perform a key-value storage delete operation. Will silently ignore absent * data. * * @param apiKey The API key to use. * @param key The key to delete data from. * @throws IOException when a network or communication error occurs. */ public void storageDelete(final @NonNull String apiKey, final @NonNull String key) throws IOException { final Request request = RequestFactory .delete(new Uri.Builder().scheme(GameUp.SCHEME).encodedAuthority(GameUp.API_SERVER).appendPath("v0") .appendPath("gamer").appendPath("storage").appendPath(key).build(), apiKey, token); final Response response = OkHttpClientFactory.getClient().newCall(request).execute(); if (response.code() != HttpURLConnection.HTTP_NO_CONTENT) { throw new IOException("Operation returned HTTP " + response.code()); } } /** * Report progress towards a given achievement. Equivalent to calling * achievement(apiKey, achievementId, 1) below. * * Progress will be "1". This method is intended for convenience when * triggering "normal"-type achievements, but will still add 1 to an * "incremental"-type achievement if needed. * * @param apiKey The API key to use. * @param achievementId The internal Achievement ID to interact with. * @return An Achievement instance if this call results in an achievement * being completed or progress is reported, null otherwise. * @throws IOException when a network or communication error occurs. */ public Achievement achievement(final @NonNull String apiKey, final @NonNull String achievementId) throws IOException { return achievement(apiKey, achievementId, 1); } /** * Report progress towards a given achievement. * * @param apiKey The API key to use. * @param achievementId The internal Achievement ID to interact with. * @param count The progress amount to report. * @return An Achievement instance if this call results in an achievement * being completed or progress is reported, null otherwise. * @throws IOException when a network or communication error occurs. */ public Achievement achievement(final @NonNull String apiKey, final @NonNull String achievementId, final int count) throws IOException { final Request request = RequestFactory.post( new Uri.Builder().scheme(GameUp.SCHEME).encodedAuthority(GameUp.API_SERVER).appendPath("v0") .appendPath("gamer").appendPath("achievement").appendPath(achievementId).build(), apiKey, token, GsonFactory.get().toJson(new AchievementProgress(count))); final Response response = OkHttpClientFactory.getClient().newCall(request).execute(); Reader in = null; try { switch (response.code()) { case HttpURLConnection.HTTP_OK: in = response.body().charStream(); try { return GsonFactory.get().fromJson(in, Achievement.class); } catch (final JsonParseException e) { throw new IOException("Response data does not match expected entity"); } case HttpURLConnection.HTTP_NO_CONTENT: return null; default: throw new IOException("Operation returned HTTP " + response.code()); } } finally { Utils.closeQuietly(in); } } /** * Get a list of achievements available for the game, including any gamer * data such as progress or completed timestamps. * * @param apiKey The API key to use. * @return A List containing Achievement instances, may be empty if none are * returned for the current game. * @throws IOException when a network or communication error occurs. */ public AchievementList achievement(final @NonNull String apiKey) throws IOException { Reader in = null; try { final Request request = RequestFactory .get(new Uri.Builder().scheme(GameUp.SCHEME).encodedAuthority(GameUp.API_SERVER) .appendPath("v0").appendPath("gamer").appendPath("achievement").build(), apiKey, token); final Response response = OkHttpClientFactory.getClient().newCall(request).execute(); if (response.code() != HttpURLConnection.HTTP_OK) { throw new IOException("Operation returned HTTP " + response.code()); } in = response.body().charStream(); try { return GsonFactory.get().fromJson(in, AchievementList.class); } catch (final JsonParseException e) { throw new IOException("Response data does not match expected entity"); } } finally { Utils.closeQuietly(in); } } /** * Submit a new score to the specified leaderboard. The new score will only * overwrite any previously submitted value if it's "better" according to * the sorting rules of the leaderboard, but updated ranking details are * returned in all cases. * * @param apiKey The API key to use. * @param leaderboardId The private ID of the leaderboard to submit to. * @param score The score to submit. * @return A Rank instance containing updated detailed rank data for the * current gamer. * @throws IOException when a network or communication error occurs. */ public Rank leaderboard(final @NonNull String apiKey, final @NonNull String leaderboardId, final long score) throws IOException { final Request request = RequestFactory.post( new Uri.Builder().scheme(GameUp.SCHEME).encodedAuthority(GameUp.API_SERVER).appendPath("v0") .appendPath("gamer").appendPath("leaderboard").appendPath(leaderboardId).build(), apiKey, token, GsonFactory.get().toJson(new LeaderboardSubmission(score))); final Response response = OkHttpClientFactory.getClient().newCall(request).execute(); Reader in = null; try { switch (response.code()) { case HttpURLConnection.HTTP_OK: in = response.body().charStream(); try { return GsonFactory.get().fromJson(in, Rank.class); } catch (final JsonParseException e) { throw new IOException("Response data does not match expected entity"); } default: throw new IOException("Operation returned HTTP " + response.code()); } } finally { Utils.closeQuietly(in); } } /** * Request leaderboard metadata, the current top ranked gamers, and the * current gamer's detailed ranking on a specified leaderboard. * * @param apiKey The API key to use. * @param leaderboardId The private ID of the leaderboard to request. * @return A corresponding LeaderboardAndRank instance. * @throws IOException when a network or communication error occurs. */ public LeaderboardAndRank leaderboard(final @NonNull String apiKey, final @NonNull String leaderboardId) throws IOException { Reader in = null; try { final Request request = RequestFactory.get( new Uri.Builder().scheme(GameUp.SCHEME).encodedAuthority(GameUp.API_SERVER).appendPath("v0") .appendPath("gamer").appendPath("leaderboard").appendPath(leaderboardId).build(), apiKey, token); final Response response = OkHttpClientFactory.getClient().newCall(request).execute(); if (response.code() != HttpURLConnection.HTTP_OK) { throw new IOException("Operation returned HTTP " + response.code()); } in = response.body().charStream(); try { return GsonFactory.get().fromJson(in, LeaderboardAndRank.class); } catch (final JsonParseException e) { throw new IOException("Response data does not match expected entity"); } } finally { Utils.closeQuietly(in); } } }