12 September, 2014

Sharing a local photo to Facebook on Android using Facebook SDK

Most of the steps depicted here can be found on Facebook Developers page but sharing a photo while showing the share dialog isn't fully documented.

Download SDK

Download the Facebook SDK and extract it.

Setup Workspace

Add the Facebook SDK project to your workspace and link the library to your project.

Register App on Facebook

Follow these steps to create a Facebook app and get the Application Id.

Android Manifest

Add these elements to your app Manifest file inside the application element and replace the 123456789456789 of the provider with the application id you got from the facebook page.
<activity android:name="com.facebook.LoginActivity" android:label="@string/app_name" android:theme="@android:style/Theme.Translucent.NoTitleBar" />

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

<provider android:authorities="com.facebook.app.NativeAppCallContentProvider123456789456789" android:name="com.facebook.NativeAppCallContentProvider" android:exported="true" />
The provider was what I was missing from my solution resulting on the photo being published without showing any dialog to the user.
Don't forget to add the facebook_app_id string in your string.xml file.

Initialization

I ended up using Android Simple Facebook library to login the user if necessary since it's really easy to use. 
private Permission[] permissions = new Permission[] { Permission.PUBLISH_ACTION };

public initialization(String appId, String appNamespace, Bundle savedInstanceState) {
 // Facebook SDK initialization
 uiHelper = new UiLifecycleHelper(this, null /*statusCallback*/);
 uiHelper.onCreate(savedInstanceState);

 // Simple Facebook Library initialization
 SimpleFacebookConfiguration configuration = new SimpleFacebookConfiguration.Builder().setAppId(appId).setNamespace(appNamespace).setPermissions(permissions).build();
 SimpleFacebook.setConfiguration(configuration);
}
Use the status callback to get Facebook session status
It is also needed to attach some lifecycle events to these libraries.
@Override
protected void onSaveInstanceState(Bundle outState) {
 super.onSaveInstanceState(outState);
 uiHelper.onSaveInstanceState(outState);
}

@Override
protected void onResume() {
 super.onResume();
 uiHelper.onResume();
 mSimpleFacebook = SimpleFacebook.getInstance(activity);
}

@Override
protected void onPause() {
 super.onPause();
 uiHelper.onPause();
}

@Override
protected void onDestroy() {
 uiHelper.onDestroy();
 super.onDestroy();
}

Login user

Before actually posting the user is requested to be logged in and authorize the app. Since we're using the Facebook SDK it requires the Facebook App to be installed on the device. Since the user will already be logged in the Facebook App this post will not request the user to login again. The first post will, however, request permission for your app to post in the user timeline.
As stated above, I've used Simple Facebook library for login.
public void checkFacebookLogin(final File photo) {
 if (facebook.isLoggedIn()) {
  postPhoto(photo);
 } else {
  facebook.logIn(new OnLoginListener() {

   @Override
   public void onFail(String result) {}

   @Override
   public void onException(Throwable exception) {}

   @Override
   public void onThinking() {}

   @Override
   public void onNotAcceptingPermissions(Type type) {}

   @Override
   public void onLogin() {
    postPhoto(photo);
   }
  });
 }
}
On production code you should actually handle the other events.

Post Photo

Actually post the photo.
public void postPhoto(File photo) {
 if (FacebookDialog.canPresentShareDialog(this, FacebookDialog.ShareDialogFeature.PHOTOS)) {
  FacebookDialog shareDialog = new FacebookDialog.PhotoShareDialogBuilder(this).addPhotoFiles(Arrays.asList(photos)).build();
  uiHelper.trackPendingDialogCall(shareDialog.present());
 }
}
Since the WebDialog provided by the Facebook SDK doesn't post photos it looks like we're stuck with this method.