Introduction

This tutorial describes how to integrate an Android application with Facebook. We are going to authenticate using OAuth and publish a wall post on Facebook from an Android application. We will be using the Facebook Android SDK to do this.
This article is aimed at Android developers.

UPDATE Dec 04, 2011: The last couple of months there were quite some changes to the Facebook API and the Android Facebook SDK that deprecated the code in this tutorial. The “stream_publish” event was deprecated in favour of the graph API(“me/feed”) and the Facebook class from the SDK needs an app_id in its constructor now(before, it didnt and the app_id needed to be passed every time to the Facebook.authorize and Facebook.request methods). I updated the tutorial below so it is up to date again.

Context

We will implement a ShareOnFacebook activity, which will be usable standalone, and will be callable from code like:

Intent postOnFacebookWallIntent = new Intent(this, ShareOnFacebook.class);
postOnFacebookWallIntent.putExtra("facebookMessage", "is integrating stuff again.");
startActivity(postOnFacebookWallIntent);

This will result in a Facebook wallpost like this:

Getting an application id from Facebook

First of all, you will need a Facebook application id. To get one, you need a facebook account first, login to that account and go to http://www.facebook.com/developers. After granting permission to the Developer app, there will be a button “Setup new application” available. After pressing this button, you will be asked for the name of your application, after which you will need to provide the application details, as in the following form:

After providing all the details, your app will be added to your (private) “current applications” on Facebook and you will be provided with an application id. Note down that application id somewhere. Note that for now the new app is not yet submitted to Facebook, which is what you want, since it is not finished yet.

The Facebook Android SDK

The facebook website API is always called through http. It will process input parameters and serve response pages/json snippets.
The Facebook Android SDK will wrap these http calls(authenticate, publish wall post,..) in an android dialog box with a webview, and enable developers to register listeners on certain events for these dialog boxes, such as unsuccesful and succesful authentication. That way, it becomes quite easy to integrate the http api in a native android application.

You can get it at http://github.com/facebook/facebook-android-sdk/ through “Downloads”. From the resulting downloaded zip file, add everything under facebooksrc to your own source folder.

ShareOnFacebook activity

Below is the complete source code for our activity. For some people, this will probably be the only interesting part.

public class ShareOnFacebook extends Activity{

	private static final String APP_ID = "269876589726953";
	private static final String[] PERMISSIONS = new String[] {"publish_stream"};

	private static final String TOKEN = "access_token";
        private static final String EXPIRES = "expires_in";
        private static final String KEY = "facebook-credentials";

	private Facebook facebook;
	private String messageToPost;

	public boolean saveCredentials(Facebook facebook) {
        	Editor editor = getApplicationContext().getSharedPreferences(KEY, Context.MODE_PRIVATE).edit();
        	editor.putString(TOKEN, facebook.getAccessToken());
        	editor.putLong(EXPIRES, facebook.getAccessExpires());
        	return editor.commit();
    	}

    	public boolean restoreCredentials(Facebook facebook) {
        	SharedPreferences sharedPreferences = getApplicationContext().getSharedPreferences(KEY, Context.MODE_PRIVATE);
        	facebook.setAccessToken(sharedPreferences.getString(TOKEN, null));
        	facebook.setAccessExpires(sharedPreferences.getLong(EXPIRES, 0));
        	return facebook.isSessionValid();
    	}

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

		facebook = new Facebook(APP_ID);
		restoreCredentials(facebook);

		requestWindowFeature(Window.FEATURE_NO_TITLE);

		setContentView(R.layout.facebook_dialog);

		String facebookMessage = getIntent().getStringExtra("facebookMessage");
		if (facebookMessage == null){
			facebookMessage = "Test wall post";
		}
		messageToPost = facebookMessage;
	}

	public void doNotShare(View button){
		finish();
	}
	public void share(View button){
		if (! facebook.isSessionValid()) {
			loginAndPostToWall();
		}
		else {
			postToWall(messageToPost);
		}
	}

	public void loginAndPostToWall(){
		 facebook.authorize(this, PERMISSIONS, Facebook.FORCE_DIALOG_AUTH, new LoginDialogListener());
	}

	public void postToWall(String message){
		Bundle parameters = new Bundle();
                parameters.putString("message", message);
                parameters.putString("description", "topic share");
                try {
        	        facebook.request("me");
			String response = facebook.request("me/feed", parameters, "POST");
			Log.d("Tests", "got response: " + response);
			if (response == null || response.equals("") ||
			        response.equals("false")) {
				showToast("Blank response.");
			}
			else {
				showToast("Message posted to your facebook wall!");
			}
			finish();
		} catch (Exception e) {
			showToast("Failed to post to wall!");
			e.printStackTrace();
			finish();
		}
	}

	class LoginDialogListener implements DialogListener {
	    public void onComplete(Bundle values) {
	    	saveCredentials(facebook);
	    	if (messageToPost != null){
			postToWall(messageToPost);
		}
	    }
	    public void onFacebookError(FacebookError error) {
	    	showToast("Authentication with Facebook failed!");
	        finish();
	    }
	    public void onError(DialogError error) {
	    	showToast("Authentication with Facebook failed!");
	        finish();
	    }
	    public void onCancel() {
	    	showToast("Authentication with Facebook cancelled!");
	        finish();
	    }
	}

	private void showToast(String message){
		Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
	}
}

In what follows, we will go through the code step by step.

1. onCreate

In the onCreate method we are restoring the previously stored credentials – if any – from the SharedPreferences of the app. As we will see later, these will be set after succesful authentication.
In the onCreate method we are also setting the view to the following layout xml file:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="250dp" android:layout_height="125dp">
	<LinearLayout
		android:orientation="vertical"
		android:layout_width="fill_parent" android:layout_height="wrap_content"
		android:layout_gravity="center"
		android:padding="5dp"
		android:layout_alignParentTop="true">
		<TextView android:text="Do you want to share this on Facebook?"
			android:padding="5dp"
			android:layout_width="wrap_content" android:layout_height="wrap_content"
			android:layout_gravity="center_horizontal"/>
	</LinearLayout>

	<LinearLayout
		android:orientation="horizontal"
		android:layout_width="fill_parent" android:layout_height="wrap_content"
		android:layout_gravity="center"
		android:layout_alignParentBottom="true">

		<Button
			android:layout_alignParentBottom="true"
			android:layout_height="wrap_content"
			android:id="@+id/FacebookShareButton" android:text="Yes"
			android:layout_width="120dp" android:layout_gravity="center_horizontal"
			android:layout_centerHorizontal="true" android:onClick="share"
			android:layout_margin="2dp"
			android:padding="5dp"/>
		<Button
			android:layout_alignParentBottom="true"
			android:layout_height="wrap_content"
			android:id="@+id/FacebookShareNotButton" android:text="No"
			android:layout_width="120dp" android:layout_gravity="center_horizontal"
			android:layout_centerHorizontal="true" android:onClick="doNotShare"
			android:layout_margin="2dp"
			android:padding="5dp"/>
	</LinearLayout>
</RelativeLayout>

In the AndroidManifest.xml file, we are also specifying the activity as:

<activity android:name=".ShareOnFacebook" android:label="@string/app_name"
	android:screenOrientation="portrait" android:theme="@android:style/Theme.Dialog">

so our activity will be displayed as a dialog box, showing as:

Obviously, when the “No” button is clicked, nothing fancy happens – the activity just finishes. More interesting is the “Yes” button though, which will execute the share method on our activity.

2. share

This share method is implemented as:

public void share(View button){
		if (! facebook.isSessionValid()) {
			loginAndPostToWall();
		}
		else {
			postToWall(messageToPost);
		}
}

So, if our session is not valid(if we dont have a token or the token is expired, the isSessionValid method will fail, so the user will initiate the login process again), the user will be asked to login to facebook, give the app the necessary permissions and authorize the wallpost. If the session is valid, the user will only be asked to authorize the wallpost. We will assume the user is not logged in yet. The second scenario is just a subset of the first one.

3. loginAndPostToWall

This method consists of just one line:

facebook.authorize(this, PERMISSIONS, Facebook.FORCE_DIALOG_AUTH, new LoginDialogListener());

This method call will present the following dialog box on the left to the user:

after, if the user logs in succesfully, he will be asked to give permission to this app to post to his wall, as shown in the right part of the pic.

Notice how the facebook android api allows us to add custom listeners for certain events, in this case all events related to authentication.

EDIT: If the Facebook app is installed on the phone, the Facebook.FORCE_DIALOG_AUTH parameter makes sure authentication is forced. Without it, all the above will result in an endless spinning wheel, because the app keeps waiting on a login dialog but never gets it.

The implementation of our custom listener looks like this:

class LoginDialogListener implements DialogListener {
	    public void onComplete(Bundle values) {
	    	saveCredentials(facebook);
	    	if (messageToPost != null){
			postToWall(messageToPost);
		}
	    }
	    public void onFacebookError(FacebookError error) {
	    	showToast("Authentication with Facebook failed!");
	        finish();
	    }
	    public void onError(DialogError error) {
	    	showToast("Authentication with Facebook failed!");
	        finish();
	    }
	    public void onCancel() {
	    	showToast("Authentication with Facebook cancelled!");
	        finish();
	    }
	}

So, in case of failure, we will show the user a notification of the failure and finish the activity. In case of succesful authentication we are storing the credentials in the shared preferences of the application, and we are posting the message to the wall through the postToWall method.

4. postToWall

Before, the following dialog box used to be presented to the user:

But when using the new Graph API(“me/feed”), the message is directly sent through and posted to the user’s wall, without asking for confirmation.

Our method is a bit longer than the previous one:

        Bundle parameters = new Bundle();
        parameters.putString("message", message);
        parameters.putString("description", "integrating stuff");
        try {
        	facebook.request("me");
		String response = facebook.request("me/feed", parameters, "POST");
		Log.d("Tests", "got response: " + response);
		if (response == null || response.equals("") ||
			response.equals("false")) {
			showToast("Blank response.");
		}
		else {
			showToast("Message posted to your facebook wall!");
		}
		finish();
	} catch (Exception e) {
		showToast("Failed to post to wall!");
		e.printStackTrace();
		finish();
	}


When the user authorizes the post, the postId will be not null, and the success message will be posted. Otherwise the cancel message will be posted. In case of an error, the user is notified of this, and the activity is finished too.