54

I''m following this Tutorial but so far I can't make it work, though this is from a year ago or so...

I'm using androidStudo 1.2.2 and FacebookSDK 4.

I want a simple login into facebook using a custom button, like the one shown in this image:

Example

Now, in the example from the tutorial I'm having problems with the Session variable, it says it cannot resolve it, neither getActivity()

Has naybody tried this on FacebookSDK4.0?

Is that a correct approach or maybe there is something more updated?

starball
  • 20,030
  • 7
  • 43
  • 238
NeoVe
  • 3,857
  • 8
  • 54
  • 134

8 Answers8

169

Step 1: First add FrameLayout and make facebook button visibility="gone" and add your custom button. Don't forgot to put xmlns:facebook="http://schemas.android.com/apk/res-auto" in your main layout.

<FrameLayout
        android:id="@+id/FrameLayout1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <com.facebook.login.widget.LoginButton
            android:id="@+id/login_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:visibility="gone" />

        <Button
            android:id="@+id/fb"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:background="#416BC1"
            android:onClick="onClick"
            android:text="FaceBook"
            android:textColor="#ffffff"
            android:textStyle="bold" />
    </FrameLayout>

Step 2: Initialize FacebookSdk in onCreate before inflecting layout.

FacebookSdk.sdkInitialize(this.getApplicationContext());

Step 3: add this into your java file.

callbackManager = CallbackManager.Factory.create();

fb = (Button) findViewById(R.id.fb);
loginButton = (LoginButton) findViewById(R.id.login_button);

List < String > permissionNeeds = Arrays.asList("user_photos", "email",
    "user_birthday", "public_profile", "AccessToken");
loginButton.registerCallback(callbackManager,
new FacebookCallback < LoginResult > () {@Override
    public void onSuccess(LoginResult loginResult) {

        System.out.println("onSuccess");

        String accessToken = loginResult.getAccessToken()
            .getToken();
        Log.i("accessToken", accessToken);

        GraphRequest request = GraphRequest.newMeRequest(
        loginResult.getAccessToken(),
        new GraphRequest.GraphJSONObjectCallback() {@Override
            public void onCompleted(JSONObject object,
            GraphResponse response) {

                Log.i("LoginActivity",
                response.toString());
                try {
                    id = object.getString("id");
                    try {
                        URL profile_pic = new URL(
                            "http://graph.facebook.com/" + id + "/picture?type=large");
                        Log.i("profile_pic",
                        profile_pic + "");

                    } catch (MalformedURLException e) {
                        e.printStackTrace();
                    }
                    name = object.getString("name");
                    email = object.getString("email");
                    gender = object.getString("gender");
                    birthday = object.getString("birthday");
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        });
        Bundle parameters = new Bundle();
        parameters.putString("fields",
            "id,name,email,gender, birthday");
        request.setParameters(parameters);
        request.executeAsync();
    }

    @Override
    public void onCancel() {
        System.out.println("onCancel");
    }

    @Override
    public void onError(FacebookException exception) {
        System.out.println("onError");
        Log.v("LoginActivity", exception.getCause().toString());
    }
});

Step 4: Don't forget to add following code.

@Override
protected void onActivityResult(int requestCode, int responseCode,
Intent data) {
    super.onActivityResult(requestCode, responseCode, data);
    callbackManager.onActivityResult(requestCode, responseCode, data);
}

Step 5: Set your custom button click to FacebookLogin button click.

public void onClick(View v) {
    if (v == fb) {
        loginButton.performClick();
    }
 }

Step 6: For programmatically logout use this.

LoginManager.getInstance().logOut();

Step 7: you can find user logged in or not by profile.

profile = Profile.getCurrentProfile().getCurrentProfile();
if (profile != null) {
    // user has logged in
} else {
    // user has not logged in
}
HassanUsman
  • 1,787
  • 1
  • 20
  • 38
Harvi Sirja
  • 2,472
  • 2
  • 18
  • 19
  • 2
    Super awesome! I'm implementing it, I just have an error on this line: 'loginButton.registerCallback(callbackManager,' 'Cannot resolve method' :( Is there something missing? – NeoVe Jul 11 '15 at 00:05
  • Should I open a new question with my code based on what you answered? – NeoVe Jul 11 '15 at 00:12
  • Ok, solved almost everything, tthere is some issue with step 6 and step 7, don't know exactly where to put them, and the JSON 'name, email, gender, birthday' on first declaration they cannot be solved – NeoVe Jul 11 '15 at 04:43
  • step 6 is use when you need to logout programmatically. for example check first user is logged in with facebook? if yes than use code for logout. otherwise allow user to login. – Harvi Sirja Jul 11 '15 at 04:51
  • step 7 is only use to check user is exist or not. It's not necessary to use this only, you also can use shared preferences for check whether user is logged in or not. – Harvi Sirja Jul 11 '15 at 04:54
  • for JSON issue just declare String name ,email, gender ,birthday; With other vairables. i just declare it global because i want to use it out side the method. you also can use it like String name = object.getString("name"); do same for ,email, gender ,birthday. – Harvi Sirja Jul 11 '15 at 05:00
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/83010/discussion-between-harvisirja-and-neove). – Harvi Sirja Jul 11 '15 at 05:07
  • Hi @HarviSirja I'm here, sorry I was compiling, just an error now but it is maybe caused by memory? – NeoVe Jul 11 '15 at 05:47
  • 2
    I'm going to open a new question for the error, thank you very much. – NeoVe Jul 12 '15 at 00:24
  • 2
    its good .. can give example how to make custom logout button for this . – Vishal Jun 15 '16 at 13:22
  • @NeoVe pls help me the same code is not working for me clicking on button showing me login screen FB then its allowing me app to give access after all no sucess no failure screen stops – Erum Aug 25 '16 at 04:46
  • 2
    Thank you very much, this post is very helpful... Perfectly explained – Krupa Kakkad Jan 05 '17 at 10:00
  • Where is permissionNeeds referenced? – FractalBob May 14 '17 at 23:05
  • 2
    `loginButton.performClick();` this is the meat of the answer, thanks buddy :) – Biskrem Muhammad Dec 12 '17 at 18:16
  • 0 down vote Do Not ask Permission for AccessToken List< String > permissionNeeds = Arrays.asList("user_photos", "email", "user_birthday", "public_profile", "AccessToken"); remove "AccessToken" List< String > permissionNeeds = Arrays.asList("user_photos", "email", "user_birthday", "public_profile"); – Debasish Ghosh Jul 23 '18 at 07:16
  • `List < String > permissionNeeds = Arrays.asList("user_photos", "email", "user_birthday", "public_profile", "AccessToken");` from this line remove `AccessToken` . And it works. I waste a lot time due to this. Hope this will help someone. – Dharmishtha Nov 28 '20 at 05:59
104

IMO part of the selected answer is kind of work-around not the proper solution. So what needs to be changed to make it proper is the following:

  1. Get rid of "com.facebook.login.widget.LoginButton" from your XML

    <com.facebook.login.widget.LoginButton
            android:id="@+id/login_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:visibility="gone" />
    
  2. Get rid of all references to the "LoginButton" in your Java code

  3. Use Facebook's "LoginManager" class which is there for that purpose as follows:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // Some code
        callbackManager = CallbackManager.Factory.create();
        LoginManager.getInstance().registerCallback(
            callbackManager,
            new FacebookCallback < LoginResult > () {
                @Override
                public void onSuccess(LoginResult loginResult) {
                    // Handle success
                }
    
                @Override
                public void onCancel() {
                }
    
                @Override
                public void onError(FacebookException exception) {
                } 
           }
       );
    }
    
    public void onClick(View v) {
       if (v == fb) {
           LoginManager.getInstance().logInWithReadPermissions(
               this,
               Arrays.asList("user_photos", "email", "user_birthday", "public_profile")
           );
       }
    }
    
    // this part was missing thanks to wesely
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        callbackManager.onActivityResult(requestCode, resultCode, data);
    }
    
Shehabic
  • 6,787
  • 9
  • 52
  • 93
  • 1
    Hi @Shehabix, this was years ago, but I'll check your code ASAP and write back! Promise! Indeed, the other answer it wasn't a definitive solution – NeoVe Aug 21 '16 at 19:39
  • So how can implement loginbutton.setFragment(this);?. if I didn't add this it will not work for a fragment.Also this requires a facebook login button.Can you tellme a solution for this? – Kiran Benny Joseph Sep 23 '16 at 14:10
  • The solution above is for custom login buttons, as per the original question – Shehabic Sep 23 '16 at 14:12
  • I also mentioning about the same.On a custombutton it is not called onActivityResult method when we didn't use setFragment method.I am talking about fragment.Please help..:) – Kiran Benny Joseph Sep 23 '16 at 14:19
  • in general you get in the Activity, pass it to your fragment from there, I usually do that for anything that returns a result. – Shehabic Sep 23 '16 at 14:22
  • 1
    That's a clean solution. Much better, than that accepted answer-workaround. – ViliusK Nov 30 '16 at 14:32
  • 1
    Also, you might want to update button's label from `Log in` to `Log out` depending on `LoginManager`'s state. – ViliusK Nov 30 '16 at 14:33
  • 10
    It should be the ACCEPTED answer as it provides a clean and legal way of maneuvering the login button. – aB9 Jan 28 '17 at 20:42
  • 1
    ACCEPTED answer! – Asad Sep 02 '18 at 15:41
  • This is the best answer to the OPs question – Tyler Dec 29 '18 at 17:05
16

This is very simple. Add a button in the layout file like

<Button
    android:layout_width="200dp"
    android:layout_height="wrap_content"
    android:text="Login with facebook"
    android:textColor="#ffff"
    android:layout_gravity="center"
    android:textStyle="bold"
    android:onClick="fbLogin"
    android:background="@color/colorPrimary"/>

And in the onClick place the LoginManager's registercallback() method Becuse the this method automatically executes.

like:

  public void fbLogin(View view)
{
    // Before Edit:
    // LoginManager.getInstance().logInWithReadPermissions(this, Arrays.asList("user_photos", "email", "public_profile", "user_posts" , "AccessToken"));

    LoginManager.getInstance().logInWithReadPermissions(this, Arrays.asList("user_photos", "email", "public_profile", "user_posts"));
    LoginManager.getInstance().logInWithPublishPermissions(this, Arrays.asList("publish_actions"));
    LoginManager.getInstance().registerCallback(callbackManager,
            new FacebookCallback<LoginResult>()
            {
                @Override
                public void onSuccess(LoginResult loginResult)
                {
                    // App code
                }

                @Override
                public void onCancel()
                {
                    // App code
                }

                @Override
                public void onError(FacebookException exception)
                {
                    // App code
                }
            });
}

Have Fun

Marcel Bro
  • 4,907
  • 4
  • 43
  • 70
Harpreet Singh
  • 329
  • 3
  • 8
13

(don't have enough reputation to add a comment ...)

I tried the @Shehabix's answer, can't receive callback until I add this

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

then it works fine.

Wesely
  • 1,425
  • 14
  • 23
10

The Simple answer is add facebookButton.performClick() inside cutomBtn.setOnClickListener

    <!-- connect with Facebook -->
    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

        <com.facebook.login.widget.LoginButton
            android:id="@+id/fb_connect"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:textSize="@dimen/login_fb_font_size"
            android:visibility="invisible" />

        <LinearLayout
            android:id="@+id/mfb_connect"
            style="@style/facebook_button">

            <ImageView
                style="@style/login_connect_icon"
                android:src="@drawable/ic_facebook" />

            <TextView
                style="@style/login_connect_text"
                android:text="@string/login_connect_facebook" />
        </LinearLayout>
    </RelativeLayout>

...

private LoginButton fb_connect;
private LinearLayout mfb_connect;

...

    // the button from facebook sdk
    fb_connect = (LoginButton) findViewById(R.id.fb_connect);
    // handle the click from my custom button to perfrom action click on facebook sdk button
    mfb_connect = (LinearLayout) findViewById(R.id.mfb_connect);
    mfb_connect.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            fb_connect.performClick();
        }
    });

That will achieve this:

enter image description here

Biskrem Muhammad
  • 4,074
  • 3
  • 31
  • 38
2

i just call the facebook default button from my custom button and left every single line of code as documented. and bang it works successfully.

public class SignInFragment extends Fragment implements
   FragmentChangeListener{
   private Button facebook



    private FirebaseAuth firebaseAuth;
    private CallbackManager mCallBackManager;
    private LoginButton loginButton;


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        // Inflate the layout for this fragment
        View view =  inflater.inflate(R.layout.fragment_sign_in, container, false);

        facebook = view.findViewById(R.id.facebookButton);
        loginButton = view.findViewById(R.id.facebookBtn);

        firebaseAuth = FirebaseAuth.getInstance();

        facebook.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                loginButton.callOnClick();
            }
        });

        loginButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mCallBackManager = CallbackManager.Factory.create();
                loginButton.setFragment(SignInFragment.this);
                loginButton.setPermissions("email","public_profile");
                loginButton.registerCallback(mCallBackManager, new FacebookCallback<LoginResult>() {
                    @Override
                    public void onSuccess(LoginResult loginResult) {
                        handleFacebookAccessToken(loginResult.getAccessToken());
                    }

                    @Override
                    public void onCancel() {
                        Toast.makeText(getContext(),"Is Cancelled",Toast.LENGTH_LONG).show();
                    }

                    @Override
                    public void onError(FacebookException error) {
                        Toast.makeText(getContext(),error.getMessage(),Toast.LENGTH_LONG).show();
                    }
                });
            }
        });

        return view;
    }


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

        mCallBackManager.onActivityResult(requestCode, resultCode, data);
      
    }

    private void handleFacebookAccessToken(AccessToken token) {
        AuthCredential credential = FacebookAuthProvider.getCredential(token.getToken());
        firebaseAuth.signInWithCredential(credential)
                .addOnCompleteListener(getActivity(), new OnCompleteListener<AuthResult>() {
                    @Override
                    public void onComplete(@NonNull Task<AuthResult> task) {
                        if (task.isSuccessful()) {
                            // Sign in success, update UI with the signed-in user's information
                            FirebaseUser user = firebaseAuth.getCurrentUser();
                            startActivity(new Intent(getContext(),MainActivity.class));
                        } else {
                            // If sign in fails, display a message to the user.
                            Toast.makeText(getContext(), task.getException().getMessage(),
                                    Toast.LENGTH_SHORT).show();
                        }

                    }
                });
    } 
 }
  • 1
    This worked for me perfectly in fragment. i just called the Default Facebook login button which i kept Visibility GONE in my XML from my custom button. – Emon Hossain Munna May 26 '20 at 16:04
1

Complementing the answers by Harvi and Shehabix, I would suggest to add this method as this registers the authentication in Firebase Auth.

This method should be called inside "On Success" of LoginManager.

private void handleFacebookAccessToken(AccessToken token) {

    AuthCredential credential = FacebookAuthProvider.getCredential(token.getToken());
    mAuth.signInWithCredential(credential)
            .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
                @Override
                public void onComplete(@NonNull Task<AuthResult> task) {
                    if (task.isSuccessful()) {

                        //Toast.makeText(MainActivity.this, "Autenticando",Toast.LENGTH_SHORT).show();

                    } else {
                        // If sign in fails, display a message to the user.
                        Toast.makeText(MainActivity.this, "Authentication failed.",
                                Toast.LENGTH_SHORT).show();
                        //updateUI(null);
                    }

                }
            });
}
nj2237
  • 1,220
  • 3
  • 21
  • 25
Thiago Silva
  • 670
  • 6
  • 18
0
try this 

// Facebook Button
LoginButton loginButton = findViewById(R.id.loginbtn);
loginButton.setVisibility(View.GONE)



// Your Custom Button

 binding.fbBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                loginButton.callOnClick();
            }
        });
smartRk
  • 1
  • 1