20

Been banging my head agains the facebook-sdk-for-android-wall for the last couple of days now and need help. I can't find what's wrong, what I should do instead or how to check for some state that I'm missing.

My Android app is recording tracks and for a track the user have to option of posting the track summary to his/hers facebook timeline. I have got it to work as a happy flow but when testing different scenarios such as no internet connection etc I'm now stuck in a state where I only get the "Session: an attempt was made to reauthorize a session that has a pending request"-exception when trying to reauthorize permissions (even when internet connection is restored). I'm using the facebook SDK for android and SSO.

package com.test;

import java.io.Fil...
...

public class FaceBookUtil {

public interface FacebookPostCallback {
    void onComplete(String postId);
    void onError(Exception e);
    void onError();
}

private static final List<String> PERMISSIONS = Arrays.asList("publish_actions", "publish_stream");
private static final int REAUTH_ACTIVITY_CODE = 100;

public static void postToFaceBook(final Track track, final Activity activity, final FacebookPostCallback postCallback) {

    try {
        Session session = initFacebookSession(activity.getApplicationContext());
        Session.setActiveSession(session);

        StatusCallback statusCallback = new StatusCallback() {
            @Override
            public void call(Session session, SessionState state, Exception exception) {

                try {
                    // Check for publish permissions    
                    List<String> permissions = session.getPermissions();
                    if (!isSubsetOf(PERMISSIONS, permissions)) {
                        Session.ReauthorizeRequest reauthRequest = new Session
                                .ReauthorizeRequest(activity, PERMISSIONS)
                                .setRequestCode(REAUTH_ACTIVITY_CODE);
                        session.reauthorizeForPublish(reauthRequest);    //Here's where it breaks and the exceptions is thrown.
                        return;
                    }

                    Bundle postParams = new Bundle();
                    postParams.putString("name", "Facebook post test"); 
                    postParams.putString("caption", "Just a test"); 
                    postParams.putString("description", "A description");
                    postParams.putString("link", "http://www.google.com/");

                    Request.Callback reqCallback = new Request.Callback() {
                        public void onCompleted(Response response) {
                            String postId = null;

                            try {
                                FacebookException error = response.getError();
                                if (error != null)
                                    throw error;

                                JSONObject graphResponse = response.getGraphObject().getInnerJSONObject();

                                postId = graphResponse.getString("id");
                                if (postId != null)
                                    postCallback.onComplete(postId);

                            } catch (Exception e) {
                                postCallback.onError();
                            }

                            if (postId == null)
                                postCallback.onError();
                        }
                    };

                    Request postRequest = new Request(session, "me/feed", postParams, HttpMethod.POST, reqCallback);
                    RequestAsyncTask task = new RequestAsyncTask(postRequest);
                    task.execute();

                } catch (Exception e) {
                    postCallback.onError(e);
                }
            }
        };

        if (session.getState().equals(SessionState.CREATED_TOKEN_LOADED))
            session.openForRead(new Session.OpenRequest(activity).setCallback(statusCallback));

        else if (!session.isOpened() && !session.isClosed()) {
            OpenRequest req = new Session.OpenRequest(activity);
            req.setCallback(statusCallback);
            session.openForRead(req);

        } else {
            Session.openActiveSession(activity, true, statusCallback);

        }

    } catch (Exception e) {
        postCallback.onError(e);
    }    
}

private static boolean isSubsetOf(Collection<String> subset, Collection<String> superset) {
    for (String string : subset) {
        if (!superset.contains(string)) {
            return false;
        }
    }
    return true;
}

private static Session initFacebookSession(Context context) {

    Session session = Session.getActiveSession();

    if (session != null) 
        return session;

    /*
    if (savedInstanceState != null)
        session = Session.restoreSession(this, null, sessionStatusCallback, savedInstanceState);
    */

    if (session == null)
        session = new Session(context);

    return session;
}
}

If I debug into the android SDK I can see that the exception is thrown by the reauthorize method in the Session class because the pendingRequest is not null but I don't understand why, where it's set or how I can check for it, remove it or whet I'm supposed to do?

private void reauthorize(ReauthorizeRequest reauthorizeRequest, AuthorizationType authType) {
    validatePermissions(reauthorizeRequest, authType);
    validateLoginBehavior(reauthorizeRequest);
    if (reauthorizeRequest != null) {
        synchronized (this.lock) {
            if (pendingRequest != null) {   //Here's where the pendingRequest is already set and causes the exception...
                throw new UnsupportedOperationException(
                        "Session: an attempt was made to reauthorize a session that has a pending request.");
            }
            switch (this.state) {
                case OPENED:
                case OPENED_TOKEN_UPDATED:
                    pendingRequest = reauthorizeRequest;
                    break;
                default:
                    throw new UnsupportedOperationException(
                            "Session: an attempt was made to reauthorize a session that is not currently open.");
            }
        }

        authorize(reauthorizeRequest);
    }
}

I've googled the exception without any results, Any help is greatly appreciated.

On a side note I can probably refactor the whole FacebookUtil.postToFaceBook-method but while testing I've kept it in one method for readability.

Cliffhanger
  • 1,057
  • 1
  • 9
  • 18

7 Answers7

8

You need to call the onActivityResult() method on your current Session when asking for more permissions:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    Logger.d(LOGTAG, "onActivityResult");
    if (RESULT_CANCELED == resultCode) {
        mAuthorizationCanceled = true;
    }
    **mFacebookSession.onActivityResult(this, requestCode, resultCode, data);**
}

This way the StatusCallback is called again and the pendingRequest is removed.

PawelPredki
  • 754
  • 1
  • 12
  • 26
5

Check to make sure you added this line of code in your Activity.

public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data); 
    UiLifecycleHelper.onActivityResult();
} 
Jesse Chen
  • 4,928
  • 1
  • 20
  • 20
  • is this necessary? Cause I've been making requests successfully without this line. Read and publish both. – Nitin Bansal Jan 10 '13 at 20:07
  • 1
    This won't change anything. That method just only calls itself. Probably you forgot adding `UiLifecycleHelper.onActivityResult()` or `Session.onActivityResult()` (both not static!)... – DragonWork Jan 27 '13 at 15:15
2

I ask user for publish permission after log-in

This is my code for SDK V3.0.1

public class FacebookAccount extends FragmentActivity {
    private UserSettingsFragment userSettingsFragment;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.facebook_login);

        FragmentManager fragmentManager = getSupportFragmentManager();
        userSettingsFragment = (UserSettingsFragment) fragmentManager
                .findFragmentById(R.id.login_fragment);

        userSettingsFragment.setReadPermissions(Utility.READ_PERMISSIONS);

        userSettingsFragment
                .setSessionStatusCallback(new Session.StatusCallback() {
                    @Override
                    public void call(Session session, SessionState state,
                            Exception exception) {
                        if (state == SessionState.OPENED
                                && !session.getPermissions().containsAll(
                                        Utility.PUBLISH_PERMISSIONS)) {
                            session.requestNewPublishPermissions(new Session.NewPermissionsRequest(
                                    FacebookAccount.this,
                                    Utility.PUBLISH_PERMISSIONS));
                        }
                        if (session.isOpened()
                                && state == SessionState.OPENED_TOKEN_UPDATED) {
                            //Save session access token
                        }
                    }
                });
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (userSettingsFragment != null)
            userSettingsFragment
                    .onActivityResult(requestCode, resultCode, data);
        super.onActivityResult(requestCode, resultCode, data);
    }

}
SnowBEE
  • 925
  • 1
  • 9
  • 14
1

It happens when previous session is not closed and not opend properly , it was just created So to avoid this sitution I handled it in onResume when status is notOpend and notClosed then setActive session null. Its working for me

public void onResume()
        {
            super.onResume();
            is not null, the session state change notification
            // may not be triggered. Trigger it if it's open/closed.
            Session session = Session.getActiveSession();
            if (session != null && (session.isOpened() || session.isClosed()))
                {
                    onSessionStateChange(session, session.getState(), null);
                }
            else
                {
                    Session.setActiveSession(null);
                }
            uiHelper.onResume();
        }
Ishtiaq
  • 1,206
  • 9
  • 18
0

I guess Facebook just doesn't allow two opening requests running together.

If you only need to open a session with publish permissions (not switching between read and publish), there's an easy way of doing this.

Go to Sessions.java in the source code and change this function private static Session openActiveSession(Context context, boolean allowLoginUI, OpenRequest openRequest) to be public.

Use this to login Facebook:

private void loginFacebook() {  
        Session session = Session.getActiveSession();
        if (session == null) {
            session = new Session(this);
        }
        Session.setActiveSession(session);

        Session.OpenRequest request = new Session.OpenRequest(this).setCallback(statusCallback);
        request.setPermissions(Arrays.asList(publishPermissions));
        if (session.getState().equals(SessionState.CREATED_TOKEN_LOADED)) {
            session.openForPublish(request);
        } else {
            session = Session.openActiveSession(this, true, request);
        }
    }

And use this to publish:

StatusCallback statusCallback = new StatusCallback() {
            @Override
            public void call(Session session, SessionState state, Exception exception) {

                try {

                    Bundle postParams = new Bundle();
                    postParams.putString("name", "Facebook post test"); 
                    postParams.putString("caption", "Just a test"); 
                    postParams.putString("description", "A description");
                    postParams.putString("link", "http://www.google.com/");

                    Request.Callback reqCallback = new Request.Callback() {
                        public void onCompleted(Response response) {
                            String postId = null;

                            try {
                                FacebookException error = response.getError();
                                if (error != null)
                                    throw error;

                                JSONObject graphResponse = response.getGraphObject().getInnerJSONObject();

                                postId = graphResponse.getString("id");
                                if (postId != null)
                                    postCallback.onComplete(postId);

                            } catch (Exception e) {
                                postCallback.onError();
                            }

                            if (postId == null)
                                postCallback.onError();
                        }
                    };

                    Request postRequest = new Request(session, "me/feed", postParams, HttpMethod.POST, reqCallback);
                    RequestAsyncTask task = new RequestAsyncTask(postRequest);
                    task.execute();

                } catch (Exception e) {
                    postCallback.onError(e);
                }
            }
        };
AAnkit
  • 27,299
  • 12
  • 60
  • 71
xiangxin
  • 409
  • 6
  • 18
  • 1
    I tried the proposed solution but the session gets stuck on state.OPENING and doesn't request for permissions either. – Shiin Zu Nov 12 '12 at 21:33
0

This is quite possible, If you have request to open a facebook session before completion of earlier request, facebook sdk will throw "UnsupportedOperationException". This can happen for user initiated events so its always recommended to handle that exception appropriately.

PS: In most of cases your need to ignore this.

Pawan Maheshwari
  • 15,088
  • 1
  • 48
  • 50
0

In order to give publish permissions before the user actually wants to publish something, from certain activity, and without using "UiLifecycleHelper". This method worked for me (based on many trial and the code in this specific tutorials: https://developers.facebook.com/docs/games/mobile/android-tutorial/ https://developers.facebook.com/docs/facebook-login/permissions/)

First create the request for permissions (in case the user doesn't have it):

Session session = Session.getActiveSession();
if (PostOnFB.isChecked() && session != null && session.isOpened()) {
    List<String> permissions = session.getPermissions();
    if (!permissions.contains("publish_actions")) {
        // if they choose to publish, then we request for publish permissions
        Session.NewPermissionsRequest newPermissionsRequest =
                new Session.NewPermissionsRequest(GetUserLocationActivity.this, Arrays.asList("publish_actions"))
                        .setDefaultAudience(SessionDefaultAudience.FRIENDS)
                        .setCallback(newPermissionsCallback)
                        .setRequestCode(REAUTH_ACTIVITY_CODE);
        session.requestNewPublishPermissions(newPermissionsRequest);
    } else {
        publishResult();
    }
}

Where "publishResult()" is the method which will be called after the user gets the permissions.

Next, add a case for onActivityResult:

@Override
public void onActivityResult (
        int requestCode, int resultCode, Intent data) {
    // Decide what to do based on the original request code
    switch (requestCode) {
        case ..
        ..
        ..
        ..
        ..
        case REAUTH_ACTIVITY_CODE :
        Session.getActiveSession().onActivityResult(GetUserLocationActivity.this, requestCode, resultCode, data);
    }
 }

Finally implement the callback:

private Session.StatusCallback newPermissionsCallback = new Session.StatusCallback() {
    @Override
    public void call(Session session, SessionState state, Exception exception) {
        if (exception != null || !session.isOpened() || !session.getPermissions().contains("publish_actions")) {
            // this means the user did not grant us write permissions, so
            // do whatever you think
        } else {
            publishResult();
        }
    }
};

That's it.

Hanan N
  • 51
  • 1
  • 4