Facebook session singleton usage - android

I'm working on Facebookintegration for my Android app and to reduce the hassle, I'm trying to create a singleton class to seperate the facebook logic from the main logic of my application.
I'd like to be able to call this class from different parts of my application (entrypoint, asynctasks, ...). At this moment, I call the initialize function first with the applicationcontext as context. After this initialization I'd like to be able to call eg. FacebookSession.getInstance().login() and FacebookSession.getInstance().requestWithQuery(examplestring).
The problem is that I just can't find decent documentation. Even the facebook official samples uses the old Facebook object, which is deprecated. I threw this class together with different parts of code I could find and I came up with this:
package be.ugent.zeus.hydra.util.facebook;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import com.facebook.HttpMethod;
import com.facebook.Request;
import com.facebook.Session;
import com.facebook.SessionState;
public class FacebookSession {
private static final String APP_ID = "hidden";
private static final String ACCESS_TOKEN = "hidden";
private static FacebookSession fbSession;
public static final String TAG = "FACEBOOK";
private Activity activity;
private static Session.StatusCallback statusCallback = new SessionStatusCallback();
protected FacebookSession() {
}
public static void initialize(Context context) {
Session session = Session.getActiveSession();
if (session == null) {
session = new Session.Builder(context)
.setApplicationId(APP_ID)
.build();
}
Session.setActiveSession(session);
}
public static FacebookSession getInstance() {
if (fbSession == null) {
fbSession = new FacebookSession();
}
return fbSession;
}
public Session getSession() {
return Session.getActiveSession();
}
public void tryOpenSession(Activity activity) {
Session session = Session.openActiveSession(activity, false, statusCallback);
Session.setActiveSession(session);
}
public void login(Activity activity) {
Session session = Session.getActiveSession();
if (!session.isOpened() && !session.isClosed()) {
Session.openActiveSession(activity, true, statusCallback);
}
}
public void logout() {
Session session = Session.getActiveSession();
if (!session.isClosed()) {
session.closeAndClearTokenInformation();
}
}
public boolean isOpen() {
Session session = Session.getActiveSession();
return session.isOpened();
}
public Request requestWithQuery(String query) {
Bundle bundle = new Bundle();
bundle.putString("q", query);
return requestWithGraphPath("/fql", bundle, "GET");
}
private Request requestWithGraphPath(String path, Bundle bundle, String method) {
// Get the session
Session session = Session.getActiveSession();
// No session? Try to open one without user interaction
if (session == null) {
session = Session.openActiveSession(activity, false, statusCallback);
Session.setActiveSession(session);
}
// Still no session? Use the default key
if (session == null) {
bundle.putString("access_token", ACCESS_TOKEN);
}
return new Request(session, path, bundle, HttpMethod.valueOf(method));
}
private static class SessionStatusCallback implements Session.StatusCallback {
#Override
public void call(Session session, SessionState state, Exception exception) {
Log.i(TAG, state.toString());
switch (state) {
case CLOSED:
case CLOSED_LOGIN_FAILED:
session.closeAndClearTokenInformation();
break;
default:
break;
}
}
}
}
The queryingpart works pretty neat, but the sessions don't. For example, if I call the login function, the active session is null, however I did initialize the Session with the applicationcontext in the entry point of my application.
Concrete, I have this question:
Is this the 'good' way of (trying) to keep track of the Session state? Can it be done this way? If so, what I'm doing wrong that it just doesn't work?
And as a follow-up question:
If not, then what is the correct way to keep track of a Session during either the application life time and otherwise between application sessions?
Thanks in advance.

I'm not 100% certain I understand the problem, but FWIW, the following works for me:
In an Activity somewhere:
Session.openActiveSession(MyActivity.this, true, new Session.StatusCallback() {
#Override
public void call(Session session, SessionState state, Exception exception) {
if(session.isOpened()) {
Log.d(TAG, "FB session opened successfully");
} else {
Log.d(TAG, "FB session not opened ... " + exception.toString());
}
}
});
And then, somewhere else (different Activity, AsyncTask, whatever) access the session with Session.getActiveSession():
Session session = Session.getActiveSession();
if(session!=null && session.isOpened()) {
Request.executeMyFriendsRequestAsync(session, new Request.GraphUserListCallback() {
#Override
public void onCompleted(List<GraphUser> users, Response response) {
for(GraphUser u : users) {
Log.d(TAG, ""+u.getId()+ ": " + u.getName());
}
}
});
}
So it seems to me that the Session class from Facebook gives you what you want (access to the active session "singleton-style")
HTH

Related

Android & Facebook SDK: Obtain user data without Login Button

I am losing my mind trying to integrate Facebook with an app. First of all, Fb's SDK is terrible and its making everything crash since I included it. Anyway, I am trying to obtain user data from Facebook, just his/her name, user id and email; however, I can't use the Login Button because it doesn't support Nested Fragments and it uses UiLifecycleHelper which keeps a Session open and keeps executing a callback that I only want to call once.
I don't need to keep a Session open; I will sporadically open Sessions the first time the user uses the app and if he/she wants to publish something (very rare).
So far I have tried using the Login Button, performing a simple Request and combining both. However, it seems that the SDK as a whole doesn't play very well with Nested Fragment.
This was my last attempt at making this work (these two methods are inside a Fragment. Once a button is pressed, performFacebookLogin is executed):
public void performFacebookLogin() {
Session.openActiveSession(getActivity(), true, Arrays.asList("email"), new Session.StatusCallback() {
#Override
public void call(Session session, SessionState state, Exception exception) {
if (session.isOpened()) {
Log.d("FACEBOOK", "Session has been opened");
Request.newMeRequest(session, new Request.GraphUserCallback() {
#Override
public void onCompleted(GraphUser user, Response response) {
Log.d("FACEBOOK", "onCompleted");
if (user != null) {
Log.d("DBG", buildUserInfoDisplay(user));
}
}
}).executeAsync();
}else{
//TODO: ERROR
Log.e("FACEBOOK", "Session could not be opened");
}
}
});
}
private String buildUserInfoDisplay(GraphUser user) {
StringBuilder userInfo = new StringBuilder("");
userInfo.append(String.format("Name: %s\n\n",
user.getName()));
userInfo.append(String.format("Email: %s\n\n",
user.getProperty("email")));
userInfo.append(String.format("ID: %s\n\n",
user.getId()));
return userInfo.toString();
}
So, what happens? The dialog prompt is shown in order to login using your Facebook account. But, once you press Login and the dialog disappears, nothing happens. Nothing is shown in the LogCat. I think is a problem with the onActivityResult method, because the callback is never executed. I tried re-adding the UiLifecycleHelper, but it ends up making unwanted calls to the callback (I only want to call this method once).
You are correct, you need to plumb the result through to the active Session for your callback to be activated. In your activities onActivityForResult method, call the active sessions onActivityResult, similar to this: https://github.com/facebook/facebook-android-sdk/blob/master/facebook/src/com/facebook/UiLifecycleHelper.java#L156-159
Session session = Session.getActiveSession();
if (session != null) {
session.onActivityResult(activity, requestCode, resultCode, data);
}
That would get your callback working.
So, I managed to achieve a modular approach to my problem: I created an activity that encapsulated the connection to Facebook's SDK and returns it via onActivityResult. Unfortunately, I haven't found a way to return the result to a nested fragment directly.
On a side note, you can make the activity transparent to avoid a black screen and add more permissions if you need them. Also, you can remove the onStop method if you want to keep the Session active.
Here's the code:
public class FacebookAccessActivity extends ActionBarActivity {
public static final String PARAM_PROFILE = "public_profile";
public static final String PARAM_EMAIL = "email";
public static final String PARAM_FIRSTNAME = "fname";
public static final String PARAM_LASTNAME = "lname";
public static final String PARAM_GENDER = "gender";
public static final String PARAM_BDAY = "user_birthday";
public static final String PARAM_ID = "id";
private static Session session = null;
private List<String> permissions = Arrays.asList(PARAM_EMAIL, PARAM_PROFILE, PARAM_BDAY);
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportActionBar().hide();
setContentView(R.layout.view_empty);
session = Session.getActiveSession();
if (session != null)
session.closeAndClearTokenInformation();
Session.openActiveSession(this, true, permissions, new Session.StatusCallback() {
#Override
public void call(Session session, SessionState state, Exception exception) {
if (exception != null || state == SessionState.CLOSED_LOGIN_FAILED) {
exception.printStackTrace();
setResult(RESULT_CANCELED);
finish();
} else if (session.isOpened()) {
Request.newMeRequest(session, new Request.GraphUserCallback() {
#Override
public void onCompleted(GraphUser user, Response response) {
if (user != null) {
Intent i = new Intent();
i.putExtra(PARAM_FIRSTNAME, user.getFirstName());
i.putExtra(PARAM_LASTNAME, user.getLastName());
i.putExtra(PARAM_ID, user.getId());
i.putExtra(PARAM_GENDER, (String) user.getProperty(PARAM_GENDER));
i.putExtra(PARAM_BDAY, user.getBirthday());
for (String s : permissions)
i.putExtra(s, (String) user.getProperty(s));
setResult(RESULT_OK, i);
finish();
}
}
}).executeAsync();
}
}
});
}
#Override
protected void onStop() {
super.onStop();
if (session != null)
session.closeAndClearTokenInformation();
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(resultCode == RESULT_CANCELED ||
!Session.getActiveSession().onActivityResult(this, requestCode, resultCode, data)) {
setResult(RESULT_CANCELED);
finish();
}
}

Why Facebook Session state is always close?

I want to add Facebook login functionality into my app. Please note that:
I don't want to use Facebook login button widget.
I copy pasted my code that was working correctly last year (from my other project) into my new project.
I checked Session page and I guess I have no problem.
The only difference was Request.executeMeRequestAsync() hss changed to Request.newMeRequest(). I did this change but seems my session is always close although I could see Session.setActiveSession(session); method runs by debug of project. So, have no idea really why session is always close in call() method.
Any idea would be appreciated. Thanks.
My code:
public class FacebookLogin extends FragmentActivity
{
private static final String TAG = "FacebookLogin";
private static final List<String> READ_PERMISSIONS =
Arrays.asList("email", "user_about_me", "user_photos");
// private static final List<String> WRITE_PERMISSIONS = Arrays.asList("");
private final Session.StatusCallback statusCallback = new Session.StatusCallback()
{
#Override
public void call(final Session session, SessionState state, Exception exception)
{
if (session.isOpened())
{
// make request to the /me API
Request request = Request.newMeRequest(session, new Request.GraphUserCallback()
{
// callback after Graph API response with user object
#Override
public void onCompleted(GraphUser user, Response response)
{
if (user != null)
{
MyLog.d(TAG, "User name: " + user.getName() + "!, Login successfully :)");
MyLog.d(TAG, "User id: " + user.getId());
MyLog.d(TAG, "Access token is: " + session.getAccessToken());
MyLog.d(TAG, "Application id: " + session.getApplicationId());
MyLog.d(TAG, "JSON Object: " + user.getInnerJSONObject());
SpStorage.setKeyFacebook(FacebookLogin.this, session.getAccessToken());
SpStorage.setFacebookUserId(FacebookLogin.this, user.getId());
// erson person = parseJSON(user.getInnerJSONObject().toString());
// registerUser();
// Close activity
FacebookLogin.this.finish();
}
}
});
request.executeAsync();
}
else if (state.isClosed()) {
MyLog.d(TAG, "Facebook session closed");
}
}
};
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Find device hash key (should not be used in production)
// printHashKey();
// start Facebook Login
openActiveSession(this, true, statusCallback, READ_PERMISSIONS);
}
private static Session openActiveSession(Activity activity, boolean allowLoginUI,
Session.StatusCallback callback, List<String> permissions)
{
Session.OpenRequest openRequest = new Session
.OpenRequest(activity)
.setPermissions(permissions)
.setCallback(callback);
Session session = new Session.Builder(activity).build();
if (SessionState.CREATED_TOKEN_LOADED.equals(session.getState()) || allowLoginUI)
{
Session.setActiveSession(session);
session.openForRead(openRequest);
return session;
}
return null;
}
}
What I get in logcat:
Facebook session closed
You need to override the onActivityResult method in your Fragment class and pass it on to the session in order to complete the session transitions.
This needs to happen regardless of whether you're using the UiLifecycleHelper or not.
You need to override onActivityResult and pass the values to the facebook SDK.
From Facebook's SDK docs:
To ensure that the sessions are set up correctly, your fragment must
override the fragment lifecycle methods: onCreate(), onResume(),
onPause(), onDestroy(), onActivityResult() and onSaveInstanceState()
and call the corresponding UiLifecycleHelper methods. For example,
calling the onCreate() method in the UiLifecycleHelper object creates
the Facebook session and opens it automatically if a cached token is
available.
Recently Facebook is using API V2.2 and it has deleted some methods.
recently Before 1 week i have done some for getting user information.
Which may be helping you.
public void getProfileData(View button){
Session activeSession = Session.getActiveSession();
new Request(
activeSession,
"me",
null,
HttpMethod.GET,
new Request.Callback() {
public void onCompleted(Response response) {
/* handle the result */
try
{ GraphObject go = response.getGraphObject();
JSONObject jso = go.getInnerJSONObject();
String name23 = jso.getString("name");
Log.e("Name response",""+name23);
name = name23;
Toast.makeText(getApplicationContext(), "Name: " + name , Toast.LENGTH_LONG).show();
}
catch ( Throwable t )
{
t.printStackTrace();
}
String name = response.toString();
Log.e("Name request",""+name);
}
}
).executeAsync();
}

How to share text message on facebook in android programatically

I want to share some text as well as one picture from my app to facebook wall programatically.
I already registered my android app on facebook developer site.
I am using following code for posting the text but it is not working.It is also not giving me any error so I am totally confused what is happening inside.
public class FacebookManager {
private Activity activity;
private Session.StatusCallback statusCallback;
public UiLifecycleHelper uiHelper;
public FacebookManager(final Activity activity) {
this.activity = activity;
statusCallback = new ShareStatusCallback();
uiHelper = new UiLifecycleHelper(activity, null);
}
public void share() {
Session session = Session.getActiveSession();
if (!session.isOpened() && !session.isClosed()) {
session.openForPublish(new Session.OpenRequest(activity).setCallback(statusCallback).setPermissions("publish_actions"));
} else {
Session.openActiveSession(activity, true, statusCallback);
}
}
public void initFbSession(Bundle savedInstanceState) {
Session session = Session.getActiveSession();
if (session == null) {
if (savedInstanceState != null) {
session = Session.restoreSession(activity, null, null, savedInstanceState);
}
if (session == null) {
session = new Session(activity);
}
Session.setActiveSession(session);
}
}
private class ShareStatusCallback implements Session.StatusCallback {
#Override
public void call(Session session, SessionState state, Exception exception) {
if (!session.isOpened()) {
return;
}
if (FacebookDialog.canPresentShareDialog(activity, FacebookDialog.ShareDialogFeature.SHARE_DIALOG)) {
FacebookDialog shareDialog = new FacebookDialog.ShareDialogBuilder(activity)
.setDescription("Test Message ")
//.setPicture("")
.build();
uiHelper.trackPendingDialogCall(shareDialog.present());
} else {
Bundle params = new Bundle();
params.putString("description", "Test Message");
// params.putString("picture", "");
WebDialog feedDialog = (
new WebDialog.FeedDialogBuilder(activity,
Session.getActiveSession(),
params)).build();
feedDialog.show();
}
}
}
}
Does it required any configuration for android app on facebook developer site to use share functionality ?
Please help me out for this.
setDescription only applies if you have a link (via setLink).
You cannot prefill the actual message itself, that's against Facebook's platform policies (the user must type in the message themselves).
We will update the javadocs so that it's more clear.

Facebook session state OPENING

I can login to my app then share something. This needs an OPENED session state. However, when I am not logged in, then I want to share something, I need to open the session. I am using a ViewPager so e.g. when I go from one page to another and this code
Session.openActiveSession(getActivity(), true, new StatusCallback() {
#Override
public void call(Session session, SessionState state, Exception exception) {
}
});
is in the beginning of the code, then the session becomes active, and I get automatically logged in, which is wrong! That's why I put this code block into an onClickListener, so I only want to open the session if I click the share button in my app:
if (session != null && session.isOpened()) {
publishFeedDialog();
}
else {
Session.openActiveSession(getActivity(), true, new StatusCallback() {
#Override
public void call(Session session, SessionState state, Exception exception) {
}
});
publishFeedDialog();
}
private void publishFeedDialog() {
session = Session.getActiveSession();
Log.i("TAG", session.getState() + ""); //OPENING
WebDialog feedDialog = (
new WebDialog.FeedDialogBuilder(getActivity(),
Session.getActiveSession(),
params))
.setOnCompleteListener(new OnCompleteListener() {
#Override
public void onComplete(Bundle values,
FacebookException error) {
if (error == null) {
final String postId = values.getString("post_id");
if (postId != null) {
} else {
// User clicked the Cancel button
}
} else if (error instanceof FacebookOperationCanceledException) {
} else {
// Generic, ex: network error
}
}
})
.build();
feedDialog.show();
}
The error:
Attempted to use a session that was not open.
So I open the session in vain, because it is still OPENING when the WebDialog should appear.
Please help.
For me it works like this:
if (Session.getActiveSession() != null && Session.getActiveSession().isOpened()) {
publishFeedDialog();
}
else {
Session session = Session.getActiveSession();
if (!session.isOpened() && !session.isClosed()) {
// List<String> permissions = new ArrayList<String>();
// permissions.add("email");
session.openForRead(new Session.OpenRequest(this)
// .setPermissions(permissions)
.setCallback(mFacebookCallback));
} else {
Session.openActiveSession(getActivity(), true, mFacebookCallback);
}
}`
where callback is
private Session.StatusCallback mFacebookCallback = new Session.StatusCallback() {
#Override
public void
call(final Session session, final SessionState state, final Exception exception) {
if (state.isOpened()) {
String facebookToken = session.getAccessToken();
Log.i("MainActivityFaceBook", facebookToken);
Request.newMeRequest(session, new Request.GraphUserCallback() {
#Override
public void onCompleted(GraphUser user,
com.facebook.Response response) {
publishFeedDialog();
}
}).executeAsync();
Prefs.setStringProperty(getActivity(), R.string.key_prefs_facebook_token, facebookToken);
}
}
};
Try to call publishFeedDialog() in CallBack onCompleted
Try calling publishFeedDialog() from inside call() after you check for the status of the session (do not use session.isOpened(), use state.isOpened() instead). Checking the state ensures that your Session is in an open state and that it can also be used to execute requests. The StatusCallback can be called multiple times until the Session is actually open, that is why you should check the status before sending any request.
If you are opening a session from a fragment use the next openActiveSession method implementation.
openActiveSession(Context context, Fragment fragment, boolean allowLoginUI, Session.StatusCallback callback)
This works for me.

Facebook SDK 3.0 Android - Token missing

I'm trying to use the facebook authentification in my app but I'm not able to login because the token is missing. In the LogCat I have a loop : "[Tag]System.out [Text]doLogin:".
This happens before the Facebook dialog, and when it appears I click on "Ok" but nothing happens.
Here is my code:
private SharedPreferences.Editor loginPrefsEditor;
private Session.StatusCallback statusCallback = new SessionStatusCallback();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fb_auth);
choosePseudoButton = (Button) findViewById(R.id.buttonChoosePseudoFb);
progressBarFb = (ProgressBar) findViewById(R.id.progressBarFbLoad);
textViewFbInfo = (TextView) findViewById(R.id.currentInfoFb);
editTextFbPseudo = (EditText) findViewById(R.id.editTextPseudoFb);
/*
* Get existing access_token if any
*/
mPrefs = getPreferences(MODE_PRIVATE);
// start Facebook Login
Session.openActiveSession(this, true, new Session.StatusCallback() {
public void call(final Session session, SessionState state, Exception exception) {
if (session.isOpened()) {
Request.executeMeRequestAsync(session, new Request.GraphUserCallback() {
public void onCompleted(GraphUser user, Response response) {
if (user != null) {
System.out.println("onCreate");
fbToken = session.getAccessToken();
SharedPreferences.Editor editor = mPrefs.edit();
editor.putString("access_token", fbToken);
JSONRequest jr = (JSONRequest) new JSONRequest().execute();
}
}
});
}
else {
doLogin();
}
}
});
}
private void doLogin() {
Session session = Session.getActiveSession();
if (!session.isOpened() && !session.isClosed()) {
session.openForRead(new Session.OpenRequest(this).setCallback(statusCallback));
} else {
Session.openActiveSession(this, true, statusCallback);
}
fbToken = session.getAccessToken();
System.out.println("doLogin: "+fbToken);
JSONRequest jr = (JSONRequest) new JSONRequest().execute();
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Session.getActiveSession().onActivityResult(this, requestCode, resultCode, data);
}
private void updateView() {
Session session = Session.getActiveSession();
if (session.isOpened()) {
fbToken = session.getAccessToken();
System.out.println("updateView: "+fbToken);
JSONRequest jr = (JSONRequest) new JSONRequest().execute();
}
else {
doLogin();
}
}
private class SessionStatusCallback implements Session.StatusCallback {
public void call(Session session, SessionState state, Exception exception) {
// Respond to session state changes, ex: updating the view
if( session.isOpened() ){
Request.executeMeRequestAsync(session, new Request.GraphUserCallback() {
public void onCompleted(GraphUser user, Response response) {
}
});
}
updateView();
}
}
Several things to note:
Session opening happens asynchronously, meaning that when you call Session.openActiveSession, it does not (always) immediately open the session (it has to call into another activity, wait for user input, etc). So your line
fbToken = session.getAccessToken();
will almost always return null.
Secondly, there are a few states a Session can be in before it gets to the OPENED state, so in your SessionStatusCallback, you should probably move the updateView() call to be inside the if(session.isOpened()) check.
Lastly, you're calling Session.openActiveSession in the onCreate method, and in that callback, you're always calling doLogin() if the session is not opened. But like I said before, the session can transition to multiple states before it's OPENED, so you're in fact calling doLogin multiple times. Remove that doLogin() call.

Categories

Resources