package com.Facebook;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.Window;
import android.view.WindowManager;
import com.Facebook.entities.Album;
import com.Facebook.entities.Feed;
import com.Facebook.entities.Photo;
import com.Facebook.entities.Profile;
import com.Facebook.entities.Story;
import com.Facebook.utils.Errors;
import com.Facebook.utils.Logger;
import com.facebook.FacebookException;
import com.facebook.FacebookOperationCanceledException;
import com.facebook.FacebookRequestError;
import com.facebook.HttpMethod;
import com.facebook.Request;
import com.facebook.RequestAsyncTask;
import com.facebook.Response;
import com.facebook.Session;
import com.facebook.SessionState;
import com.facebook.model.GraphMultiResult;
import com.facebook.model.GraphObject;
import com.facebook.model.GraphObjectList;
import com.facebook.model.GraphUser;
import com.facebook.widget.WebDialog;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
/**
* Simple Facebook SDK which wraps original Facebook SDK 3.5
*
* <br>
* <br>
* <b>Features:</b>
* <ul>
* <li>Simple configuration</li>
* <li>No need to use LoginButton view</li>
* <li>Login/logout</li>
* <li>Publish feed</li>
* <li>Publish open graph story</li>
* <li>Publish photo</li>
* <li>Invite friends</li>
* <li>Fetch my profile</li>
* <li>Fetch friends</li>
* <li>Fetch albums</li>
* <li>Predefined all possible permissions. See {@link Permissions}</li>
* <li>No need to care for correct sequence logging with READ and PUBLISH permissions</li>
* </ul>
*
* @author sromku
*/
public class SimpleFacebook
{
private static SimpleFacebook mInstance = null;
private static SimpleFacebookConfiguration mConfiguration = new SimpleFacebookConfiguration.Builder().build();
private static Activity mActivity;
private SessionStatusCallback mSessionStatusCallback = null;
private WebDialog mDialog = null;
private SimpleFacebook()
{
mSessionStatusCallback = new SessionStatusCallback();
}
public static void initialize(Activity activity)
{
if (mInstance == null) {
mInstance = new SimpleFacebook();
}
mActivity = activity;
}
public static SimpleFacebook getInstance(Activity activity)
{
if (mInstance == null)
{
mInstance = new SimpleFacebook();
}
mActivity = activity;
return mInstance;
}
public static SimpleFacebook getInstance()
{
return mInstance;
}
/**
* Set facebook configuration
*
* @param facebookToolsConfiguration
*/
public static void setConfiguration(SimpleFacebookConfiguration facebookToolsConfiguration)
{
mConfiguration = facebookToolsConfiguration;
}
/**
* Login to Facebook
*
* @param onLoginListener
*/
public void login(OnLoginListener onLoginListener)
{
if (isLogin())
{
// log
logInfo("You were already logged in before calling 'login()' method");
if (onLoginListener != null)
{
onLoginListener.onLogin();
}
}
else
{
Session session = Session.getActiveSession();
if (session == null || session.getState().isClosed())
{
session = new Session.Builder(mActivity.getApplicationContext())
.setApplicationId(mConfiguration.getAppId())
.build();
Session.setActiveSession(session);
}
mSessionStatusCallback.mOnLoginListener = onLoginListener;
session.addCallback(mSessionStatusCallback);
/*
* If session is not opened, then open it
*/
if (!session.isOpened())
{
openSession(session, true);
}
else
{
if (onLoginListener != null)
{
onLoginListener.onLogin();
}
}
}
}
/**
* Logout from Facebook
*/
public void logout(OnLogoutListener onLogoutListener)
{
if (isLogin())
{
Session session = Session.getActiveSession();
if (session != null && !session.isClosed())
{
mSessionStatusCallback.mOnLogoutListener = onLogoutListener;
session.closeAndClearTokenInformation();
session.removeCallback(mSessionStatusCallback);
if (onLogoutListener != null)
{
onLogoutListener.onLogout();
}
}
}
else
{
// log
logInfo("You were already logged out before calling 'logout()' method");
if (onLogoutListener != null)
{
onLogoutListener.onLogout();
}
}
}
/**
* Get my profile from facebook.<br>
* This methos will return profile with next default properties depends on permissions you have:<br>
* <em>id, name, first_name, middle_name, last_name, gender, locale, languages, link, username, timezone, updated_time, verified, bio, birthday, education, email,
* hometown, location, political, favorite_athletes, favorite_teams, quotes, relationship_status, religion, website, work</em>
*
* <br>
* <br>
* If you need additional or other profile properties like: <em>age_range, picture and more</em>, then use
* this method: {@link #getProfile(Properties, OnProfileRequestListener)} <br>
* <br>
* <b>Note:</b> If you need only few properties for your app, then it is recommended <b>not</b> to use
* this method, since getting unnecessary properties is time consuming task from facebook side.<br>
* It is recommended in this case, to use {@link #getProfile(Properties, OnProfileRequestListener)} and
* mention only needed properties.
*
* @param onProfileRequestListener
*/
public void getProfile(final OnProfileRequestListener onProfileRequestListener)
{
getProfile(null, onProfileRequestListener);
}
/**
* Get my profile from facebook by mentioning specific parameters. <br>
* For example, if you need: <em>square picture 500x500 pixels</em>
*
* @param onProfileRequestListener
* @param properties The {@link Properties}. <br>
* To create {@link Properties} instance use:
*
* <pre>
* // define the profile picture we want to get
* PictureAttributes pictureAttributes = Attributes.createPictureAttributes();
* pictureAttributes.setType(PictureType.SQUARE);
* pictureAttributes.setHeight(500);
* pictureAttributes.setWidth(500);
*
* // create properties
* Properties properties = new Properties.Builder()
* .add(Properties.ID)
* .add(Properties.FIRST_NAME)
* .add(Properties.PICTURE, attributes)
* .build();
* </pre>
*/
public void getProfile(Properties properties, final OnProfileRequestListener onProfileRequestListener)
{
// if we are logged in
if (isLogin())
{
Session session = getOpenSession();
Bundle bundle = null;
if (properties != null)
{
bundle = properties.getBundle();
}
Request request = new Request(session, "me", bundle, HttpMethod.GET, new Request.Callback()
{
@Override
public void onCompleted(Response response)
{
GraphUser graphUser = response.getGraphObjectAs(GraphUser.class);
FacebookRequestError error = response.getError();
if (error != null)
{
// log
logError("failed to get profile", error.getException());
// callback with 'exception'
if (onProfileRequestListener != null)
{
onProfileRequestListener.onException(error.getException());
}
}
else
{
// callback with 'complete'
if (onProfileRequestListener != null)
{
Profile profile = Profile.create(graphUser);
onProfileRequestListener.onComplete(profile);
}
}
}
});
RequestAsyncTask task = new RequestAsyncTask(request);
task.execute();
// callback with 'thinking'
if (onProfileRequestListener != null)
{
onProfileRequestListener.onThinking();
}
}
else
{
String reason = Errors.getError(Errors.ErrorMsg.LOGIN);
logError(reason, null);
// callback with 'fail' due to not being loged
if (onProfileRequestListener != null)
{
onProfileRequestListener.onFail(reason);
}
}
}
/**
* Get my friends from facebook.<br>
* This methos will return profile with next default properties depends on permissions you have:<br>
* <em>id, name</em>
*
* <br>
* <br>
* If you need additional or other friend properties like: <em>education, location and more</em>, then use
* this method: {@link #getFriends(Properties, OnFriendsRequestListener)} <br>
* <br>
*
* @param onFriendsRequestListener
*/
public void getFriends(final OnFriendsRequestListener onFriendsRequestListener)
{
getFriends(null, onFriendsRequestListener);
}
/**
* Get my friends from facebook by mentioning specific parameters. <br>
* For example, if you need: <em>id, last_name, picture, birthday</em>
*
* @param onFriendsRequestListener
* @param properties The {@link Properties}. <br>
* To create {@link Properties} instance use:
*
* <pre>
* // define the friend picture we want to get
* PictureAttributes pictureAttributes = Attributes.createPictureAttributes();
* pictureAttributes.setType(PictureType.SQUARE);
* pictureAttributes.setHeight(500);
* pictureAttributes.setWidth(500);
*
* // create properties
* Properties properties = new Properties.Builder()
* .add(Properties.ID)
* .add(Properties.LAST_NAME)
* .add(Properties.PICTURE, attributes)
* .add(Properties.BIRTHDAY)
* .build();
* </pre>
*/
public void getFriends(Properties properties, final OnFriendsRequestListener onFriendsRequestListener)
{
// if we are logged in
if (isLogin())
{
// move these params to method call parameters
Session session = getOpenSession();
Bundle bundle = null;
if (properties != null)
{
bundle = properties.getBundle();
}
Request request = new Request(session, "me/friends", bundle, HttpMethod.GET, new Request.Callback()
{
@Override
public void onCompleted(Response response)
{
List<GraphUser> graphUsers = typedListFromResponse(response, GraphUser.class);
FacebookRequestError error = response.getError();
if (error != null)
{
// log
logError("failed to get friends", error.getException());
// callback with 'exception'
if (onFriendsRequestListener != null)
{
onFriendsRequestListener.onException(error.getException());
}
}
else
{
// callback with 'complete'
if (onFriendsRequestListener != null)
{
List<Profile> friends = new ArrayList<Profile>(graphUsers.size());
for (GraphUser graphUser: graphUsers)
{
friends.add(Profile.create(graphUser));
}
onFriendsRequestListener.onComplete(friends);
}
}
}
});
RequestAsyncTask task = new RequestAsyncTask(request);
task.execute();
// callback with 'thinking'
if (onFriendsRequestListener != null)
{
onFriendsRequestListener.onThinking();
}
}
else
{
String reason = Errors.getError(Errors.ErrorMsg.LOGIN);
logError(reason, null);
// callback with 'fail' due to not being loged
if (onFriendsRequestListener != null)
{
onFriendsRequestListener.onFail(reason);
}
}
}
/**
* Get albums
*
* <b>Permission:</b><br>
* {@link Permissions#USER_PHOTOS}
*/
public void getAlbums(final OnAlbumsRequestListener onAlbumsRequestListener)
{
// if we are logged in
if (isLogin())
{
// move these params to method call parameters
Session session = getOpenSession();
Bundle bundle = new Bundle();
bundle.putString("date_format", "U");
Request request = new Request(session, "me/albums", bundle, HttpMethod.GET, new Request.Callback()
{
@Override
public void onCompleted(Response response)
{
List<GraphObject> graphObjects = typedListFromResponse(response, GraphObject.class);
FacebookRequestError error = response.getError();
if (error != null)
{
// log
logError("failed to get albums", error.getException());
// callback with 'exception'
if (onAlbumsRequestListener != null)
{
onAlbumsRequestListener.onException(error.getException());
}
}
else
{
// callback with 'complete'
if (onAlbumsRequestListener != null)
{
List<Album> albums = new ArrayList<Album>(graphObjects.size());
for (GraphObject graphObject: graphObjects)
{
Album album = Album.create(graphObject);
albums.add(album);
}
onAlbumsRequestListener.onComplete(albums);
}
}
}
});
RequestAsyncTask task = new RequestAsyncTask(request);
task.execute();
// callback with 'thinking'
if (onAlbumsRequestListener != null)
{
onAlbumsRequestListener.onThinking();
}
}
else
{
String reason = Errors.getError(Errors.ErrorMsg.LOGIN);
logError(reason, null);
// callback with 'fail' due to not being loged
if (onAlbumsRequestListener != null)
{
onAlbumsRequestListener.onFail(reason);
}
}
}
/**
*
* Publish {@link Feed} on the wall.<br>
* <br>
*
* <b>Permission:</b><br>
* {@link Permissions#PUBLISH_ACTION}
*
* @param feed The feed to publish. Use {@link Feed.Builder} to create a new <code>Feed</code>
* @param onPublishListener The listener for publishing action
* @see https://developers.facebook.com/docs/howtos/androidsdk/3.0/publish-to-feed/
*/
public void publish(final Feed feed, final OnPublishListener onPublishListener)
{
// if we are logged in
if (isLogin())
{
// if we defined the publish permission
if (mConfiguration.getPublishPermissions().contains(Permissions.PUBLISH_ACTION.getValue()))
{
// callback with 'thinking'
if (onPublishListener != null)
{
onPublishListener.onThinking();
}
/*
* Check if session to facebook has 'publish_action' permission. If not, we will ask user for
* this permission.
*/
if (!getOpenSessionPermissions().contains(Permissions.PUBLISH_ACTION.getValue()))
{
mSessionStatusCallback.mOnReopenSessionListener = new OnReopenSessionListener()
{
@Override
public void onSuccess()
{
publishImpl(feed, onPublishListener);
}
@Override
public void onNotAcceptingPermissions()
{
// this fail can happen when user doesn't accept the publish permissions
String reason = Errors.getError(Errors.ErrorMsg.CANCEL_PERMISSIONS_PUBLISH, String.valueOf(mConfiguration.getPublishPermissions()));
logError(reason, null);
onPublishListener.onFail(reason);
}
};
// extend publish permissions automatically
extendPublishPermissions();
}
else
{
publishImpl(feed, onPublishListener);
}
}
else
{
String reason = Errors.getError(Errors.ErrorMsg.PERMISSIONS_PUBLISH, Permissions.PUBLISH_ACTION.getValue());
logError(reason, null);
// callback with 'fail' due to insufficient permissions
if (onPublishListener != null)
{
onPublishListener.onFail(reason);
}
}
}
else
{
// callback with 'fail' due to not being loged
if (onPublishListener != null)
{
String reason = Errors.getError(Errors.ErrorMsg.LOGIN);
logError(reason, null);
onPublishListener.onFail(reason);
}
}
}
/**
* Publish open graph story.<br>
* <br>
*
* <b>Permission:</b><br>
* {@link Permissions#PUBLISH_ACTION}
*
* @param openGraph
* @param onPublishListener
*/
public void publish(final Story story, final OnPublishListener onPublishListener)
{
if (isLogin())
{
// if we defined the publish permission
if (mConfiguration.getPublishPermissions().contains(Permissions.PUBLISH_ACTION.getValue()))
{
// callback with 'thinking'
if (onPublishListener != null)
{
onPublishListener.onThinking();
}
/*
* Check if session to facebook has 'publish_action' permission. If not, we will ask user for
* this permission.
*/
if (!getOpenSessionPermissions().contains(Permissions.PUBLISH_ACTION.getValue()))
{
mSessionStatusCallback.mOnReopenSessionListener = new OnReopenSessionListener()
{
@Override
public void onSuccess()
{
publishImpl(story, onPublishListener);
}
@Override
public void onNotAcceptingPermissions()
{
// this fail can happen when user doesn't accept the publish permissions
String reason = Errors.getError(Errors.ErrorMsg.CANCEL_PERMISSIONS_PUBLISH, String.valueOf(mConfiguration.getPublishPermissions()));
logError(reason, null);
onPublishListener.onFail(reason);
}
};
// extend publish permissions automatically
extendPublishPermissions();
}
else
{
publishImpl(story, onPublishListener);
}
}
else
{
// callback with 'fail' due to insufficient permissions
if (onPublishListener != null)
{
String reason = Errors.getError(Errors.ErrorMsg.PERMISSIONS_PUBLISH, Permissions.PUBLISH_ACTION.getValue());
logError(reason, null);
onPublishListener.onFail(reason);
}
}
}
else
{
// callback with 'fail' due to not being loged
if (onPublishListener != null)
{
String reason = Errors.getError(Errors.ErrorMsg.LOGIN);
logError(reason, null);
onPublishListener.onFail(reason);
}
}
}
/**
* Publish photo to specific album. You can use {@link #getAlbums(OnAlbumsRequestListener)} to retrieve
* all user's albums.<br>
* <br>
*
* <b>Permission:</b><br>
* {@link Permissions#PUBLISH_STREAM}<br>
* <br>
*
* <b>Important:</b><br>
* - The user must own the album<br>
* - The album should not be full (Max: 200 photos). Check it by {@link Album#getCount()}<br>
* - The app can add photos to the album<br>
* - The privacy setting of the app should be at minimum as the privacy setting of the album (
* {@link Album#getPrivacy()}
*
* @param photo The photo to upload
* @param albumId The album to which the photo should be uploaded
* @param onPublishListener The callback listener
*/
public void publish(final Photo photo, final String albumId, final OnPublishListener onPublishListener)
{
if (isLogin())
{
// if we defined the publish permission
if (mConfiguration.getPublishPermissions().contains(Permissions.PUBLISH_STREAM.getValue()))
{
// callback with 'thinking'
if (onPublishListener != null)
{
onPublishListener.onThinking();
}
/*
* Check if session to facebook has 'publish_action' permission. If not, we will ask user for
* this permission.
*/
if (!getOpenSessionPermissions().contains(Permissions.PUBLISH_STREAM.getValue()))
{
mSessionStatusCallback.mOnReopenSessionListener = new OnReopenSessionListener()
{
@Override
public void onSuccess()
{
publishImpl(photo, albumId, onPublishListener);
}
@Override
public void onNotAcceptingPermissions()
{
// this fail can happen when user doesn't accept the publish permissions
String reason = Errors.getError(Errors.ErrorMsg.CANCEL_PERMISSIONS_PUBLISH, String.valueOf(mConfiguration.getPublishPermissions()));
logError(reason, null);
onPublishListener.onFail(reason);
}
};
// extend publish permissions automatically
extendPublishPermissions();
}
else
{
publishImpl(photo, albumId, onPublishListener);
}
}
else
{
// callback with 'fail' due to insufficient permissions
if (onPublishListener != null)
{
String reason = Errors.getError(Errors.ErrorMsg.PERMISSIONS_PUBLISH, Permissions.PUBLISH_STREAM.getValue());
logError(reason, null);
onPublishListener.onFail(reason);
}
}
}
else
{
// callback with 'fail' due to not being loged
if (onPublishListener != null)
{
String reason = Errors.getError(Errors.ErrorMsg.LOGIN);
logError(reason, null);
onPublishListener.onFail(reason);
}
}
}
/**
* Publish photo to application default album.<br>
* <br>
*
* <b>Permission:</b><br>
* {@link Permissions#PUBLISH_STREAM}<br>
* <br>
*
* <b>Important:</b><br>
* - The album should not be full (Max: 200 photos). Check it by {@link Album#getCount()}<br>
* {@link Album#getPrivacy()}
*
* @param photo The photo to upload
* @param onPublishListener The callback listener
*/
public void publish(final Photo photo, final OnPublishListener onPublishListener)
{
publish(photo, "me", onPublishListener);
}
/**
* Open invite dialog and can add multiple friends
*
* @param message The message inside the dialog. It could be <code>null</code>
* @param onInviteListener The listener. It could be <code>null</code>
*/
public void invite(String message, final OnInviteListener onInviteListener)
{
if (isLogin())
{
Bundle params = new Bundle();
params.putString("message", message);
openInviteDialog(mActivity, params, onInviteListener);
}
else
{
String reason = Errors.getError(Errors.ErrorMsg.LOGIN);
logError(reason, null);
onInviteListener.onFail(reason);
}
}
/**
* Open invite dialog and invite only specific friend
*
* @param to The id of the friend profile
* @param message The message inside the dialog. It could be <code>null</code>
* @param onInviteListener The listener. It could be <code>null</code>
*/
public void invite(String to, String message, final OnInviteListener onInviteListener)
{
if (isLogin())
{
Bundle params = new Bundle();
if (message != null)
{
params.putString("message", message);
}
params.putString("to", to);
openInviteDialog(mActivity, params, onInviteListener);
}
else
{
String reason = Errors.getError(Errors.ErrorMsg.LOGIN);
logError(reason, null);
onInviteListener.onFail(reason);
}
}
/**
* Open invite dialog and invite several specific friends
*
* @param suggestedFriends The ids of friends' profiles
* @param message The message inside the dialog. It could be <code>null</code>
* @param onInviteListener The error listener. It could be <code>null</code>
*/
public void invite(String[] suggestedFriends, String message, final OnInviteListener onInviteListener)
{
if (isLogin())
{
Bundle params = new Bundle();
if (message != null)
{
params.putString("message", message);
}
params.putString("suggestions", TextUtils.join(",", suggestedFriends));
openInviteDialog(mActivity, params, onInviteListener);
}
else
{
String reason = Errors.getError(Errors.ErrorMsg.LOGIN);
logError(reason, null);
onInviteListener.onFail(reason);
}
}
/**
*
* Requests {@link Permissions#PUBLISH_ACTION} and nothing else.
* Useful when you just want to request the action and won't be publishing at the time, but still need the
* updated <b>access token</b> with the permissions (possibly to pass back to your backend).
* You must add {@link Permissions#PUBLISH_ACTION} to your SimpleFacebook configuration before calling this.
*
* <b>Must be logged to use.</b>
*
* @param onPermissionListener The listener for the request permission action
*/
public void requestPublish(final OnPermissionListener onPermissionListener)
{
if (isLogin())
{
if (mConfiguration.getPublishPermissions().contains(Permissions.PUBLISH_ACTION.getValue()))
{
if (onPermissionListener != null)
{
onPermissionListener.onThinking();
}
/*
* Check if session to facebook has 'publish_action' permission. If not, we will ask user for
* this permission.
*/
if (!getOpenSessionPermissions().contains(Permissions.PUBLISH_ACTION.getValue()))
{
mSessionStatusCallback.mOnReopenSessionListener = new OnReopenSessionListener()
{
@Override
public void onSuccess()
{
if (onPermissionListener != null)
{
onPermissionListener.onSuccess(getAccessToken());
}
}
@Override
public void onNotAcceptingPermissions()
{
// this fail can happen when user doesn't accept the publish permissions
String reason = Errors.getError(Errors.ErrorMsg.CANCEL_PERMISSIONS_PUBLISH, String.valueOf(mConfiguration.getPublishPermissions()));
logError(reason, null);
if (onPermissionListener != null)
{
onPermissionListener.onFail(reason);
}
}
};
// extend publish permissions automatically
extendPublishPermissions();
}
else
{
// We already have the permission.
if (onPermissionListener != null)
{
onPermissionListener.onSuccess(getAccessToken());
}
}
}
}
else
{
// callback with 'fail' due to not being loged
if (onPermissionListener != null)
{
String reason = Errors.getError(Errors.ErrorMsg.LOGIN);
logError(reason, null);
onPermissionListener.onFail(reason);
}
}
}
/**
* Call this inside your activity in {@link android.app.Activity#onActivityResult} method
*
* @param activity
* @param requestCode
* @param resultCode
* @param data
* @return
*/
public boolean onActivityResult(Activity activity, int requestCode, int resultCode, Intent data)
{
if (Session.getActiveSession() != null)
{
return Session.getActiveSession().onActivityResult(activity, requestCode, resultCode, data);
}
else
{
return false;
}
}
/**
* Indicate if you are logged in or not.
*
* @return <code>True</code> if you is logged in, otherwise return <code>False</code>
*/
public boolean isLogin()
{
Session session = Session.getActiveSession();
if (session == null)
{
if (session == null)
{
session = new Session.Builder(mActivity.getApplicationContext())
.setApplicationId(mConfiguration.getAppId())
.build();
}
Session.setActiveSession(session);
}
if (session.isOpened())
{
// mSessionNeedsToReopen = false;
return true;
}
/*
* Check if we can reload the session when it will be nessecary. We won't do it now.
*/
if (session.getState().equals(SessionState.CREATED_TOKEN_LOADED))
{
List<String> permissions = session.getPermissions();
if (permissions.containsAll(mConfiguration.getReadPermissions()))
{
reopenSession();
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
/**
* Get access token of open session
*
* @return
*/
public String getAccessToken()
{
Session session = getOpenSession();
if (session != null)
{
return session.getAccessToken();
}
return null;
}
/**
* Get open session
*
* @return the open session
*/
public static Session getOpenSession()
{
return Session.getActiveSession();
}
/**
* Clean all references like Activity to prevent memory leaks
*/
public void clean()
{
mActivity = null;
}
private static void publishImpl(Feed feed, final OnPublishListener onPublishListener)
{
Session session = getOpenSession();
Request request = new Request(session, "me/feed", feed.getBundle(), HttpMethod.POST, new Request.Callback()
{
@Override
public void onCompleted(Response response)
{
GraphObject graphObject = response.getGraphObject();
if (graphObject != null)
{
JSONObject graphResponse = graphObject.getInnerJSONObject();
String postId = null;
try
{
postId = graphResponse.getString("id");
}
catch (JSONException e)
{
// log
logError("JSON error", e);
}
FacebookRequestError error = response.getError();
if (error != null)
{
// log
logError("Failed to publish", error.getException());
// callback with 'exception'
if (onPublishListener != null)
{
onPublishListener.onException(error.getException());
}
}
else
{
// callback with 'complete'
if (onPublishListener != null)
{
onPublishListener.onComplete(postId);
}
}
}
else
{
// log
logError("The GraphObject in Response of publish action has null value. Response=" + response.toString(), null);
if (onPublishListener != null)
{
onPublishListener.onComplete("0");
}
}
}
});
RequestAsyncTask task = new RequestAsyncTask(request);
task.execute();
}
private static void publishImpl(Story story, final OnPublishListener onPublishListener)
{
Session session = getOpenSession();
String appNamespace = mConfiguration.getNamespace();
Request request = new Request(session, story.getGraphPath(appNamespace), story.getActionBundle(), HttpMethod.POST, new Request.Callback()
{
@Override
public void onCompleted(Response response)
{
GraphObject graphObject = response.getGraphObject();
if (graphObject != null)
{
JSONObject graphResponse = graphObject.getInnerJSONObject();
String postId = null;
try
{
postId = graphResponse.getString("id");
}
catch (JSONException e)
{
// log
logError("JSON error", e);
}
FacebookRequestError error = response.getError();
if (error != null)
{
// log
logError("Failed to publish", error.getException());
// callback with 'exception'
if (onPublishListener != null)
{
onPublishListener.onException(error.getException());
}
}
else
{
// callback with 'complete'
if (onPublishListener != null)
{
onPublishListener.onComplete(postId);
}
}
}
else
{
// log
logError("The GraphObject in Response of publish action has null value. Response=" + response.toString(), null);
if (onPublishListener != null)
{
onPublishListener.onComplete("0");
}
}
}
});
RequestAsyncTask task = new RequestAsyncTask(request);
task.execute();
}
private static void publishImpl(Photo photo, String albumId, final OnPublishListener onPublishListener)
{
Session session = getOpenSession();
Request request = new Request(session, albumId + "/photos", photo.getBundle(), HttpMethod.POST, new Request.Callback()
{
@Override
public void onCompleted(Response response)
{
GraphObject graphObject = response.getGraphObject();
if (graphObject != null)
{
JSONObject graphResponse = graphObject.getInnerJSONObject();
String postId = null;
try
{
postId = graphResponse.getString("id");
}
catch (JSONException e)
{
// log
logError("JSON error", e);
}
FacebookRequestError error = response.getError();
if (error != null)
{
// log
logError("Failed to publish", error.getException());
// callback with 'exception'
if (onPublishListener != null)
{
onPublishListener.onException(error.getException());
}
}
else
{
// callback with 'complete'
if (onPublishListener != null)
{
onPublishListener.onComplete(postId);
}
}
}
else
{
// log
logError("The GraphObject in Response of publish action has null value. Response=" + response.toString(), null);
if (onPublishListener != null)
{
onPublishListener.onComplete("0");
}
}
}
});
RequestAsyncTask task = new RequestAsyncTask(request);
task.execute();
}
private void openInviteDialog(Activity activity, Bundle params, final OnInviteListener onInviteListener)
{
mDialog = new WebDialog.RequestsDialogBuilder(activity, Session.getActiveSession(), params).
setOnCompleteListener(new WebDialog.OnCompleteListener()
{
@Override
public void onComplete(Bundle values, FacebookException error)
{
if (error != null)
{
// log
logError("Failed to invite", error);
if (error instanceof FacebookOperationCanceledException)
{
onInviteListener.onCancel();
}
else
{
if (onInviteListener != null)
{
onInviteListener.onException(error);
}
}
}
else
{
Object object = values.get("request");
if (object == null)
{
onInviteListener.onCancel();
}
else
{
List<String> invitedFriends = fetchInvitedFriends(values);
onInviteListener.onComplete(invitedFriends);
}
}
mDialog = null;
}
}).build();
Window dialogWindow = mDialog.getWindow();
dialogWindow.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
mDialog.show();
}
/**
* Fetch invited friends from response bundle
*
* @param values
* @return list of invited friends
*/
@SuppressLint("DefaultLocale")
private static List<String> fetchInvitedFriends(Bundle values)
{
List<String> friends = new ArrayList<String>();
int size = values.size();
friends.add(values.getString("request"));
int numOfFriends = size-1;
if (numOfFriends > 0)
{
for (int i = 0; i < numOfFriends; i++)
{
String key = String.format("to[%d]", i);
String friendId = values.getString(key);
if (friendId != null)
{
friends.add(friendId);
}
}
}
return friends;
}
/**
* Get permissions that are accepted by user for current token
*
* @return the list of accepted permissions
*/
private static List<String> getOpenSessionPermissions()
{
return getOpenSession().getPermissions();
}
private void openSession(Session session, boolean isRead)
{
Session.OpenRequest request = new Session.OpenRequest(mActivity);
if (request != null)
{
request.setDefaultAudience(mConfiguration.getSessionDefaultAudience());
request.setPermissions(mConfiguration.getReadPermissions());
request.setLoginBehavior(mConfiguration.getSessionLoginBehavior());
/*
* In case there are also PUBLISH permissions, then we would ask for these permissions second time
* (after, user accepted the read permissions)
*/
if (mConfiguration.hasPublishPermissions())
{
mSessionStatusCallback.askPublishPermissions();
}
if (isRead)
{
// Open session with read permissions
session.openForRead(request);
}
else
{
session.openForPublish(request);
}
}
}
/**
* Call this method only if session really needs to be reopened for read or for publish. <br>
* <br>
*
* <b>Important</b><br>
* Any open method must be called at most once, and cannot be called after the Session is closed. Calling
* the method at an invalid time will result in {@link UnsupportedOperationException}.
*/
private void reopenSession()
{
Session session = Session.getActiveSession();
if (session != null && session.getState().equals(SessionState.CREATED_TOKEN_LOADED))
{
List<String> permissions = session.getPermissions();
if (permissions.containsAll(mConfiguration.getPublishPermissions()))
{
openSession(session, false);
}
else if (permissions.containsAll(mConfiguration.getReadPermissions()))
{
openSession(session, true);
}
}
}
/**
* Extend and ask user for PUBLISH permissions
*/
private static void extendPublishPermissions()
{
Session session = Session.getActiveSession();
Session.NewPermissionsRequest request = new Session.NewPermissionsRequest(mActivity, mConfiguration.getPublishPermissions());
session.requestNewPublishPermissions(request);
}
/**
* Helper method
*/
private static <T extends GraphObject> List<T> typedListFromResponse(Response response, Class<T> clazz)
{
GraphMultiResult multiResult = response.getGraphObjectAs(GraphMultiResult.class);
if (multiResult == null)
{
return null;
}
GraphObjectList<GraphObject> data = multiResult.getData();
if (data == null)
{
return null;
}
return data.castToListOf(clazz);
}
private class SessionStatusCallback implements Session.StatusCallback
{
private boolean mAskPublishPermissions = false;
private boolean mDoOnLogin = false;
OnLoginListener mOnLoginListener = null;
OnLogoutListener mOnLogoutListener = null;
OnReopenSessionListener mOnReopenSessionListener = null;
@Override
public void call(Session session, SessionState state, Exception exception)
{
/*
* These are already authorized permissions
*/
List<String> permissions = session.getPermissions();
if (exception != null)
{
// log
logError("SessionStatusCallback: exception=", exception);
if (exception instanceof FacebookOperationCanceledException)
{
/*
* If user canceled the read permissions dialog
*/
if (permissions.size() == 0)
{
mOnLoginListener.onNotAcceptingPermissions();
}
else
{
/*
* User canceled the WRITE permissions. We do nothing here. Once the user will try to
* do some action that require WRITE permissions, the dialog will be shown
* automatically.
*/
}
}
else
{
mOnLoginListener.onException(exception);
}
}
// log
logInfo("SessionStatusCallback: state=" + state.name() + ", session=" + String.valueOf(session));
switch (state)
{
case CLOSED:
mOnLogoutListener.onLogout();
break;
case CLOSED_LOGIN_FAILED:
break;
case CREATED:
break;
case CREATED_TOKEN_LOADED:
break;
case OPENING:
mOnLoginListener.onThinking();
break;
case OPENED:
/*
* Check if we came from publishing actions where we ask again for publish permissions
*/
if (mOnReopenSessionListener != null)
{
mOnReopenSessionListener.onNotAcceptingPermissions();
mOnReopenSessionListener = null;
}
/*
* Check if WRITE permissions were also defined in the configuration. If so, then ask in
* another dialog for WRITE permissions.
*/
else if (mAskPublishPermissions && session.getState().equals(SessionState.OPENED))
{
if (mDoOnLogin)
{
/*
* If user didn't accept the publish permissions, we still want to notify about
* complete
*/
mDoOnLogin = false;
mOnLoginListener.onLogin();
}
else
{
mDoOnLogin = true;
extendPublishPermissions();
mAskPublishPermissions = false;
}
}
else
{
mOnLoginListener.onLogin();
}
break;
case OPENED_TOKEN_UPDATED:
/*
* Check if came from publishing actions and we need to reask for publish permissions
*/
if (mOnReopenSessionListener != null)
{
mOnReopenSessionListener.onSuccess();
mOnReopenSessionListener = null;
}
else if (mDoOnLogin)
{
mDoOnLogin = false;
mOnLoginListener.onLogin();
}
break;
default:
break;
}
}
/**
* If we want to open another dialog with publish permissions just after showing read permissions,
* then this method should be called
*/
public void askPublishPermissions()
{
mAskPublishPermissions = true;
}
}
private static void logInfo(String message)
{
Logger.logInfo(SimpleFacebook.class, message);
}
private static void logError(String error, Throwable throwable)
{
if (throwable != null)
{
Logger.logError(SimpleFacebook.class, error, throwable);
}
else
{
Logger.logError(SimpleFacebook.class, error);
}
}
private interface OnReopenSessionListener
{
void onSuccess();
void onNotAcceptingPermissions();
}
/**
* On my profile request listener
*
* @author sromku
*
*/
public interface OnProfileRequestListener extends OnActionListener
{
void onComplete(Profile profile);
}
/**
* On friends request listener
*
* @author sromku
*
*/
public interface OnFriendsRequestListener extends OnActionListener
{
void onComplete(List<Profile> friends);
}
public interface OnPlaceRequestListener extends OnActionListener
{
void onComplete(List<Profile> friends);
}
/**
* On albums request listener
*
* @author sromku
*
*/
public interface OnAlbumsRequestListener extends OnActionListener
{
void onComplete(List<Album> albums);
}
/**
* On publishing action listener
*
* @author sromku
*
*/
public interface OnPublishListener extends OnActionListener
{
void onComplete(String id);
}
/**
* On login/logout actions listener
*
* @author sromku
*/
public interface OnLoginListener extends OnActionListener
{
/**
* If user performed {@link FacebookTools#login(android.app.Activity)} action, this callback method will be
* invoked
*/
void onLogin();
/**
* If user pressed 'cancel' in READ (First) permissions dialog
*/
void onNotAcceptingPermissions();
}
public interface OnLogoutListener extends OnActionListener
{
/**
* If user performed {@link FacebookTools#logout()} action, this callback method will be invoked
*/
void onLogout();
}
/**
* On permission listener - If the App must request a specific permission (only to obtain the new Access Token)
*
* @author Gryzor
*
*/
public interface OnPermissionListener extends OnActionListener
{
/**
* If the permission was granted, this callback is invoked.
*/
void onSuccess(final String accessToken);
/**
* If user pressed 'cancel' in PUBLISH permissions dialog
*/
void onNotAcceptingPermissions();
}
/**
* On invite action listener
*
* @author sromku
*
*/
public interface OnInviteListener extends OnErrorListener
{
void onComplete(List<String> invitedFriends);
void onCancel();
}
/**
* General interface in this simple sdk
*
* @author sromku
*
*/
public interface OnActionListener extends OnErrorListener
{
void onThinking();
}
public interface OnErrorListener
{
void onException(Throwable throwable);
void onFail(String reason);
}
}