I'm using FB API 3.0 for Android, and I'm trying to set the session in proper way.
But, it seems I'm doing something wrong, or to say I'm missing something.
In my launcher activity, I check if there is a session already and if it does not exist I create new one (as in FB example):
Settings.addLoggingBehavior(LoggingBehaviors.INCLUDE_ACCESS_TOKENS);
Session session = Session.getActiveSession();
if (session != null){
String sesija = session.toString();
Log.w ("ss", sesija);}
if (session == null) {
if (savedInstanceState != null) {
session = Session.restoreSession(this, null, MyGlobals.INSTANCE.statusCallback, savedInstanceState);
Log.w("No Session found", "Loading saved session as it exists");
}
if (session == null) {
session = new Session(this);
Log.w("No Session found", "CreatingNewSession");
String sesija2 = session.toString();
Log.w ("ss2", sesija2);
}
Session.setActiveSession(session);
if (session.getState().equals(SessionState.CREATED_TOKEN_LOADED)) {
session.openForRead(new Session.OpenRequest(this).setCallback(MyGlobals.INSTANCE.statusCallback));
Log.w("There is an active session", "Active Session Exists");
}
}
At this point my log says this:
10-24 09:11:52.284: W/ss(1404): {Session state:CREATED, token:{AccessToken token: permissions:[}, appId:xxxxxx}
(AppId is correct)
Then in my fragment, I try to log on to facebook using this code:
Session session = Session.getActiveSession();
if (!session.isOpened() && !session.isClosed()) {
Log.w("Session is not opend", "Session is not closed");
session.openForRead(new Session.OpenRequest(getActivity()).setCallback(MyGlobals.INSTANCE.statusCallback));
} else {
Session.openActiveSession(getActivity(), true, MyGlobals.INSTANCE.statusCallback);
Log.w("Open Active Session", "Status Callback");
}
At this point, FB webview appears, I set username and pass, it kind a works, and then disappears.
Now, I'm sort of nowhere, I do not see response and TOKEN recived, and if I click again to login (on the same button) my App crashes.
In the log I have this then:
10-24 09:12:31.944: W/ss(1404): {Session state:OPENING, token:{AccessToken token: permissions:[}, appId:xxxxxx}
If I compare it to SessionLoginExample provided by FB, I get there:
10-24 09:08:50.004: W/ss(1356): {Session state:OPENED, token:{AccessToken token:AAAFaKtb7Pg4BALYg4B5eosa0ZAE9ZAXB0ZBMFDJdNbsDsZAkGJUfKtGs71OEJikDxT2VBfo4QMXiNASz23ZAa6D76eUxW0mZAMa013HP2kxgZDZD permissions:[}, appId:xxxxxxx}
Meaning I'm doing something wrong, as I do not catch the returned AccessToken, and it is not saved properly.
That's what I think is wrong, but I do not understand how should I read the session information in proper way, or to say where to catch and save Token information within the fragment.
I tried to put in the fragment:
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Boolean session = Session.getActiveSession().onActivityResult(getSherlockActivity(), requestCode, resultCode, data);
}
As this was the "onActivityResult" in the example, but it seems that it does nothing.
Just to sum things up:
I create session behind the launcher activity. Then if there is no FB app installed, on Tab4 (settings) user should be able to log on to FB using webview. Then on Tab1 and Tab2 user should be able to Share image to FB similar like on Instagram.
If there is the app installed, I'd disable the tab 4, and let user share throuhg FB app using Tab1 and Tab2.
Tnx.
RESOLVED:
Thing that is never written but should be well known. When FB authentication is performed, the "On Activity Result" code must be executed to save the Token with the session.
However, if you are dealing with Fragments and Tabs, like I do, function "onActivityResult" is never executed within Fragment code, but must be executed in the Activity that is taking care of fragments/tabs.
So it looks like you have already worked around this and your workaround is fine, but I wanted to give another option here in case it is cleaner for you or others to use the Fragment for this instead.
For cases where you cannot or do not want to derive from FacebookActivity, you can use either your Activity or your Fragment to initialize the Session. Both Activity and Fragment have onActivityResult you can override, and the one that gets used depends on what you pass to initialize Session.OpenRequest.
So if you want the callback to come to the Fragment, you can call:
...new Session.OpenRequest(myFragment)...
Then override onActivityResult in your Fragment and forward the call to your Session.
This is somewhat nicer in that your Fragment no longer needs to have special knowledge of what Activity it is running in.
Related
I use com.facebook.widget.UserSettingsFragment to manage login/logout to Facebook in my app:
UserSettingsFragment userSettingsFragment = new UserSettingsFragment();
Session session = Session.getActiveSession();
if(session==null || session.isClosed()){
userSettingsFragment.setPublishPermissions(Arrays.asList("publish_actions"));
}
fragmentTransaction.replace(R.id.content_container, userSettingsFragment, "userSettingsFragment");
In another Fragment (but same Activity), Before publishing on Facebook I'm checking whether I'm logged using
Session session = Session.getActiveSession();
if (session != null && session.isOpened()){
publishOnFacebook(intent.getExtras());
}
When I start my app and try to publish, I always get a null Session although I logged in before. And when I start UserSettingsFragment, it shows me that I'm connected.
How can Session.getActiveSession(); be null if I'm actually connected?
Moreover, if I I start the Fragment where I do the publishing straight after UserSettingsFragment, I get the correct Session, i.e.:
1 start the app
2 call `Session.getActiveSession()` in another `Fragment` returns `null`
3 start `UserSettingsFragment` shows that I'm log
but
1 start the app
2 start `UserSettingsFragment` shows that I'm log
3 call `Session.getActiveSession()` in another `Fragment` returns a session
Anybody can help?
Ok, I've just read about openActiveSessionFromCache (not from Facebook doc though):
Session session = Session.getActiveSession();
if(session==null){
// try to restore from cache
session = Session.openActiveSessionFromCache(getActivity());
}
if(session!=null && session.isOpened()){
publishOnFacebook(intent.getExtras());
}
else{
login();
}
Jul's answer does not work for me. So I tried this:
If you had loggedin earlier and if you restarted the app then the session may not be null and it could be in the CREATED_TOKEN_LOADED state. This state implies that the Session has not yet been opened and has a cached token. Opening a Session in this state will not involve user interaction. To open the session from this state you can call the method session.openForRead(null). This immediately opens the session without any user interaction.
So check for this add the following code:
Session session = Session.getActiveSession();
if (session != null){
if(session.getState() == SessionState.CREATED_TOKEN_LOADED){
//this immediately opens the session
session.openForRead(null);
}
if(session.isOpened()) {
publishOnFacebook(intent.getExtras());
}
else{
//start the facebook login fragment or activity
login();
}
}
else{
login();
}
I have the following scenario with facebook sdk 3.0.1. When user first login and chooses "FB login" then the SSO starts, a new session is open and everything works fine. But then, when the user closes the app and start it again - I don't understand how to get the last open session, currently I'm opening a new session and the user sees again the FB progress bar(while it's being connected to FB again, even so the user already approved FB in his last run). Does anybody know how to skip this operation?
Edit 1:
This is how I retrieve the session:
public void tryRetrievFacebookSession() {
Session session = Session.getActiveSession();
if (session != null && session.isOpened())
return;
session = Session.openActiveSession(this, true, new Session.StatusCallback() {
#Override
public void call(Session session, SessionState state, Exception exception) {
MobliLog.d("SplashScreen", "Inside call() with session with state: " + session.getState());
// onSessionStateChanged(session, state, exception);
}
});
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Session session = Session.getActiveSession();
if (session != null)
session.onActivityResult(this, requestCode, resultCode, data);
uiHelper.onActivityResult(requestCode, resultCode, data);
}
The session is normally being created or from the LoginButton or with those lines:
session = Session.getActiveSession();
if (session.getState().isClosed())
session = new Session(this);
if (session.isOpened()) {
onAuthenticationEndListener.onSuccessfullAuthentication();
return;
} else {
this.onFacebookAuthenticationEndListener = onAuthenticationEndListener;
Session.setActiveSession(session);
session.openForRead(new Session.OpenRequest(SocNetwksCompatScreen.this).setCallback(null));
return;
}
Information 1:
When I'm doing the first Session session = Session.getActiveSession(); in the logins after the sso authentication, my session has state CLOSED instead of OPENED
Information 2:
I'm using uiHelper and initialize it like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
twitter = SocialPluginsUtils.getTwitterClient();
uiHelper = new UiLifecycleHelper(this, statusCallback);
uiHelper.onCreate(savedInstanceState);
}
Now, I know that after I perform Session.getActiveSession(); the session state should get OPENED and after that OPEN and then the uiHelper's callback should be invoked. In my code the state is CLOSED or CLOSED_LOGIN_FAILED or CREATED(not sure why it's not stable) and the rest doesn't happens
In fact the sessions are being closed every time the user closed the app.
So if Session.getActiveSession() return a null session you only need to call Session.openActiveSession(activity, true, sessionCallback).
If there is a valid token cache this method will use it in order to open a new session without the need for the user to insert any data. Otherwise this will shows the default dialog with the basic permissions.
From what I can see after your edit maybe the problem is related with the way in which you manage the session.
I use this code in my projects:
Session session = Session.getActiveSession();
if (session == null){
Session.openActiveSession(activity, true, sessionCallback);
}
else if (!session.getState().isOpened()){
session.openForRead(new Session.OpenRequest(activity)
.setCallback(sessionCallback));
//this will open the session with basic read permissions
}
else {
//do what you want with the opened session
}
Moreover if you use the UiLifecycleHelper you don't need the two line of code that I suggest to you in the comment, they are already in the method of the helper. But you must be sure to call all the methods of the helper in each related method of the activity (onResume, onPause etc.)
If there isn't a token cache the openActiveSession(activity, true, sessionCallback) will automatically call a new dialog and if the user login with success a new token cache will be available for future uses.
Problem solved. I accidentally called session.closeAndClearTokenInformation(); in the onStop() (yeah, so stupid!)
I have a screen that popups up that Facebook login thing with the following code:
//facebook stuff
if (Session.getActiveSession() == null || Session.getActiveSession().isClosed()) {
Session.openActiveSession(this, true, null);
}
If the user hits the "back" button and dismisses the login thing, it automatically sends them to the screen that was supposed to be facebook protected (and has errors because no user is logged in).
How should I handle this? I see that in the old sdk or whatever, it had:
facebook.isSessionValid()
Is there still a way to do this? If so, HOW would I actually do it? Some sort of while loop until they finally stop trying to back out? Is there a way to disable backing out of that screen? I don't generate that screen at all: facebook does. Is this just a bug I have to live with?
Edit:
Corollary to this is that I have just noticed that if I use the facebook app to logout, the facebook sdk doesn't tell me this in my own app: It still authenticates. Even worse is when it does it even after I have logged out, then logged in as an entirely different user using the facebook app. How do I handle this?
Edit: More Code
This is now how I log in to facebook (in the Activity's onCreate) and ask to get my graph user and my friends' (gets me first, then once that asynch thing is done, gets friends)
if (Session.getActiveSession() == null || Session.getActiveSession().isClosed()) {
Log.v(CLASS_NAME, "Logging into facebook");
//Session.openActiveSession(this, true, new Session.statusCallback());
final Session.StatusCallback sessionStatusCallback = new Session.StatusCallback() {
#Override
public void call(Session session, SessionState state,
Exception exception) {
// TODO Auto-generated method stub
if(exception != null)
{
// Handle fail case here.
Log.v(CLASS_NAME, "Facebook login error " + exception);
return;
}
// If session is just opened...
if(state == SessionState.OPENED)
{
// Handle success case here.
Log.v(CLASS_NAME, "Facebook login success!");
return;
}
}
};
Session.openActiveSession(this, true, sessionStatusCallback);
}
//annonymous inner class
final Callback<ArrayList<GraphUser>> inner_callback_class2 = new FacebookHelper.Callback<ArrayList<GraphUser>>() {
#Override
public void setResult(ArrayList<GraphUser> result) throws Exception {
// TODO Auto-generated method stub
for(GraphUser user : result){
friends.put(user.getId(), user);
}
friends.put(me.getId(), me); //i can see my own comments as well
//get comments
//ArrayList<String>tmp = new ArrayList<String>();
//tmp.add("Jenny");
//tmp.add("Charles");
//initial comment call so I can view them
//
getComments();
}
};
//annonymous inner class
FacebookHelper.Callback<GraphUser> inner_callback_class = new FacebookHelper.Callback<GraphUser>() {
#Override
public void setResult(GraphUser result) throws Exception {
// TODO Auto-generated method stub
me = result;
Log.v("ANYTHING", me.getName());
//make sure i have an id before I do anything
FacebookHelper.getMyFriends(Session.getActiveSession(),inner_callback_class2);
//post comment
//RailsServerHelper.postComment("Comment Testing", movie.id, myID, currentNPT);
}
};
FacebookHelper.getMyID(Session.getActiveSession(), inner_callback_class);
This is how I get my friends (in a static helper class):
public static void getMyFriends(final Session session, final Callback<ArrayList<GraphUser>> callback){
Request request = Request.newMyFriendsRequest(session, new Request.GraphUserListCallback() {
#Override
public void onCompleted(List<GraphUser> users, Response response) {
// TODO Auto-generated method stub
ArrayList<GraphUser> tmp = new ArrayList<GraphUser>(users);
try {
callback.setResult(tmp);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
request.executeAsync();
}
The only other facebook thing I have is that whole clear the session thing you suggested:
#Override
public void onDestroy(){
Session.getActiveSession().closeAndClearTokenInformation();
Log.v(CLASS_NAME, "Clearing facebook session");
pleaseStop = true;
super.onDestroy();
}
It won't let me log back in at all. I have two devices I'm testing it on.
The first device was working fine originally with one profile, but when I tried to try a second profile it wouldn't log the first one out, so I did that closeAndClearTokenInformation() thing. That resulted in NO profile, and it won't pop up the whole facebook log in stuff anymore. I think there is no profile because it always prints out "Logging into facebook", and the getMe call thing never works. (Just from describing my code here I see I can put the getMe stuff in the facebook log in callback, though) It also never prints the success statement I put in the callBack, and on the first device it prints out the error statement if I hit the back button (no matter how long I sit at that screen).
The second device never managed to get a profile logged in because the back button was hit on the login screen. Ever since that, the system seems to think its "logged in" as nobody, and nothing I do can get it to refresh. I don't ever see it attempting to log in like the first device. The closeAndClear call thing seems to do nothing on this device. If relevant, the second device is a virtual simulator running on an entirely different machine.
On the actual device, what I see is:
01-24 07:16:04.669: V/MakeAndViewCommentsActivity(3677): Logging into facebook
And then a bunch of system messages (I waited over ten minutes), none of which seem to have warnings or errors, and then:
01-24 07:29:05.548: V/MakeAndViewCommentsActivity(3677): Clearing facebook session
01-24 07:29:05.552: V/MakeAndViewCommentsActivity(3677): Facebook login error com.facebook.FacebookException: Log in attempt aborted.
Why isn't the facebook openActiveSession ever returning?
Edit: The facebook sample apps still have the original log in working, but don't care if I log out or change users in the actual facebook app. I guess they don't include any session clearing either?
Edit: When I have a print statement of:
Log.v(CLASS_NAME, "Facebook callback with state: " + state);
I see:
01-24 09:10:54.333: V/MakeAndViewCommentsActivity(4869): Facebook callback with state: OPENING
does the callback only get called once? Or is it going to be theoretically called again with a state of "OPEN" if it completes?
SOLUTION:
Edit: Looking up the fact that it never leaves the "OPENING" state helped me a lot, it turns out I had to add the code:
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Session.getActiveSession()
.onActivityResult(this, requestCode, resultCode, data);
}
And then it just worked! I have no idea why. I guess I will do research on this, but I"m glad I can finally change users.
You're passing in a null callback. If you implemented the callback, you would see that the callback would be called, and if you checked state.isClosed(), it would return true (and the state should be CLOSED_LOGIN_FAILED). There should also be an exception in the exception parameter. You cannot disable the ability for users to back out of SSO, that's by design.
You're also correct in that there are no notifications if the user logs out of the facebook app. One way to have a better user experience is to always display the user's name and profile picture when you're doing something on behalf of the user (like posting a status), so it's immediately obvious that it's not the correct user. The other options is to always call session.closeAndClearTokenInformation every time your app exits. This way, you're always starting a fresh session, and if the user has the facebook app installed, it's still a very fast auth (no additional buttons to press). The drawback is that if they don't have the facebook app, then it will pop up a web dialog for login every time (but you can probably work around that by checking for the existence of the FB app).
I see there's a new version of Facebook SDK (3.0) that deprecates the old Facebook class and introduces a new way of logging in using the Session class.
I quickly wrote a simple app using the new login API:
public class MainActivity extends Activity {
private Session mFacebookSession;
private StatusCallback fbStatusCallback = new StatusCallback() {
#Override
public void call(Session session, SessionState state, Exception exception) {
Log.v("dbg", "state: " + state);
Log.v("dbg", "session: " + session);
}
};
public void bc(View view)
{
mFacebookSession = Session.openActiveSession(this, true, fbStatusCallback);
}
//etc..
}
The callback should get called twice, first for destroying the actual session token and second for getting a new access token.
Of course, my app id is set as a meta-data in my manifest file, etc.
When I execute the code, the Facebook Login dialog appears, I input my username and password and then it closes.
However, in my log I only see this:
01-17 03:28:01.587: V/dbg(7002): state: OPENING
01-17 03:28:01.587: V/dbg(7002): session: {Session state:OPENING, token:{AccessToken token:ACCESS_TOKEN_REMOVED permissions:[]}, appId:xxxxxxxxxxxxxxx}
As a result, if I try to call mFacebookSession.getAccessToken() i get an empty string (not null).
What seems to be the problem?
HOW I SOLVED THIS (LATER)
I have overridden onActivityResult and called Session.getActiveSession().onActivityResult(this, requestCode, resultCode, data);
Im just going to put this here for anyone else:
The Facebook SDK is really terrible at letting you know you have forgotten something. If it's not working for you double check your Facebook app and make sure you haven't forgotten something or put in the wrong values. In my case the appID was wrong in Sterpu Mihai's case it was the hash key
another thing that would cause the same error (Token_removed)
Android Facebook SDK3.0, session state OPENING
I'm have a feature on my Android app where the user authorizes the app and shares a link.
I also need to give an option for the user to logout of facebook and I need to conditionally disable this button if the user is not logged int (or not authorized the app).
I can't seem to find the API call on the Android SDK that would let me ask FB if the user is logged in or not.
What I have found is getAccessExpires():
Retrieve the current session's expiration time (in milliseconds since
Unix epoch), or 0 if the session doesn't expire or doesn't exist.
Will checking if the session equals 0 be the way to go? Or is there something I'm missing?
Facebook SDK 4.x versions have a different method now:
boolean loggedIn = AccessToken.getCurrentAccessToken() != null;
or
by using functions
boolean loggedIn;
//...
loggedIn = isFacebookLoggedIn();
//...
public boolean isFacebookLoggedIn(){
return AccessToken.getCurrentAccessToken() != null;
}
Check this link for better reference https://developers.facebook.com/docs/facebook-login/android
check this heading too "Access Tokens and Profiles" it says "You can see if a person is already logged in by checking AccessToken.getCurrentAccessToken() and Profile.getCurrentProfile()
I struggled to find a simple answer to this in the FB docs. Using the Facebook SDK version 3.0 I think there are two ways to check if a user is logged in.
1) Use Session.isOpened()
To use this method you need to retrieve the active session with getActiveSession() and then (here's the confusing part) decipher if the session is in a state where the user is logged in or not. I think the only thing that matters for a logged in user is if the session isOpened(). So if the session is not null and it is open then the user is logged in. In all other cases the user is logged out (keep in mind Session can have states other than opened and closed).
public boolean isLoggedIn() {
Session session = Session.getActiveSession();
return (session != null && session.isOpened());
}
There's another way to write this function, detailed in this answer, but I'm not sure which approach is more clear or "best practice".
2) Constantly monitor status changes with Session.StatusCallback and UiLifecycleHelper
If you follow this tutorial you'll setup the UiLifecycleHelper and register a Session.StatusCallback object with it upon instantiation. There's a callback method, call(), which you override in Session.StatusCallback which will supposedly be called anytime the user logs in/out. Within that method maybe you can keep track of whether the user is logged in or not. Maybe something like this:
private boolean isLoggedIn = false; // by default assume not logged in
private Session.StatusCallback callback = new Session.StatusCallback() {
#Override
public void call(Session session, SessionState state, Exception exception) {
if (state.isOpened()) { //note: I think session.isOpened() is the same
isLoggedIn = true;
} else if (state.isClosed()) {
isLoggedIn = false;
}
}
};
public boolean isLoggedIn() {
return isLoggedIn;
}
I think method 1 is simpler and probably the better choice.
As a side note can anyone shed light on why the tutorial likes to call state.isOpened() instead of session.isOpened() since both seem to be interchangeable (session.isOpened() seems to just call through to the state version anyway).
Note to readers: This is now deprecated in the new FB 3.0 SDK.
facebook.isSessionValid() returns true if user is logged in, false if not.
Session.getActiveSession().isOpened()
returns true if user is logged in, false if not
Android Studio with :
compile 'com.facebook.android:facebook-android-sdk:4.0.1'
then check login like as:
private void facebookPost() {
//check login
AccessToken accessToken = AccessToken.getCurrentAccessToken();
if (accessToken == null) {
Log.d(TAG, ">>>" + "Signed Out");
} else {
Log.d(TAG, ">>>" + "Signed In");
}
}
#diljeet was right. https://stackoverflow.com/a/29375963/859330
In addition, use
return AccessToken.getAccessToken() != null && Profile.getCurrentProfile()!=null;
It always works this way.
For Facebook Android SDK 4.x you have to use the "AccessToken.getCurrentAccessToken()" as said by #Diljeet but his check didn't work for me, I finally checked it by doing:
Activity "onCreate":
facebookAccessToken = AccessToken.getCurrentAccessToken();
To check if the session is still active (I made it in the "onResume" method but do it where you need):
if(facebookAccessToken != null){
sessionExpired = facebookAccessToken.isExpired();
}else{
sessionExpired = true;
}
More info in https://developers.facebook.com/docs/facebook-login/android
This seems to be working quite well with the new sdk.
private boolean isFacebookLoggedIn(){
Session session = Session.getActiveSession();
if (session != null) {
//Session can be open, check for valid token
if (!session.isClosed()) {
if(!session.getAccessToken().equalsIgnoreCase("")){
return true;
}
}
}
return false;
}
I had the same issue. Here is my solution using SDK 4.0:
First of all, in your activity dealing with login check, be sure to call this primary:
FacebookSdk.sdkInitialize(this.getApplicationContext());
In your onCreate method put this :
updateWithToken(AccessToken.getCurrentAccessToken());
new AccessTokenTracker() {
#Override
protected void onCurrentAccessTokenChanged(AccessToken oldAccessToken, AccessToken newAccessToken) {
updateWithToken(newAccessToken, handler);
}
};
Then add the method called:
private void updateWithToken(AccessToken currentAccessToken) {
if (currentAccessToken != null) {
fillUIWithFacebookInfos(handler);
} else {
login();
}
}
This way will handle the already logged in user and the newly logged in user.
I was using FB sdk for just for login..
Facebook developers reject my app, Because whenever user login, I send logout.. I dont matter user profile data... I just use FB for login.. FB tells if user login... dont send lgout...
I find Hacking way...
Now my app doesnot send logout whenever user login.... Whatever user login with,Google,Facebook,or normal.. When they click logout... In this three condition
I use
LoginManager.getInstance().logOut();
No matter which platform they use... There is no crash :)