When to request permissions with Facebook's new Android SDK 3.0?

With Facebook's new Android SDK 3.0 (that was released a few days ago), the process of authentication has changed.

So how do you request a read permission such as "friends_hometown"?

The following code is how I am trying to do it - but I'm quite sure it's not the way you should do this:

Version 1:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Session.openActiveSession(this, true, new Session.StatusCallback() { // start Facebook login
        @Override
        public void call(Session session, SessionState state, Exception exception) { // callback for session state changes
            if (session.isOpened()) {
                List<String> permissions = new ArrayList<String>();
                permissions.add("friends_hometown");
                session.requestNewReadPermissions(new Session.NewPermissionsRequest(FBImport.this, permissions));
                Request.executeGraphPathRequestAsync(session, "me/friends/?access_token="+session.getAccessToken()+"&fields=id,name,hometown", new Request.Callback() {
                    ...
                });
            }
        }
    });
}

Version 2:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Session currentSession = Session.getActiveSession();
    if (currentSession == null || currentSession.getState().isClosed()) {
        Session session = Session.openActiveSession(this, true, fbStatusCallback); // PROBLEM: NO PERMISSIONS YET BUT CALLBACK IS EXECUTED ON OPEN
        currentSession = session;
    }
    if (currentSession != null && !currentSession.isOpened()) {
        OpenRequest openRequest = new OpenRequest(this).setCallback(fbStatusCallback); // HERE IT IS OKAY TO EXECUTE THE CALLBACK BECAUSE WE'VE GOT THE PERMISSIONS
        if (openRequest != null) {
            openRequest.setDefaultAudience(SessionDefaultAudience.FRIENDS);
            openRequest.setPermissions(Arrays.asList("friends_hometown"));
            openRequest.setLoginBehavior(SessionLoginBehavior.SSO_WITH_FALLBACK);
            currentSession.openForRead(openRequest);
        }
    }
}

What I'm doing is to request the permission as soon as the session is open - but at this point the code is already starting a Graph API request, thus the permission request comes to late ...

Can't you request a permission at the same time you initialize the session?


Solution 1:

I was able to get it to work. It's a modification of your Version 2 sample. The link Jesse provided also helped a ton.

Here is the code I run through when authenticating a user:

private void signInWithFacebook() {

    mSessionTracker = new SessionTracker(getBaseContext(), new StatusCallback() {

        @Override
        public void call(Session session, SessionState state, Exception exception) {
        }
    }, null, false);

    String applicationId = Utility.getMetadataApplicationId(getBaseContext());
    mCurrentSession = mSessionTracker.getSession();

    if (mCurrentSession == null || mCurrentSession.getState().isClosed()) {
        mSessionTracker.setSession(null);
        Session session = new Session.Builder(getBaseContext()).setApplicationId(applicationId).build();
        Session.setActiveSession(session);
        mCurrentSession = session;
    }

    if (!mCurrentSession.isOpened()) {
        Session.OpenRequest openRequest = null;
        openRequest = new Session.OpenRequest(SignUpChoices.this);

        if (openRequest != null) {
            openRequest.setDefaultAudience(SessionDefaultAudience.FRIENDS);
            openRequest.setPermissions(Arrays.asList("user_birthday", "email", "user_location"));
            openRequest.setLoginBehavior(SessionLoginBehavior.SSO_WITH_FALLBACK);

            mCurrentSession.openForRead(openRequest);
        }
    }else {
        Request.executeMeRequestAsync(mCurrentSession, new Request.GraphUserCallback() {
              @Override
              public void onCompleted(GraphUser user, Response response) {
                  Log.w("myConsultant", user.getId() + " " + user.getName() + " " + user.getInnerJSONObject());
              }
            });
    }
}

For testing I ran it through the below code after returning from Facebooks authentication:

public void onActivityResult(int requestCode, int resultCode, Intent data) {
  super.onActivityResult(requestCode, resultCode, data);
  Session.getActiveSession().onActivityResult(this, requestCode, resultCode, data);

  if (mCurrentSession.isOpened()) {
    Request.executeMeRequestAsync(mCurrentSession, new Request.GraphUserCallback() {

          // callback after Graph API response with user object
          @Override
          public void onCompleted(GraphUser user, Response response) {
              Log.w("myConsultant", user.getId() + " " + user.getName() + " " + user.getInnerJSONObject());
          }
        });
    }
}

Solution 2:

I solved the same problem by implementing my own Session.openActiveSession() method:

private static Session openActiveSession(Activity activity, boolean allowLoginUI, StatusCallback callback, List<String> permissions) {
    OpenRequest openRequest = new OpenRequest(activity).setPermissions(permissions).setCallback(callback);
    Session session = new Builder(activity).build();
    if (SessionState.CREATED_TOKEN_LOADED.equals(session.getState()) || allowLoginUI) {
        Session.setActiveSession(session);
        session.openForRead(openRequest);
        return session;
    }
    return null;
}

Solution 3:

I recommend that you read our login tutorial here specifically in step 3. Using the login button that we provide is the most convenient method, (see authButton.setReadPermissions())

EDIT:

To set permissions without using the loginbutton is trickier since you will have to do all the session management by hand. Digging into the source code for the login button, this line of code is probably what you need. It looks like you will need to create your own Session.OpenRequest and set it's attributes such as permissions, audience, and login behavior, then get the current session and call openForRead() on your Session.OpenRequest.

Solution 4:

Although the first question was asked few months ago and there is accepted answers, there is another more elegant solution for requesting more permissions from user while authentication. In addition, Facebook SDK 3.5 was released recently and it would be good to refresh this thread :)

So, the elegant solution comes with this open source library: android-simple-facebook

Setup

Just add next lines in your Activity class:

  1. Define and select permissions you need, like friends_hometown:

    Permissions[] permissions = new Permissions[]
    {
        Permissions.FRIENDS_HOMETOWN,
        Permissions.FRIENDS_PHOTOS,
        Permissions.PUBLISH_ACTION
    };
    

    The great thing here with the permissions, is that you don't need to seperate READ and PUBLISH permissions. You just mention what you need and library will take care for the rest.

  2. Build and define the configuration by putting app_id, namespace and permissions:

    SimpleFacebookConfiguration configuration = new SimpleFacebookConfiguration.Builder()
            .setAppId("625994234086470")
            .setNamespace("sromkuapp")
            .setPermissions(permissions)
            .build();
    
  3. And, create SimpleFacebook instance and set this configuration:

    SimpleFacebook simpleFacebook = SimpleFacebook.getInstance(Activity);
    simpleFacebook.setConfiguration(configuration);
    

Now, you can run the methods like: login, publish feed/story, invite,…

Login

mSimpleFacebook.login(OnLoginListener);

Logout

mSimpleFacebook.logout(OnLogoutListener);

For more examples and usage check this page: https://github.com/sromku/android-simple-facebook#actions-examples