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.annotation.SuppressLint; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.graphics.Bitmap; import android.graphics.Color; import android.net.Uri; import android.os.Build; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.Button; import android.widget.LinearLayout; import android.widget.ProgressBar; 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 io.gameup.android.entity.Game; import io.gameup.android.entity.Leaderboard; import io.gameup.android.entity.Server; import io.gameup.android.http.OkHttpClientFactory; import io.gameup.android.http.RequestFactory; import io.gameup.android.entity.AchievementList; import io.gameup.android.json.GsonFactory; import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.NonNull; /** * Static methods for interacting with the GameUp service without a gamer login. */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class GameUp { /** Connection scheme and server addresses. */ public static final String SCHEME = "https"; public static final String LOGIN_SERVER = "login.gameup.io"; public static final String API_SERVER = "api.gameup.io"; /** User Agent string to be used in API calls and the login WebView. */ public static final String _WEBVIEW_USER_AGENT_ = "_WEBVIEW_USER_AGENT_"; public static final String USER_AGENT_TEMPLATE = "gameup-android-sdk/" + BuildConfig.VERSION_NAME + " (Android " + Build.VERSION.SDK_INT + "; " + _WEBVIEW_USER_AGENT_ + ")"; /** * 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; false otherwise. * @throws IOException */ public static boolean ping(final @NonNull String apiKey) throws IOException { return ping(apiKey, ""); } /** * Ping the GameUp service to check it is reachable and ready to handle * requests. * * Abstracted here to be used as shared code between the GameUp.ping() and * GameUpSession.ping() methods. * * @param apiKey The API key to use. * @param token The gamer token to use, may be an empty String but not null. * @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. */ static boolean ping(final @NonNull String apiKey, final @NonNull String token) throws IOException { final Request request = RequestFactory.head(new Uri.Builder().scheme(GameUp.SCHEME) .encodedAuthority(GameUp.API_SERVER).appendPath("v0").build(), apiKey, token); final Response response = OkHttpClientFactory.getClient().newCall(request).execute(); return response.code() == HttpURLConnection.HTTP_OK; } /** * Retrieve GameUp global service and/or server instance data. * * @param apiKey The API key to use. * @return A Server instance containing GameUp service and/or server * instance information, for example current time. * @throws IOException when a network or communication error occurs. */ public static Server server(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("server").build(), apiKey); 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, Server.class); } catch (final JsonParseException e) { throw new IOException("Response data does not match expected entity"); } } finally { Utils.closeQuietly(in); } } /** * Retrieve information about the game the given API key corresponds to, as * configured in the remote service. * * @param apiKey The API key to use. * @return An entity representing remotely configured data about the game. * @throws IOException when a network or communication error occurs. */ public static Game game(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("game").build(), apiKey); 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, Game.class); } catch (final JsonParseException e) { throw new IOException("Response data does not match expected entity"); } } finally { Utils.closeQuietly(in); } } /** * Get a list of achievements available for the game, excluding any gamer * data such as progress or completed timestamps. * * @param apiKey The API key to use. * @return An AchievementList, containing Achievement instances, may be * empty if none are returned for the current game. * @throws IOException when a network or communication error occurs. */ public static 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("game").appendPath("achievement").build(), apiKey); 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); } } /** * Request leaderboard metadata and the current top ranked gamers on a * specified leaderboard. * * @param apiKey The API key to use. * @param leaderboardId The private ID of the leaderboard to request. * @return A Leaderboard instance. * @throws IOException when a network or communication error occurs. */ public static Leaderboard 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("game").appendPath("leaderboard").appendPath(leaderboardId).build(), apiKey); 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, Leaderboard.class); } catch (final JsonParseException e) { throw new IOException("Response data does not match expected entity"); } } finally { Utils.closeQuietly(in); } } /** * Pop up a dialog with options for the user to login to the GameUp service. * * @param context Used as a parent for the dialog and too look up resources. * @param apiKey The GameUp API key to pass to the login service. * @param listener The listener entity to be notified of login events. */ @SuppressLint("SetJavaScriptEnabled") public static void login(final @NonNull Context context, final @NonNull String apiKey, final @NonNull GameUpLoginListener listener) { // Minimal dialog with no title. final Dialog dialog = new Dialog(context); dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); dialog.setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(final DialogInterface dialog) { listener.onLoginCancelled(); } }); // Indeterminate progress bar to indicate work being done in web view. final ProgressBar progressBar = new ProgressBar(context); progressBar.setIndeterminate(true); // Web view to display remote login page. final WebView webView = new WebView(context); final WebSettings webSettings = webView.getSettings(); // JS required for Google+ login - yes, really. webSettings.setJavaScriptEnabled(true); webSettings.setUserAgentString( USER_AGENT_TEMPLATE.replace(_WEBVIEW_USER_AGENT_, webSettings.getUserAgentString())); webView.setWebViewClient(new WebViewClient() { @Override public void onPageStarted(final WebView view, final String url, final Bitmap favicon) { super.onPageStarted(view, url, favicon); progressBar.setVisibility(View.VISIBLE); if (url.startsWith(GameUp.SCHEME + "://" + LOGIN_SERVER + "/v0/gamer/login/success")) { final Uri uri = Uri.parse(url); final String token = uri.getQueryParameter("token"); dialog.dismiss(); listener.onLoginComplete(new GameUpSession(token)); } } @Override public void onPageFinished(final WebView view, final String url) { super.onPageFinished(view, url); progressBar.setVisibility(View.GONE); } @Override public void onReceivedError(final WebView view, final int errorCode, final String description, final String failingUrl) { dialog.dismiss(); listener.onLoginError(); } }); // Progress bar will appear in the top-left corner, inside the web view. webView.addView(progressBar); // A separator for the cancel button. final View separator = new View(context); separator.setBackgroundColor(Color.parseColor("#D0D0D0")); // The cancel button itself. final Button cancelButton = (Button) LayoutInflater.from(context) .inflate(R.layout.gameup_login_cancel_button, null); cancelButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { dialog.cancel(); } }); // The holder layout. final LinearLayout layout = new LinearLayout(context); layout.setBackgroundColor(Color.parseColor("#FFFFFF")); layout.setOrientation(LinearLayout.VERTICAL); layout.addView(webView); layout.addView(separator); layout.addView(cancelButton); // Width, height (and optionally weight) of each view inside the layout. webView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT, 1)); separator.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 2)); cancelButton.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)); // Set the holder layout as the full view of the dialog. dialog.setContentView(layout); // Stretch the dialog to nearly full screen. final ViewGroup.LayoutParams lp = dialog.getWindow().getAttributes(); lp.width = WindowManager.LayoutParams.MATCH_PARENT; lp.height = WindowManager.LayoutParams.MATCH_PARENT; // Pop up the dialog and load the initial login page. dialog.show(); webView.loadUrl(new Uri.Builder().scheme(GameUp.SCHEME).encodedAuthority(GameUp.LOGIN_SERVER) .appendPath("v0").appendPath("gamer").appendPath("login").appendQueryParameter("apiKey", apiKey) .build().toString()); } }