time

Android Facebook SDK integration tutorial

android_facebook.png In this tutorial, we are going to learn how to integrate Facebook SDK for Android application login and also we will retrieve and display Facebook user profile information. Using the latest version of Facebook's SDK for Android, it takes only a few minutes to add this feature to your app.

To integrate Facebook login in your android application, Facebook account is an essential requirements.

Add Facebook SDK to your project

Step 1. Head over to Facebook Developer website to create your app and get your application ID by clicking on the Add a new app button.

Step 2. Go to Dashboard and click on Choose Platform tehn select Android as your platform from the window pop-up.

Step 3. The Facebook SDK is available on Maven Central. Add the Maven Central Repository to build.gradle (project):

repositories {
    mavenCentral()
}

Step 4. Add compile 'com.facebook.android:facebook-android-sdk:[4,5)' to your build.gradle (module) dependencies.

Step 5. Open your strings.xml file and add a new string with the name facebook_app_id containing the value of your Facebook App ID

<string name="facebook_app_id">111</string>

Step 6. Open AndroidManifest.xml file and ddd a uses-permission element to the manifest

<uses-permission android:name="android.permission.INTERNET"/>

Step 7. Add a meta-data element to the application element

<application android:label="@string/app_name" ...>
    ...
    <meta-data android:name="com.facebook.sdk.ApplicationId" android:value="@string/facebook_app_id"/>
    ...
</application>

Step 8. In the bottom form, enter the package name of your app and the name of your Activity.

Step 9. To fill in the Key Hashes field, open a terminal window and run the keytool command to generate a key hash using the debug keystore located at ~/.android/debug.keystore. This is what the command should look like.

# on MAC and any Linux OS
keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore | openssl sha1 -binary | openssl base64

The default password for the debug keystore is android. Enter that password when prompted. The output of the command should be a string of 28 characters. Copy it, go back to your browser, and paste the string into the Key Hashes field.

Also you can get hash key using following snippet

public void getFacebookHashKey(Context context) {
    try {
        PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
        for (Signature signature : info.signatures) {
            MessageDigest md = MessageDigest.getInstance("SHA");
            md.update(signature.toByteArray());
            String hashKey = new String(Base64.encode(md.digest(), 0));
            Log.i(TAG, "getFacebookHashKey(): " + hashKey);
        }
    } catch (NoSuchAlgorithmException e) {
        Log.e(TAG, "getFacebookHashKey()", e);
    } catch (Exception e) {
        Log.e(TAG, "getFacebookHashKey()", e);
    }
}

Creating Login activity

Add two widgets to main_activity.xml file.

  • a LoginButton to allow the user to log in to Facebook
  • a TextView to display the result of the latest login attempt

You can place them inside a RelativeLayout.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <com.facebook.login.widget.LoginButton
        android:id="@+id/btnLogin"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        />

    <TextView
        android:id="@+id/tvStatus"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="18sp"
        />
</LinearLayout>

Now let's modify MainActivity activity. Remember that the name of this class and the package that it belongs to should match the values you entered while registering your app with Facebook.

Declare the widgets you defined in the activity's layout as fields of this class and declare a CallbackManager as another field.

private TextView tvStatus;
private LoginButton btnLogin;
private CallbackManager callbackManager;

The CallbackManager is used to manage the callbacks used in the app.

private CallbackManager callbackManager;

Next, initialize your instance of CallbackManager using the CallbackManager.Factory.create method.

callbackManager = CallbackManager.Factory.create();

Call setContentView to set the layout defined above as the layout of this Activity and then use findViewById to initialize the widgets.

setContentView(R.layout.main_activity);
tvStatus = (TextView) findViewById(R.id.tvStatus);
btnLogin = (LoginButton)findViewById(R.id.btnLogin);
tvStatus.setText("Status: ready");

It's time to create a callback to handle the results of the login attempts and register it with the CallbackManager. Custom callbacks should implement FacebookCallback. The interface has methods to handle each possible outcome of a login attempt:

  • If the login attempt is successful, onSuccess is called.
  • If the user cancels the login attempt, onCancel is called.
  • If an error occurs, onError is called.

To register the custom callback, use the registerCallback method.

When the onSuccess method is called, a LoginResult is passed as a parameter. Retrieve the access token it contains using getAccessToken and use its getUserId method to get the user's ID. To get the token in the form of a String, use getToken.

Tapping the login button starts off a new Activity, which returns a result. To receive and handle the result, override the onActivityResult method of your Activity and pass its parameters to the onActivityResult method of CallbackManager.

Below is full code of MainActivity.

public class MainActivity extends AppCompatActivity {
    private TextView tvStatus;
    private LoginButton btnLogin;
    private CallbackManager callbackManager;

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

        callbackManager = CallbackManager.Factory.create();

        setContentView(R.layout.activity_main);

        tvStatus = (TextView) findViewById(R.id.tvStatus);
        btnLogin = (LoginButton)findViewById(R.id.btnLogin);
        tvStatus.setText("Status: ready");

        btnLogin.registerCallback(callbackManager, new FacebookCallback<LoginResult>() {
            @Override
            public void onSuccess(LoginResult loginResult) {
                AccessToken token = loginResult.getAccessToken();
                tvStatus.setText(String.format("User ID: %s\n Auth Token: %s", 
                    token.getUserId(), token.getToken()));
            }

            @Override
            public void onCancel() {
                tvStatus.setText("Status: Login attempt canceled.");
            }

            @Override
            public void onError(FacebookException e) {
                tvStatus.setText("Status: Login attempt failed.");
            }
        });
    }

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

Creating Profile activity

Let's add some more code to get Profile and track it updates. Layout is the same.

public class MainActivity extends AppCompatActivity {
    private TextView tvStatus;
    private LoginButton btnLogin;
    private CallbackManager callbackManager;
    ProfileTracker profileTracker;

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

        callbackManager = CallbackManager.Factory.create();

        profileTracker = new ProfileTracker() {
            @Override
            protected void onCurrentProfileChanged(Profile oldProfile, Profile newProfile) {
                updateProfileView(newProfile);
            }
        };
        profileTracker.startTracking();

        setContentView(R.layout.activity_main);

        tvStatus = (TextView) findViewById(R.id.tvStatus);
        btnLogin = (LoginButton)findViewById(R.id.btnLogin);

        try {
            Profile profile = Profile.getCurrentProfile();
            updateProfileView(profile);
        } catch (NullPointerException e) {
            tvStatus.setText("Status: ready");
        }

        btnLogin.registerCallback(callbackManager, new FacebookCallback<LoginResult>() {
            @Override
            public void onSuccess(LoginResult loginResult) {
                Profile profile = Profile.getCurrentProfile();
                updateProfileView(profile);
            }

            @Override
            public void onCancel() {
                tvStatus.setText("Status: Login attempt canceled.");
            }

            @Override
            public void onError(FacebookException e) {
                tvStatus.setText("Status: Login attempt failed.");
            }
        });
    }

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

    protected void onStop() {
        super.onStop();
        profileTracker.stopTracking();
    }

    @Override
    public void onResume() {
        super.onResume();
        Profile profile = Profile.getCurrentProfile();
        updateProfileView(profile);
    }

    private void updateProfileView(Profile profile) {
        if (profile != null) {
            String status = String.format("Status: logged!\nUser: %s", profile.getName());
            Log.d("DBG", profile.getProfilePictureUri(200,200).toString());
            tvStatus.setText(status);
        } else {
            tvStatus.setText("Status: ready");
        }
    }
}

You can see screen of Activity in end of tutorial.

How to check login status and logout

You can check log in status usign following method

public boolean isLoggedIn() {
    AccessToken accessToken = AccessToken.getCurrentAccessToken();
    return accessToken != null;
}

For log out use following snippet

public void logOut() {
    LoginManager.getInstance().logOut();
}

How to define custom login button

Define Button somewhere in layout

<Button
    android:id="@+id/btnLogin"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="LOG IN WITH FACEBOOK"
    android:layout_below="@id/vDivider"
    android:layout_centerHorizontal="true"
    android:onClick="logIn"/>

Snippet of MainActivity.java.

public class MainActivity extends AppCompatActivity {
    private CallbackManager callbackManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // FACEBOOK LOGIN
        callbackManager = CallbackManager.Factory.create();

        if (isLoggedIn()) {
            Profile profile = Profile.getCurrentProfile();
            Log.d(Constants.TAG, profile.getFirstName());
        }
    }

    public void logIn(View v) {
        LoginManager.getInstance().logInWithReadPermissions(MainActivity.this, Arrays.asList("public_profile"));

        LoginManager.getInstance().registerCallback(callbackManager,
            new FacebookCallback<LoginResult>() {
                @Override
                public void onSuccess(LoginResult loginResult) {
                    AccessToken token = loginResult.getAccessToken();
                    Toast.makeText(MainActivity.this, String.format("User ID: %s\n Auth Token: %s", 
                        token.getUserId(), token.getToken()), Toast.LENGTH_SHORT).show();
                }

                @Override
                public void onCancel() {}

                @Override
                public void onError(FacebookException e) {
                    Log.d(Constants.TAG, e.toString());
                }
            });
    }

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

The logInWithPermissionName methods always open a UI and prompt someone for additional permissions if needed. To get addiitional permissions from someone, make this request with the Facebook SDK for Android:

LoginManager.getInstance().logInWithReadPermissions(
    MainActivity.this,
    Arrays.asList("email"));

How to get user profile

After login you can get basic user profile using

Profile profile = Profile.getCurrentProfile();
Log.d(Constants.TAG, profile.getName());
Log.d(Constants.TAG, profile.getUri(200, 200));

Also you can use Graph API to get additional information

GraphRequest request = GraphRequest.newMeRequest(
    AccessToken.getCurrentAccessToken(),
    new GraphRequest.GraphJSONObjectCallback() {
        @Override
        public void onCompleted(JSONObject object, GraphResponse response) {
            Log.d(Constants.TAG, object.toString());
        }
    });
Bundle parameters = new Bundle();
parameters.putString("fields", "id,name,link,email,gender,birthday,work");
request.setParameters(parameters);
request.executeAsync();

Creating Share activity

In this example we'll implement sharing from your Android app to Facebook. When someone shares from your app, their content appears on their Timeline and may appear in their friends' News Feeds.

First, you need to set up a ContentProvider in your AndroidManifest.xml where 111 is your app ID. Add it before </application>

<provider android:authorities="com.facebook.app.FacebookContentProvider111"
          android:name="com.facebook.FacebookContentProvider"
          android:exported="true"/>

When people share links from your app to Facebook, it includes attributes that show up in the post:

  • a contentURL, the link to be shared
  • a contentTitle that represents the title of the content in the link (deprecated)
  • a imageURL, the URL of thumbnail image that will appear on the post (deprecated)
  • a contentDescription of the content, usually 2-4 sentences (deprecated)
ShareLinkContent linkContent = new ShareLinkContent.Builder()
    .setContentUrl(Uri.parse("http://en.proft.me"))
    .build();
ShareDialog dlgShare = new ShareDialog(getActivity());
dlgShare.show(linkContent);

You can debug share link at Sharing Debugger.

People can share photos from your app to Facebook with the ShareDialog or with a custom interface.

  • The photos must be less than 12MB in size
  • People need the native Facebook for Android app installed, version 7.0 or higher

If you want to share photo you must request more permissions when declare LoginButton like this

btnLogin.setReadPermissions("email,publish_actions");

Build your share content for photos into the SharePhotoContent model. For a list of all attributes, see SharePhotoContent reference.

Bitmap icon = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);

SharePhoto photo = new SharePhoto.Builder()
    .setBitmap(icon)
    .setCaption("Android icon")
    .build();
SharePhotoContent content = new SharePhotoContent.Builder()
    .addPhoto(photo)
    .build();
dlgShare.show(content);

The Like button is a quick way for people to share content with their friends. A tap on the Like button will like pieces of content from your app and share them on Facebook. The Like button can be used to like a Facebook Page or any Open Graph object and can be referenced by URL or ID. To add a Like button add the following code snippet to your view:

btnLike = (LikeView) findViewById(R.id.btnLike);
btnLike.setObjectIdAndType("http://en.proft.me", LikeView.ObjectType.PAGE);

Full activity_main.xml layout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <com.facebook.login.widget.LoginButton
        android:id="@+id/btnLogin"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"/>

    <TextView
        android:id="@+id/tvStatus"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="18sp"/>

    <Button
        android:id="@+id/btnShareLink"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="gone"
        android:text="Share link"
        android:onClick="shareLink" />

    <Button
        android:id="@+id/btnShareImage"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="gone"
        android:text="Share image"
        android:onClick="shareImage"/>

    <com.facebook.share.widget.LikeView
        android:id="@+id/btnLike"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="gone"/>
</LinearLayout>

Full code of MainActivity

public class MainActivity extends AppCompatActivity {
    private TextView tvStatus;
    private LoginButton btnLogin;
    private CallbackManager callbackManager;
    ProfileTracker profileTracker;
    Button btnShareLink, btnShareImage;
    LikeView btnLike;
    ShareDialog dlgShare;

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

        callbackManager = CallbackManager.Factory.create();

        profileTracker = new ProfileTracker() {
            @Override
            protected void onCurrentProfileChanged(Profile oldProfile, Profile newProfile) {
                updateProfileView(newProfile);
            }
        };
        profileTracker.startTracking();

        setContentView(R.layout.activity_main);

        dlgShare = new ShareDialog(this);

        tvStatus = (TextView) findViewById(R.id.tvStatus);
        btnLogin = (LoginButton)findViewById(R.id.btnLogin);

        btnShareLink = (Button) findViewById(R.id.btnShareLink);
        btnShareImage = (Button) findViewById(R.id.btnShareImage);

        btnLike = (LikeView) findViewById(R.id.btnLike);
        btnLike.setObjectIdAndType("http://en.proft.me", LikeView.ObjectType.PAGE);

        try {
            Profile profile = Profile.getCurrentProfile();
            updateProfileView(profile);
        } catch (NullPointerException e) {
            tvStatus.setText("Status: ready");
        }

        dlgShare.registerCallback(callbackManager, new FacebookCallback<Sharer.Result>() {
            @Override
            public void onSuccess(Sharer.Result result) {
                Toast.makeText(MainActivity.this, "Successfully shared", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onCancel() {
                Toast.makeText(MainActivity.this, "Sharing cancelled", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onError(FacebookException e) {
                Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
            }
        });

        //
        // LOGIN callback
        //

        btnLogin.setReadPermissions("email,publish_actions");

        btnLogin.registerCallback(callbackManager, new FacebookCallback<LoginResult>() {
            @Override
            public void onSuccess(LoginResult loginResult) {
                Profile profile = Profile.getCurrentProfile();
                updateProfileView(profile);
            }

            @Override
            public void onCancel() {
                tvStatus.setText("Status: Login attempt canceled.");
            }

            @Override
            public void onError(FacebookException e) {
                tvStatus.setText("Status: Login attempt failed.");
            }
        });

        //
        // SHARE LINK callback
        //
        btnShareLink.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (ShareDialog.canShow(ShareLinkContent.class)) {
                    ShareLinkContent linkContent = new ShareLinkContent.Builder()
                            .setContentUrl(Uri.parse("http://en.proft.me"))
                            .setContentTitle("Proft blog")
                            .setImageUrl(Uri.parse("http://en.proft.me/static/img/melogo.png"))
                            .setContentDescription("Test message from Android.")
                            .build();
                    dlgShare.show(linkContent);
                }
            }
        });

        //
        // SHARE IMAGE callback
        //
        btnShareImage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (ShareDialog.canShow(SharePhotoContent.class)) {
                    Bitmap icon = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);

                    SharePhoto photo = new SharePhoto.Builder()
                            .setBitmap(icon)
                            .setCaption("My caption")
                            .build();
                    SharePhotoContent content = new SharePhotoContent.Builder()
                            .addPhoto(photo)
                            .build();
                    dlgShare.show(content);
                }
            }
        });
    }

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

    protected void onStop() {
        super.onStop();
        profileTracker.stopTracking();
    }

    @Override
    public void onResume() {
        super.onResume();
        Profile profile = Profile.getCurrentProfile();
        updateProfileView(profile);
    }

    private void updateProfileView(Profile profile) {
        if (profile != null) {
            String status = String.format("Status: logged!\nUser: %s", profile.getName());
            Log.d("VVV", profile.getProfilePictureUri(200,200).toString());
            tvStatus.setText(status);
            btnShareLink.setVisibility(View.VISIBLE);
            btnShareImage.setVisibility(View.VISIBLE);
            btnLike.setVisibility(View.VISIBLE);
        } else {
            tvStatus.setText("Status: ready");
            btnShareLink.setVisibility(View.GONE);
            btnShareImage.setVisibility(View.GONE);
            btnLike.setVisibility(View.GONE);
        }
    }
}

Result

android_facebook_share.png

How to get user's photos

To request user's photos I'm going to use Graph API. The Android SDK has support for integrating with Facebook Graph API. With the GraphRequest and GraphResponse classes, you can make requests and get responses in JSON asynchronously.

First of all we need clasess for deserialization list of photos and one photo with url.

File FacebookResponsePhotos.java

class FacebookResponsePhotos {
    class Photo {
        String created_time;
        String id;
    }

    class Cursors {
        String before;
        String after;
    }

    class Paging {
        Cursors cursors;
    }

    public List<Photo> data;
    public Paging paging;

    public List<Photo> getPhotos() {
        return data;
    }
}

File FacebookResponsePhoto.java

class FacebookResponsePhoto {
    class Image {
        int height;
        int width;
        String source;
    }

    public List<Image> images;
    public String id;

    public List<Image> getImages() {
        return images;
    }

    public String getHeight960() {
        String url = "";
        if (images.size() > 0) {
            for(Image img : images){
                if(img.height == 960) {
                    url = img.source;
                    break;
                }
            }
        }
        return url;
    }
}

Second we need permission for user_photos.

btnLogin = (LoginButton)findViewById(R.id.btnLogin);
btnLogin.setReadPermissions("public_profile,user_photos");

After successful login we can use following method to get list of photos and one photo with url

public void getUserPhotos() {
    new GraphRequest(AccessToken.getCurrentAccessToken(), "/me/photos", null, HttpMethod.GET,
        new GraphRequest.Callback() {
            public void onCompleted(GraphResponse response) {
                Log.d(Constants.TAG, "PHOTOS " + response.getJSONObject().toString());

                FacebookResponsePhotos fbResponse = new Gson().fromJson(
                    response.getJSONObject().toString(), FacebookResponsePhotos.class);

                for (FacebookResponsePhotos.Photo p : fbResponse.getPhotos()) {
                    getPhotoUrl(p.id);
                }
            }
        }
    ).executeAsync();
}

public void getPhotoUrl(String photoID) {
    new GraphRequest(AccessToken.getCurrentAccessToken(), "/" + photoID + "?fields=images", null, HttpMethod.GET,
            new GraphRequest.Callback() {
                public void onCompleted(GraphResponse response) {
                    Log.d(Constants.TAG, "PHOTO " + response.getJSONObject().toString());
                    FacebookResponsePhoto fbResponse = new Gson().fromJson(
                        response.getJSONObject().toString(), FacebookResponsePhoto.class);
                    Log.d(TAG, "onCompleted URL: " + fbResponse.getHeight960());
                }
            }
    ).executeAsync();
}
comments powered by Disqus