laroe
laroe

Reputation: 147

Facebook Android SDK 3.0 always returns OPENING when logging in

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".

Upvotes: 12

Views: 12173

Answers (9)

axplusb
axplusb

Reputation: 459

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.

Upvotes: 0

mdzeko
mdzeko

Reputation: 932

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

Upvotes: 3

kaay
kaay

Reputation: 1093

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.

Upvotes: 1

G&#225;bor
G&#225;bor

Reputation: 10234

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.

Upvotes: 11

Steven H
Steven H

Reputation: 21

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);
}

Upvotes: 2

Felipe Caldas
Felipe Caldas

Reputation: 2503

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.

Upvotes: 0

Roger Alien
Roger Alien

Reputation: 3060

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();
                }
            }
        });
    }
}

Upvotes: 7

serenskye
serenskye

Reputation: 3467

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

Upvotes: -1

kanaqsasak
kanaqsasak

Reputation: 11

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

Upvotes: 1

Related Questions