12

I followed the instructions at https://developers.facebook.com/docs/howtos/androidsdk/3.0/login-with-facebook/. When I touch the "Log In"-button, the session state is OPENING - always. When I try to post a story to the user's wall I get an error stating

"Session: an attempt was made to request new permissions for a session that has a pending request."

Here is my code:

public class MainFragment extends Fragment {
    private static final String TAG = "MainFragment";
    private UiLifecycleHelper uiHelper;

    private Session.StatusCallback callback = new Session.StatusCallback() {
        @Override
        public void call(Session session, SessionState state,
                Exception exception) {
            onSessionStateChange(session, state, exception);
        }
    };

    private void onSessionStateChange(Session session, SessionState state,
            Exception exception) {
        if (state.isOpened()) {
            Log.d(TAG, "Logged in...");
        } else if (state.isClosed()) {
            Log.d(TAG, "Logged out...");
        } else {
            Log.d(TAG, "Unknown state: " + state);
        }
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        uiHelper = new UiLifecycleHelper(getActivity(), callback);
        uiHelper.onCreate(savedInstanceState);
    }

    @Override
    public void onResume() {
        super.onResume();

        // For scenarios where the main activity is launched and user
        // session 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);
        }

        uiHelper.onResume();
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        uiHelper.onActivityResult(requestCode, resultCode, data);
    }

    @Override
    public void onPause() {
        super.onPause();
        uiHelper.onPause();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        uiHelper.onDestroy();
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        uiHelper.onSaveInstanceState(outState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.facebook_login, container, false);
        LoginButton authButton = (LoginButton) view
                .findViewById(R.id.authButton);
        authButton.setFragment(this);

        return view;
    }

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

    private static final List<String> PERMISSIONS = Arrays
            .asList("publish_actions");
    private static final String PENDING_PUBLISH_KEY = "pendingPublishReauthorization";
    private boolean pendingPublishReauthorization = false;

    public void publishStory() {
        Session session = Session.getActiveSession();

        if (session != null) {

            // Check for publish permissions
            List<String> permissions = session.getPermissions();
            if (!isSubsetOf(PERMISSIONS, permissions)) {
                pendingPublishReauthorization = true;
                Session.NewPermissionsRequest newPermissionsRequest = new Session.NewPermissionsRequest(
                        this, PERMISSIONS);
                session.requestNewPublishPermissions(newPermissionsRequest);
                return;
            }

            Bundle postParams = new Bundle();
            postParams.putString("name", "Facebook SDK for Android");
            postParams.putString("caption",
                    "Build great social apps and get more installs.");
            postParams
                    .putString(
                            "description",
                            "The Facebook SDK for Android makes it easier and faster to develop Facebook integrated Android apps.");
            postParams.putString("link",
                    "https://developers.facebook.com/android");
            postParams
                    .putString("picture",
                            "https://raw.github.com/fbsamples/ios-3.x-howtos/master/Images/iossdk_logo.png");

            Request.Callback callback = new Request.Callback() {
                public void onCompleted(Response response) {
                    JSONObject graphResponse = response.getGraphObject()
                            .getInnerJSONObject();
                    String postId = null;
                    try {
                        postId = graphResponse.getString("id");
                    } catch (JSONException e) {
                        Log.i(TAG, "JSON error " + e.getMessage());
                    }
                    FacebookRequestError error = response.getError();
                    if (error != null) {
                        Toast.makeText(getActivity().getApplicationContext(),
                                error.getErrorMessage(), Toast.LENGTH_SHORT)
                                .show();
                    } else {
                        Toast.makeText(getActivity().getApplicationContext(),
                                postId, Toast.LENGTH_LONG).show();
                    }
                }
            };

            Request request = new Request(session, "me/feed", postParams,
                    HttpMethod.POST, callback);

            RequestAsyncTask task = new RequestAsyncTask(request);
            task.execute();
        }

    }
}

What's wrong?

Edit: I'd also like to mention that the text on the "Log In"-Button doesn't change. I don't get an error while authenticating, but as far as I read it should change to "Log Out".

11
  • 3
    Are you using SSO? Do you have the facebook app installed? Are you actually going through the whole app authorization workflow? The error you're seeing suggests that the app authorization workflow never completed (and your fragment's onActivityResult was never called), which is why the Session still thinks there's a pending request.
    – Ming Li
    Commented Jan 10, 2013 at 17:42
  • Yes, I do use SSO and I have the app installed. So when I log in from my app, I usually don't have to go through an authorization workflow, although the first time a dialog pops up asking for permissions.
    – laroe
    Commented Jan 10, 2013 at 21:20
  • Hmm, interesting. The first time you authorized the app (when the dialog popped up), did you get any callbacks saying that the session is OPENED? Because once you auth the app, the code you have here should auto open the session in subsequent restarts of the app.
    – Ming Li
    Commented Jan 10, 2013 at 23:04
  • 1
    Usually the lack of a state transition is indicative of the result not being passed onto the session (i.e. onActivityResult not being called). But your code looks fine and there's no obvious issues. Can you try removing your app from the device, reinstall it, go through the auth (where you get the dialog), and make sure that onActivityResult is called in your fragment?
    – Ming Li
    Commented Jan 11, 2013 at 18:06
  • 1
    Ok, I found the error... My MainActivity is not a subclass of FragmentActivity but some base class I created for my app. This base class however, is a subclass of FragmentActivity. I guess something got lost by this kind of setup. Thank you for your time, @ming-li, you pointed me into the right direction - somehow ;-)
    – laroe
    Commented Feb 18, 2013 at 20:10

9 Answers 9

11

It wasn't easy to figure this out. :-)

If you actually check out exception in onSessionStateChange(), you'll see that it's not really simply stuck in OPENING. That exception contains: java.lang.UnsupportedOperationException: Session: an attempt was made to open a session that has a pending request.

The reason I finally found was that onActivityResult() wasn't called. It's its responsibility to call back to uiHelper and that call will call the finishing part of the login, so it if isn't called, the state stays OPENING forever. Apart from the obvious pieces of advice above, this is what I found and they are all needed for the login to work:

Make sure you use both a fragment and an activity, as described in the FB documentation. Even if the doc hints at an activity being enough, it isn't. You need both even if the activity has nothing else but calling the single fragment.

Make sure you have no history set for the activity (neither in the manifest nor in code).

Make sure you override the fragment's onActivityResult() as per the documentation. And make sure you override the same in the activity as well:

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
  super.onActivityResult(requestCode, resultCode, data);
}

Yes, this seems strange but it doesn't work without it.

7

Finally this works for me. The key points was to add

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    uiHelper.onActivityResult(requestCode, resultCode, data);
}

and

        loginBtn.setFragment(this);
    loginBtn.setUserInfoChangedCallback(new LoginButton.UserInfoChangedCallback() {
        @Override
        public void onUserInfoFetched(GraphUser user) {
            graphUser = user;
        }
    });

Code inside Fragment:

private GraphUser graphUser;
private UiLifecycleHelper uiHelper;

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

    uiHelper = new UiLifecycleHelper(getActivity(), callback);
    uiHelper.onCreate(savedInstanceState);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    return inflater.inflate(R.layout.test_frame, container, false);
}

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);

    view.findViewById(R.id.getMeBtn).setOnClickListener(this);

    LoginButton loginBtn = (LoginButton) view.findViewById(R.id.loginBtn);
    loginBtn.setFragment(this);
    loginBtn.setUserInfoChangedCallback(new LoginButton.UserInfoChangedCallback() {
        @Override
        public void onUserInfoFetched(GraphUser user) {
            graphUser = user;
        }
    });
}

@Override
public void onResume() {
    super.onResume();
    uiHelper.onResume();

    updateUI();
}

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    uiHelper.onSaveInstanceState(outState);
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    uiHelper.onActivityResult(requestCode, resultCode, data);
}

@Override
public void onPause() {
    super.onPause();
    uiHelper.onPause();
}

@Override
public void onDestroy() {
    super.onDestroy();
    uiHelper.onDestroy();
}

private Session.StatusCallback callback = new Session.StatusCallback() {
    @Override
    public void call(Session session, SessionState state, Exception exception) {
        onSessionStateChange(session, state, exception);
    }
};

private void onSessionStateChange(Session session, SessionState state, Exception exception) {
    updateUI();
}

private void updateUI() {
    if (graphUser != null) {
        Log.d("TEST", graphUser.getFirstName());
    }
}

@Override
public void onClick(View view) {
    Session session = Session.getActiveSession();
    Log.d("TEST", "session.getState() = " + session.getState());

    if (session.getState().isOpened()) {
        Request.executeMeRequestAsync(session, new Request.GraphUserCallback() {

            @Override
            public void onCompleted(GraphUser user, Response response) {
                if (user != null) {
                    updateUI();
                }
            }
        });
    }
}
3

I had the same issue. The state was always OPENING. Turns out that @Gabor was wright.

When the session state is OPENING a method onActivityResult gets called, that's for sure.

If you are using a fragment, onActivityResult is called in the ACTIVITY that holds the fragment, and not in the fragment. That is due to the passed context etc.

So i did it like this:

  1. Check what onActivityResults gets called (by logcat) - just to make sure

  2. In that method add a line Session.getActiveSession().onActivityResult(this, requestCode, resultCode, data); Notice that the parameter this is an activity context.

    OR

    Use the UiLifecycleHelper's onActivityResult method, like this: UiHelper.onActivityResult(requestCode, resultCode, data);More about it here

0
2

If anyone else is still having this issue it can be caused by overriding onActivityResult in the Activity and not calling the super method.

Activity

protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
    /* other code here */
    super.onActivityResult(requestCode, resultCode, data);
}

Fragment

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    uiHelper.onActivityResult(requestCode, resultCode, data);
    super.onActivityResult(requestCode, resultCode, data);
}
1

You should implement onActivityResult in the activity not in the fragment. Worked for me.

3
  • This answer is given elsewhere, and not relevant to the OP's question. However, it is a common mistake when this is the result. Commented Apr 16, 2013 at 6:24
  • Well, indeed the ACtivity's onActivityResult is being invoked, and not the Fragment's... it's happening here with me as well... What to do then?? Commented May 31, 2013 at 0:14
  • This clear succinct note totally saved my non-working code .. I did not see this answer any where else. Thanks for adding it here!
    – Gene Bo
    Commented Feb 27, 2015 at 1:16
1

Yet another reason this might occur (as it has for me) is when you're using getChildFragmentManager() - onActivityResult is not passed to nested Fragments, due to a bug. This (having the hosting Fragment pass its received onActivityResult to all children) is what helped me.

0

My Activity's onActivityResult is being invoked... So I did a very very bad hacking there:

public void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == 64206) //Bad hacking
            if(df!=null)
                df.onActivityResult(requestCode, resultCode, data);

df is my fragment. That made the session get open... this is friggin crazy. Very interested to find a better solution for this.

2
  • Ended up having to do the same thing using a fragment dialog as the presentation for the button (and other login buttons) and not wanting all of the code for all logins in the one dialog fragment.
    – ort11
    Commented Nov 4, 2014 at 15:12
  • Just noting that this isnt a bad hack if you check the requestCode properly... if requestCode == Session.DEFAULT_AUTHORIZE_ACTIVITY_CODE Commented Feb 27, 2015 at 19:07
0

In my case, I was trying to change the fragment class from android.support.v4.app.Fragment to android.app.Fragment (I was not supporting pre API-11 Android versions) and omitted the FBAuthButton.setFragment(this); statement. That was actually causing the problem.

-1

I experienced the same problem, I uninstalled my app and reinstalled and it worked!

Not the answer you're looking for? Browse other questions tagged or ask your own question.