io.gameup.android.GameUp.java Source code

Java tutorial

Introduction

Here is the source code for io.gameup.android.GameUp.java

Source

/*
 * 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());
    }

}