I use the following method to login to Facebook using the Facebook sdk. I added there an exception try/catch handler that caught the following exception:
Exception ::::::﹕ Session: an attempt was made to open an already
opened session.
As you can see I check if the session is closed before I open it. and it passes it which means its closed and it is safe to open it. even that an exception is caught.
I run the debugger and stopped at the line marked below and checked the currentSession state and it is closed!!!!!
private Session openActiveSession(Activity activity, boolean allowLoginUI, List permissions, Session.StatusCallback callback) {
Session currentSession = Session.getActiveSession();
if (currentSession == null) {
Session session = new Session.Builder(activity).build();
Session.setActiveSession(session);
currentSession = session;
}
if (!currentSession.isOpened() && (SessionState.CREATED_TOKEN_LOADED.equals(currentSession.getState()) || allowLoginUI)) {
try {
Session.OpenRequest openRequest = new Session.OpenRequest(activity).setPermissions(permissions).setCallback(callback);
currentSession.openForRead(openRequest); // I stopped here..
} catch (Exception e) {
Log.d("Exception :::::: ", e.getMessage()); // and stopped here.
}
return currentSession;
}
return null;
}
Any explanation?
Could that mean that there is another session that is open? my could always gets an active session if exists I don't see how that could happen. I always check for active session in all methods and use it!
How Session.getActiveSession() method really works? what if another session has been initialised in a previous activity? this method should get it right?
The error message could be improved, I think. It should actually say: "an attempt was made to open a session that's already open, or has already been closed".
You should check to see if currentSession.isClosed() is true, and if so, create a new session.
A session can only be opened once. It can't be re-opened.
Related
I am integrating logging in via Facebook in my app. I have this code that presents the login:
Session.openActiveSession(getActivity(), true, new Session.StatusCallback() {
// callback when session changes state
public void call(Session session, SessionState state, Exception exception) {
if (session.isOpened()) {
Log.d("s", "SUCCES");
// make request to;2 the /me API
}
if (exception != null) {
Log.d("s", exception.toString());
}
Log.d("s", "state:" + state.toString());
}
});
After I enter my username/password, I get this in the logs:
D/app﹕ com.facebook.FacebookException: Log in attempt aborted.
D/app﹕ state:CLOSED_LOGIN_FAILED
D/app﹕ state:OPENING
What I've tried:
Checked my package name is correct
Checked the hash key is correct (Used the code by facebook)
Taken my app out of sandbox mode.
Checked I have the correct app id in my manifest file
What I am doing wrong?
Thanks!
I think you have this problem because you already have an active session. So just test it before trying to reopen it.
Session.StatusCallback statusCallback = new Session.StatusCallback() {
// callback when session changes state
public void call(Session session, SessionState state, Exception exception) {
if (session.isOpened()) {
Log.d("s", "SUCCES");
// make request to;2 the /me API
}
if (exception != null) {
Log.d("s", exception.toString());
}
Log.d("s", "state:" + state.toString());
}
Session session = Session.getActiveSession();
if (session!=null && !session.isOpened() && !session.isClosed()) {
session.openForRead(new Session.OpenRequest(this)
.setPermissions(...)
.setCallback(statusCallback));
} else {
Session.openActiveSession(getActivity(), true, statusCallback);
}
Reading through Facebook's SDK code we can extract some conclusions.
You get an exception on your callback with a specific message and state. These two are sent only when the close() Session's method is called, while the state was OPENING, which is at the same time called by setActiveSession, and that one gets called directly by your Session.openActiveSession(...)
The next thing that gets called is session.openForRead(openRequest), which calls open(openRequest, ...), with the current state being CREATED (default state after building a new session). This sets the new session as the active session through setActiveSession(session) which ends up closing the oldSession if there was one active, and here's the thing again, you are getting a FacebookException with a "Log in attempt aborted" on the message, which only gets called if the current state of the previous session was OPENING.
All that means that either you are calling this piece of code twice within a short amount of time or that the authorization process is not going through and ends up on a dead end (most likely).
Before getting deeper on the authorize method I'll double check configuration in facebook (mode, key hashes -different depending on the launching environment: production, degub, etc-, package name and start activity). For simplicity, clear data and caches of your app every time you try (or manually call closeAndClearTokenInformation() from your code.
Let's see what comes out to keep digging into the issue.
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 am trying to restore user session inside my application. For that I call :
Session.openActiveSessionFromCache(ctx);
Most of the time it works fine. But sometimes I get :
java.lang.UnsupportedOperationException: Session: an attempt was made to open an already opened session.
Edit :
I've add this check :
Session activeSession = Session.getActiveSession();
if (activeSession!=null && activeSession.isOpened()){
return activeSession;
}
activeSession = Session.openActiveSessionFromCache(ctx);
and it did not help
Does anybody have a hint how to tackle that problem?
thanks
As the
openActiveSessionFromCache
works like following: "Create a new Session, and if a token cache is available, open the Session and make it active without any user interaction".
So it should be better if you check if the session is open or not before calling openActiveSessionFromCache. you can use
isOpened()
Use this line of code when facebook session is not in opened state.
private Session.StatusCallback callback = new Session.StatusCallback() {
#Override
public void call(Session session, SessionState state, Exception exception) {
if (!state.isClosed()) {
Session.openActiveSessionFromCache(ctx);
}
}
};
In the Facebook Android SDK when I call
Session tempSession = new Builder(this).build();
Session.setActiveSession(tempSession);
tempSession.openForRead(new OpenRequest(this).setPermissions(FB_PERMISSIONS));
It gets a FB session and every thing runs as normal. But when I replace Read with Publish. i.e. follows
Session tempSession = new Builder(this).build();
Session.setActiveSession(tempSession);
tempSession.openForPublish(new OpenRequest(this).setPermissions(FB_PERMISSIONS));
It gives an error saying, that the session is empty, and cannot get publish permissions to empty session.
Can you please tell why is it like this and what would be the best way to handle this?
It took me a while to sort this out so a user could click a button to share one of my products on Facebook in a feed. I didn't want them to be prompted to sign in until they actually wanted to share, so I really just wanted publish permission. The following stacks the initial login/read permission request with the publish permission request. This will double-prompt the users, first for read, then for publish, but that is required now regardless of the solution:
Session session = Session.getActiveSession();
if (session == null) {
session = new Session.Builder(this).setApplicationId("<APP ID HERE>").build();
Session.setActiveSession(session);
session.addCallback(new StatusCallback() {
public void call(Session session, SessionState state, Exception exception) {
if (state == SessionState.OPENED) {
Session.OpenRequest openRequest = new Session.OpenRequest(FacebookActivity.this);
openRequest.setLoginBehavior(SessionLoginBehavior.SSO_WITH_FALLBACK);
session.requestNewPublishPermissions(
new Session.NewPermissionsRequest(FacebookActivity.this, PERMISSIONS));
}
else if (state == SessionState.OPENED_TOKEN_UPDATED) {
publishSomething();
}
else if (state == SessionState.CLOSED_LOGIN_FAILED) {
session.closeAndClearTokenInformation();
// Possibly finish the activity
}
else if (state == SessionState.CLOSED) {
session.close();
// Possibly finish the activity
}
}});
}
if (!session.isOpened()) {
Session.OpenRequest openRequest = new Session.OpenRequest(this);
openRequest.setLoginBehavior(SessionLoginBehavior.SSO_WITH_FALLBACK);
session.openForRead(openRequest);
}
else
publishSomething();
The short answer is, don't call openForPublish. Call openForRead, and then requestNewPublishPermissions later if you need publish permissions.
The long answer is, you can't request publish permissions (on a user who's never connected with Facebook before via your app) unless you already have basic or default permissions already (what you would get if you call openForRead with an empty permission set). So openForPublish actually handles a very specific niche use case that most apps probably don't have.
I am trying to implement the new Facebook SDK 3.0 into my Android app and I have run into a problem. The problem I am having is that the user is asked to log in again to give publish ("post_to_wall") permissions, even though the user is already logged in, with read permissions. This only happens if the user doesn't have the FB application installed. If he has the FB application installed, then he is only asked to grant the permissions.
This is how I implemented the login:
public void login(Activity activity) {
Session session = Session.getActiveSession();
if (session == null || !session.isOpened()) {
openActiveSession(activity, true, sessionStatusCallback);
}
}
private Session openActiveSession(final Activity activity, final boolean allowLoginUI, final StatusCallback callback) {
return openActiveSession(activity, allowLoginUI, new OpenRequest(activity).setCallback(callback));
}
private Session openActiveSession(final Context context, final boolean allowLoginUI, final OpenRequest openRequest) {
Session session = new Builder(context).setApplicationId(FACEBOOK_APPLICATION_ID).build();
if (SessionState.CREATED_TOKEN_LOADED.equals(session.getState()) || allowLoginUI) {
Session.setActiveSession(session);
session.openForRead(openRequest);
return session;
}
return null;
}
This is the callback's call method:
public void call(final Session session, final SessionState state, final Exception exception) {
if (session.isOpened()) {
if (state.equals(SessionState.OPENED_TOKEN_UPDATED)) {
// code if new permissions have been granted
} else {
// code for login
}
} else if (session.isClosed()) {
// code for user canceled login
} else if (exception != null) {
// code if there were errors during login
}
}
}
This is the code I added to onActivityResult method of the activity that calls the login:
Session.getActiveSession().onActivityResult(activity, requestCode, resultCode, data);
And this is how I ask for new permissions:
Session session = Session.getActiveSession();
if (session != null && session.isOpened()) {
if (DONT_HAVE_PERMISSIONS) {
Session.NewPermissionsRequest newPermissionsRequest = new Session.NewPermissionsRequest(activity,
FACEBOOK_PERMISSIONS).setRequestCode(FACEBOOK_AUTHORIZE_ACTIVITY_CODE);
session.requestNewPublishPermissions(newPermissionsRequest);
}
}
I've tried to find out more about this problem, and I only found out some hints that this is intended, but I haven't found anything concrete.
Is this the default behavior? If so, is there a way around it? Or, perhaps I did something wrong?
Thanks for the help.
Update your SDK version; this issue is resolved in Facebook Android SDK v3.0.1.
Looking at facebooks source code I think it should be possible to start trying to get the permissions directly as both login, and permissions classes derive from the same AuthorizationRequest class, and the AuthorizationRequest class does all the work, like really all the work. The Session.NewPermissionsRequest class just makes some private methods, public in the AuthorizationRequest class and that's it! They might as well give us access to AuthorizationRequest directly. The new facebook API doesn't seem to have any form of "OnFailed/OnSuccess" callbacks, so I end up having a state machine to remember the goal of firing up facebook (login, permissions, get friends list ...), and which step I'm on. If they have done some form of onFailed/onSuccess callbacks it would be simple to make a chain rather than keeping track of a state machine.
I haven't tried what I said. If I do, I'll update the answer. If you try and it works to just fire up Session.NewPermissionsRequest directly without logging in let me know!
Update
I got it working with only asking for credentials once as I explained above.
goto src/com/facebook/Sessions.java
On line 862 you will find
private static Session openActiveSession(Context context, boolean allowLoginUI, OpenRequest openRequest)
make it be a public function and save.
Now instead of creating the Session.NewPermissionsRequest object. Make Session.OpenRequest
permisions = new ArrayList<String>();
permisions.add("user_photos");
permisions.add("friends_photos");
Session.NewPermissionsRequest request = new Session.NewPermissionsRequest(
activity, permisions);
request.setCallback(helper);
if (Session.getActiveSession() != null)
Session.getActiveSession().requestNewReadPermissions(request);
else {
Session.OpenRequest orequest = new Session.OpenRequest(activity);
orequest.setPermissions(permisions);
orequest.setCallback(helper);
// its now public so you can call it
Session.openActiveSession(act, true, request);
}
Now make sure you do set a callback, for one important reason
#Override
public void call(Session session, SessionState state, Exception exception) {
if (state.isClosed()) {
// we are doing unofficial stuff so we loose guarantees.
// Set the active session to null if we logout or user cancels
// logging in. If you don't do this, the second time it will result
// in a crash.
Session.setActiveSession(null);
}
}
Now it will ask for all permissions directly and login in one go.