com.facebook.android.friendsmash.integration.GraphAPICall.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.android.friendsmash.integration.GraphAPICall.java

Source

/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
 * copy, modify, and distribute this software in source code or binary form for use
 * in connection with the web services and APIs provided by Facebook.
 *
 * As with any software that integrates with the Facebook platform, your use of
 * this software is subject to the Facebook Developer Principles and Policies
 * [http://developers.facebook.com/policy/]. This copyright notice shall be
 * included in all copies or substantial portions of the software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package com.facebook.android.friendsmash.integration;

import android.os.Bundle;
import android.util.Log;

import com.facebook.AccessToken;
import com.facebook.FacebookRequestError;
import com.facebook.GraphRequest;
import com.facebook.GraphRequestBatch;
import com.facebook.GraphResponse;
import com.facebook.android.friendsmash.FriendSmashApplication;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

/**
 * This class is handling all GraphAPI calls made in Friend Smash!
 * Simply put it wraps Facebook SDK's GraphRequest class providing convenience methods to make calls
 * and handle results through GraphApiCallback interface.
 * See https://developers.facebook.com/docs/graph-api for overview of Graph API and
 * https://developers.facebook.com/docs/android/graph for Android specific details.
 */
public class GraphAPICall {

    /**
     * When making Graph API calls different parameters can be specified. fields is a common
     * parameter used to specify which of the fields should be included in the response, as
     * by default, not all the fields in a node or edge are returned when you make a query.
     * This is really useful for making your API calls more efficient and fast.
     */
    private static final String PARAM_FIELDS = "fields";

    /**
     * Facebook SDK's class for handling Graph API calls
     * See https://developers.facebook.com/docs/reference/android/current/class/GraphRequest/
     */
    private GraphRequest graphRequest;

    /**
     * Facebook SDK's class encapsulating the response of a Graph API call
     * See https://developers.facebook.com/docs/reference/android/current/class/GraphResponse/
     */
    private GraphResponse graphResponse;

    /**
     * Friend Smash! specific interface making it easier to handle Graph API responses
     */
    private GraphAPICallback graphAPICallback;

    /**
     * Private constructor to enforce usage of static convenience methods creatic a specific call
     */
    private GraphAPICall(String path, GraphAPICallback callback) {
        setUp(callback);
        createPathRequest(path);
    }

    /**
     * Same as above
     */
    private GraphAPICall(GraphAPICallback callback) {
        setUp(callback);
    }

    /**
     * Executes the call asynchronously
     */
    public void executeAsync() {
        graphRequest.executeAsync();
    }

    /**
     * Adds a parameter to the request. Parameters can be used for many different purposes, such
     * as specifying fields to be included in the response, specifying number of results to be
     * included in the response and many others.
     * See https://developers.facebook.com/docs/graph-api/using-graph-api/
     */
    public void addParam(String param, String value) {
        Bundle params = graphRequest.getParameters();
        params.putString(param, value);
        graphRequest.setParameters(params);
    }

    /**
     * Registers callback and checks if valid access token is available, as it is
     * required to make a Graph API call.
     */
    private void setUp(GraphAPICallback callback) {
        graphAPICallback = callback;
        if (!FacebookLogin.isAccessTokenValid()) {
            Log.e(FriendSmashApplication.TAG, "Cannot call Graph API without a valid AccessToken");
        }
    }

    /**
     * Creates a GraphRequest with the specified path
     */
    private void createPathRequest(final String path) {
        AccessToken token = AccessToken.getCurrentAccessToken();
        graphRequest = GraphRequest.newGraphPathRequest(token, path, new GraphRequest.Callback() {
            @Override
            public void onCompleted(GraphResponse response) {
                handleResponse(response);
            }
        });
    }

    /**
     * Creates a GraphRequest for /me Graph API call
     */
    private void createMeRequest() {
        AccessToken token = AccessToken.getCurrentAccessToken();
        graphRequest = GraphRequest.newMeRequest(token, new GraphRequest.GraphJSONObjectCallback() {
            @Override
            public void onCompleted(JSONObject jsonObject, GraphResponse graphResponse) {
                handleResponse(graphResponse);
            }
        });
    }

    /**
     * Creates a GraphRequest for /me/friends Graph API call
     */
    private void createMyFriendsRequest() {
        AccessToken token = AccessToken.getCurrentAccessToken();
        graphRequest = GraphRequest.newMyFriendsRequest(token, new GraphRequest.GraphJSONArrayCallback() {
            @Override
            public void onCompleted(JSONArray users, GraphResponse response) {
                handleResponse(response);
            }
        });
    }

    /**
     * Creates a GraphRequest for a Graph API call to delete an object with given objectId.
     * See https://developers.facebook.com/docs/graph-api/using-graph-api/#deleting to understand
     * how deleting Graph API objects works.
     */
    private void createDeleteObjectRequest(String objectId) {
        AccessToken token = AccessToken.getCurrentAccessToken();
        graphRequest = GraphRequest.newDeleteObjectRequest(token, objectId, new GraphRequest.Callback() {
            @Override
            public void onCompleted(GraphResponse response) {
                handleResponse(response);
            }
        });
    }

    /**
     * Creates a GraphRequest to publish me/scores object with specified score parameter
     * See https://developers.facebook.com/docs/graph-api/using-graph-api/#publishing to undersntad
     * how publishing Graph API objects works.
     */
    private void createPublishScoreRequest(int score) {
        AccessToken token = AccessToken.getCurrentAccessToken();
        JSONObject object = new JSONObject();
        try {
            object.put("score", score);
        } catch (JSONException e) {
            Log.w(FriendSmashApplication.TAG, "Error publishing score to Facebook");
        }
        graphRequest = GraphRequest.newPostRequest(token, "me/scores", object, new GraphRequest.Callback() {
            @Override
            public void onCompleted(GraphResponse response) {
                handleResponse(response);
            }
        });
    }

    /**
     * Handles GraphResponse. Checks if there's next page available and makes another request
     * if necessary. By default Graph API results are paged and return up to a specific number
     * of objects. This number is called page size. For example if there's 40 objects to be returned
     * and the page size is 25 the first call will return 25 objects. Calling next page will return
     * the remaining 15 objects.
     * You can specify page size using 'limit' parameter when making call. If page size is not
     * specified it defaults to 25.
     * See https://developers.facebook.com/docs/graph-api/using-graph-api/#paging for more details
     * on paging.
     */
    private void handleResponse(GraphResponse response) {
        FacebookRequestError error = response.getError();
        if (error != null) {
            Log.e(FriendSmashApplication.TAG, error.toString());
            graphAPICallback.handleError(error);
        } else if (response != null) {
            addDataToResponse(response);
            if (hasNextPage(response)) {
                callNextPage(response);
            } else {
                graphAPICallback.handleResponse(graphResponse);
            }
        }
    }

    /**
     * Checks if GraphResponse has the next page.
     * See https://developers.facebook.com/docs/graph-api/using-graph-api/#paging for more details
     * on paging.
     */
    private boolean hasNextPage(GraphResponse response) {
        return response.getRequestForPagedResults(GraphResponse.PagingDirection.NEXT) != null;
    }

    /**
     * Calls the next page for a given GraphResponse.
     * See https://developers.facebook.com/docs/graph-api/using-graph-api/#paging for more details
     * on paging.
     */
    private void callNextPage(GraphResponse response) {
        graphRequest = response.getRequestForPagedResults(GraphResponse.PagingDirection.NEXT);
        if (graphRequest != null) {
            graphRequest.setCallback(new GraphRequest.Callback() {
                @Override
                public void onCompleted(GraphResponse graphResponse) {
                    handleResponse(graphResponse);
                }
            });
            graphRequest.executeAsync();
        }
    }

    /**
     * Adds data from specified GraphResponse to stored graphResponse variable. This is used to
     * combine the data from multiple pages into single GraphResponse object.
     * See https://developers.facebook.com/docs/graph-api/using-graph-api/#paging for more details
     * on paging.
     */
    private void addDataToResponse(GraphResponse response) {
        if (graphResponse == null) {
            graphResponse = response;
        } else {
            JSONArray newData = response.getJSONObject().optJSONArray("data");
            JSONArray existingData = graphResponse.getJSONObject().optJSONArray("data");
            for (int i = 0; i < newData.length(); i++) {
                existingData.put(newData.opt(i));
            }
        }
    }

    /**
     * Creates GraphAPICall wrapper for GET /me call with specified fields.
     * Response is passed to the specified GraphAPICallback.
     * /me call returns the information about the current user
     */
    public static GraphAPICall callMe(String fields, GraphAPICallback callback) {
        GraphAPICall call = new GraphAPICall(callback);
        call.createMeRequest();
        call.addParam(PARAM_FIELDS, fields);
        return call;
    }

    /**
     * Creates GraphAPICall wrapper for GET /{user-Id} call with specified fields.
     * Response is passed to the specified GraphAPICallback.
     * /{user-id} returns the information about specific user
     */
    public static GraphAPICall callUser(String userId, String fields, GraphAPICallback callback) {
        GraphAPICall call = new GraphAPICall(userId, callback);
        call.addParam(PARAM_FIELDS, fields);
        return call;
    }

    /**
     * Creates GraphAPICall wrapper for GET /me/friends call with specified fields.
     * Response is passed to the specified GraphAPICallback.
     * Note user_friends permission is required to make this call
     * /me/friends returns the information about current user's friends who are also playing the game
     */
    public static GraphAPICall callMeFriends(String fields, GraphAPICallback callback) {
        if (FacebookLogin.isPermissionGranted(FacebookLoginPermission.USER_FRIENDS)) {
            GraphAPICall call = new GraphAPICall(callback);
            call.createMyFriendsRequest();
            call.addParam(PARAM_FIELDS, fields);
            return call;
        } else {
            Log.w(FriendSmashApplication.TAG, "Cannot call me/friends without user_friends permission");
            return null;
        }
    }

    /**
     * Creates GraphAPICall wrapper for GET /me/scores call.
     * Response is passed to the specified GraphAPICallback.
     * /me/scores returns the current user's score in the app.
     * See https://developers.facebook.com/docs/games/scores for details about Scores API
     */
    public static GraphAPICall callMeScores(GraphAPICallback callback) {
        GraphAPICall call = new GraphAPICall("me/scores", callback);
        call.addParam(PARAM_FIELDS, "score");
        return call;
    }

    /**
     * Creates GraphAPICall wrapper for GET /{app-id}/scores call.
     * Response is passed to the specified GraphAPICallback.
     * Note user_friends permission is required to make this call
     * /{app-id}/scores returns the current user friends' scores in the app.
     * See https://developers.facebook.com/docs/games/scores for details about Scores API
     */
    public static GraphAPICall callAppScores(String appId, GraphAPICallback callback) {
        if (FacebookLogin.isPermissionGranted(FacebookLoginPermission.USER_FRIENDS)) {
            GraphAPICall call = new GraphAPICall(appId + "/scores", callback);
            call.addParam("fields", "user,score");
            return call;
        } else {
            Log.w(FriendSmashApplication.TAG, "Cannot call app-id/scores without user_friends permisison");
            return null;
        }
    }

    /**
     * Creates GraphAPICall wrapper for POST /me/scores call.
     * Response is passed to the specified GraphAPICallback.
     * Note publish_actions permission is required to make this call
     * POST /me/scores publishes current user's score. Used in Friend Smash to track the top score.
     * See https://developers.facebook.com/docs/games/scores for details about Scores API
     */
    public static GraphAPICall publishScore(int score, GraphAPICallback callback) {
        if (FacebookLogin.isPermissionGranted(FacebookLoginPermission.PUBLISH_ACTIONS)) {
            GraphAPICall call = new GraphAPICall(callback);
            call.createPublishScoreRequest(score);
            return call;
        } else {
            Log.w(FriendSmashApplication.TAG, "Cannot publish scores without publish_actions permission");
            return null;
        }
    }

    /**
     * Creates GraphAPICall wrapper for GET /{request-id} call.
     * Response is passed to the specified GraphAPICallback.
     * /{request-id} returns the information about specific request
     */
    public static GraphAPICall callRequest(String requestId, GraphAPICallback callback) {
        GraphAPICall call = new GraphAPICall(requestId, callback);
        return call;
    }

    /**
     * Creates GraphAPICall wrapper for DELETE /{request-Id} call with specified fields.
     * Response is passed to the specified GraphAPICallback.
     * DELETE /{request-id} deletes specified request.
     */
    public static GraphAPICall deleteRequest(String requestId, GraphAPICallback callback) {
        GraphAPICall call = new GraphAPICall(callback);
        call.createDeleteObjectRequest(requestId);
        return call;
    }

    /**
     * Helper method to create a batch of multiple GraphAPICall objects.
     * Batching allows to pass instructions for several operations in a single HTTP request.
     * For more information on batching see
     * https://developers.facebook.com/docs/graph-api/making-multiple-requests and
     * https://developers.facebook.com/docs/reference/android/current/class/GraphRequestBatch/
     * for Android implementation.
     */
    public static GraphRequestBatch createRequestBatch(GraphAPICall... requests) {
        GraphRequestBatch batch = new GraphRequestBatch();
        for (GraphAPICall request : requests) {
            if (request != null) {
                batch.add(request.graphRequest);
            }
        }
        return batch;
    }

    /**
     * Helper method to extract JSONArray with data returned in GraphResponse.
     * Useful for example for extracting friends data returned in GraphResponse for /me/friends call
     */
    public static JSONArray getDataFromResponse(GraphResponse response) {
        JSONObject graphObject = response.getJSONObject();
        return graphObject.optJSONArray("data");
    }
}