37

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?

KevinM
  • 1,799
  • 4
  • 28
  • 58
caw
  • 30,999
  • 61
  • 181
  • 291
  • http://stackoverflow.com/q/17878417/1503130 I have an issue using this method , I have posted a thread with it. – Prateek Jul 26 '13 at 10:09

4 Answers4

67

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());
          }
        });
    }
}
KevinM
  • 1,799
  • 4
  • 28
  • 58
  • Thank you? Does this work in all cases? Have you tested it? I've seen that it is important to check if the Facebook auth also works on subsequent calls when there already is a session and it needs to reuse that. Apart from that, one should put the actual request (which you have in `onActivityResult()`) into the auth code's callback method. – caw Dec 22 '12 at 13:30
  • Yes I've tested it with subsequent calls to existing sessions and it works. I just refactor my code with your suggestion. Thanks – KevinM Dec 22 '12 at 14:54
  • @MarcoW. Even when I clear the data for the official facebook app my app does remember the user details, how could I avoid this. Or it would be better if I ask , how do I logout from the app itself. – Prateek Jan 27 '13 at 09:33
  • @pKs: You could even change your Facebook password and the user will not be logged out. This is due to the session that is created and valid for some time (probably a few hours). – caw Jan 27 '13 at 12:39
  • @MarcoW. I got the point behind this , but my thing is how could I actualy log out of my app once I have logged in in a nutshell for sdk 3.0 – Prateek Jan 27 '13 at 13:30
  • You can drop the access token and require the user to log in again. – caw Jan 27 '13 at 17:50
  • 1
    Can you tell me how could I change your example so that I didn't use the SessionTracker? Because SessionTracker is an internal class and they can be modified any time without warning. Any suggestions? – Karlis Jul 25 '13 at 08:30
  • http://stackoverflow.com/q/17878417/1503130 I have an issue using this method , I have posted a thread with it. – Prateek Jul 26 '13 at 10:08
  • I am having an issue in this. I am using this to publish on timeline but the mcurrentSession object is always closed therefore it never goes in the else part when I call this function. Please refer to http://stackoverflow.com/questions/19191683/publishing-on-user-timeline-using-facebook-android-sdk – Vyper Oct 04 '13 at 22:33
55

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;
}
Pedro Oliveira
  • 1,251
  • 10
  • 7
  • 1
    you mean you copy paste the private method from facebook-android-sdk code :) – tbruyelle Apr 04 '13 at 14:32
  • 2
    This Builder class is an inner class inside Session class for those who are asking where is it from. – Raphael Oliveira Apr 17 '13 at 20:59
  • @MuhammadBabar I just copied the private method Session.openActiveSession and added a parameter to receive the permissions. – Pedro Oliveira Apr 29 '13 at 22:34
  • @PedroOliveira so can't we do the permission thing by the overrided openActiveSession() – Muhammad Babar Apr 30 '13 at 04:37
  • +1 works for me thanks. However this is just for read permissions. My app crashed when I had publish permission together with read permissions. When I removed it them app worked fine. Is there any idea how to add publish permissions as well? – Hesam May 04 '13 at 15:20
  • @YogeshTatwal you have to call this method at place where you are calling Session.openActiveSession with arraylist of permissions. – Vikram Jun 04 '13 at 11:46
  • 7
    Very Elegant solution, this should be voted as the right one. – Yosi Taguri Jul 03 '13 at 14:19
  • If you're using the FB webview and you have a session but not all the permissions, this will pop up a UI even if allowLoginUI is false. – funkybro Feb 20 '14 at 19:44
  • Worked for me as well , I was just wondering why my facebook session is skipping permission all the time and This made it very clear and elegant – Cyph3rCod3r Mar 27 '14 at 07:44
  • @Dr.aNdRO because your user has already granted permission so it is skipping permission all the time. You can go to User's app setting and remove the app. This will make native app to ask permissions for Facebook session – AkshayT Apr 30 '14 at 12:32
16

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.

Jesse Chen
  • 4,928
  • 1
  • 20
  • 20
  • Thanks, Jesse, I've read that tutorial, but I was looking for a way to do it without the button. As your tutorial states, you can also log the user in directly. And I guess something like the code posted in my question should be the solution. – caw Dec 17 '12 at 22:44
  • Thank you! Updated my question as well ("Version 2"): Is this what you intended? I see a problem there, which I marked with comments: As soon as a new session is opened, the given callback method is executed (where one does the requests, usually). But at this point, we don't have the necessary permissions yet, in some cases. – caw Dec 17 '12 at 23:18
  • Marco, I'm trying to conquer the same problem right now. I was just about to post my own question when I saw your. I had the same logic that you are trying. I also see the problem with how they have the callback method executed immediately. – KevinM Dec 18 '12 at 02:18
  • @JesseChen hi i just want to know is there any way i can signup with facebook within my android app? – Muhammad Babar Apr 29 '13 at 07:55
  • @JesseChen I see you work at Facebook: you should really fix this to be the same as in the ios SDK, almost every app that is sensitive to design would have it's own facebook button. – Yosi Taguri Jul 03 '13 at 14:24
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

sromku
  • 4,663
  • 1
  • 36
  • 37