The Facebook SDK for Android provides a method to let you publish stories from your app to a user's timeline. You can also use this method to post a status update on your user's behalf. This method uses the Graph API, and is an alternative to using the Feed Dialog; if you are trying to publish an Open Graph story, you can read the how to here.
To publish a story to a user's timeline, you create a Request object that includes the path to a user's feed and information about the story the app is about to post. Publishing a story will require write permissions to a user's account, so you'll pass the user through a reauthorization flow to get those permissions.
This document walks you through the following:
Before you begin, make sure you've set up Facebook Login. Your app will need to authenticate a user before you can publish to their feed. This tutorial will build directly on the code from the Login How To.
The completed sample application from this tutorial lets users log in with Facebook and publish a link to their Timeline. It builds on top of the sample from Facebook Login, adding a button that posts a hard-coded story to the user's Timeline. If the post is successful, an alert pops up with the story's ID.
|
|
|
In the Login How To, you created a single-activity application, where the code for the UI was delegated to a Fragment class called MainFragment. From that application, add a "share" button to the UI that will be visible only if the user has logged in.
Define a new string resource for the share button. Open the Android string resource file, res/values/strings.xml, and define a new string:
<string name="share">Share</string>
Then, open your main activity's layout XML file, res/layout/main.xml, and add a <Button> to the layout just below the login button:
<Button
android:id="@+id/shareButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textStyle="bold"
android:gravity="center"
android:layout_marginTop="30dp"
android:visibility="invisible"
android:text="@string/share"
/>
The button is set to be hidden initially, and will be visible once the user has logged in.
Now, open your fragment class, MainFragment.java, and declare a private variable for the button:
private Button shareButton;
Next, add the button to the onCreateView code so you can add functionality to it later:
shareButton = (Button) view.findViewById(R.id.shareButton);
Modify the onSessionStateChange() method in your MainFragment class file to show the publish button only when the user is authenticated:
private void onSessionStateChange(Session session, SessionState state, Exception exception) {
if (state.isOpened()) {
shareButton.setVisibility(View.VISIBLE);
} else if (state.isClosed()) {
shareButton.setVisibility(View.INVISIBLE);
}
}
In this step, you'll add logic to publish a story to Facebook.
In the MainFragment class, define a new method called publishStory(). The method will first check if the logged-in user has granted your app publish permissions; if they have not, they will be prompted to reauthorize the app and grant the missing permissions. The SDK provides methods and a standard UI to handle the reauthorization process.
Next, it creates a Request object that will be executed by a subclass of AsyncTask called RequestAsyncTask. There are several ways to construct a Request object, depending on what you want to read from or write to the Graph API. Here, you're making a POST to the Graph API, so you pass in the current user's session, the Graph endpoint we're posting to, a Bundle of POST parameters, the HTTP method (POST) and a callback to handle the response when the call completes.
In a production app, you would add an interface to handle user input, including the ability to add a message. For simplicity, you're going to post a hard-coded story to the user's feed. Note that you will NOT add in a message in this example, as pre-filled messages are against Facebook's Platform Policies.
private void publishStory() {
Session session = Session.getActiveSession();
if (session != null){
// Check for publish permissions
List<String> permissions = session.getPermissions();
if (!isSubsetOf(PERMISSIONS, permissions)) {
pendingPublishReauthorization = true;
Session.NewPermissionsRequest newPermissionsRequest = new Session
.NewPermissionsRequest(this, PERMISSIONS);
session.requestNewPublishPermissions(newPermissionsRequest);
return;
}
Bundle postParams = new Bundle();
postParams.putString("name", "Facebook SDK for Android");
postParams.putString("caption", "Build great social apps and get more installs.");
postParams.putString("description", "The Facebook SDK for Android makes it easier and faster to develop Facebook integrated Android apps.");
postParams.putString("link", "https://developers.facebook.com/android");
postParams.putString("picture", "https://raw.github.com/fbsamples/ios-3.x-howtos/master/Images/iossdk_logo.png");
Request.Callback callback= new Request.Callback() {
public void onCompleted(Response response) {
JSONObject graphResponse = response
.getGraphObject()
.getInnerJSONObject();
String postId = null;
try {
postId = graphResponse.getString("id");
} catch (JSONException e) {
Log.i(TAG,
"JSON error "+ e.getMessage());
}
FacebookRequestError error = response.getError();
if (error != null) {
Toast.makeText(getActivity()
.getApplicationContext(),
error.getErrorMessage(),
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getActivity()
.getApplicationContext(),
postId,
Toast.LENGTH_LONG).show();
}
}
};
Request request = new Request(session, "me/feed", postParams,
HttpMethod.POST, callback);
RequestAsyncTask task = new RequestAsyncTask(request);
task.execute();
}
}
Remember to import the necessary libraries (In Eclipse hit Cmd-Shift-O for Mac and Ctl-Shift-O for Windows). If given a choice during the import, select com.facebook.Session and com.facebook.Request.
You may have noticed a few things in the code above. First, the response you get from the API call contains a GraphObject, which is the interface used by the SDK to represent objects in the graph. You can fetch the story ID from the underlying JSONObject by calling getInnerJSONObject.
You also called a helper method isSubsetOf to determine whether or not the user has granted the necessary permissions to publish the story. Define it below publishStory:
private boolean isSubsetOf(Collection<String> subset, Collection<String> superset) {
for (String string : subset) {
if (!superset.contains(string)) {
return false;
}
}
return true;
}
Then, you referenced the permissions you need and the pending authorization info within MainFragment:
private static final List<String> PERMISSIONS = Arrays.asList("publish_actions");
private static final String PENDING_PUBLISH_KEY = "pendingPublishReauthorization";
private boolean pendingPublishReauthorization = false;
During the reauthorization flow, the UiLifecycleHelper object handles the incoming result and updates the active session with the updated permission. This was set up earlier by overriding the onActivityResult() method.
Once the session has been updated, the onSessionStateChange() method will be called. You can check if a reauthorization was in progress and the token was updated to continue the publish story flow:
....
shareButton.setVisibility(View.VISIBLE);
if (pendingPublishReauthorization &&
state.equals(SessionState.OPENED_TOKEN_UPDATED)) {
pendingPublishReauthorization = false;
publishStory();
}
....
Now, there may be scenarios where the activity is stopped during the reauthorization flow. In this instance you'll want to save the pendingPublishReauthorization flag. Do this by modifying the onSaveInstanceState() method in MainFragment:
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(PENDING_PUBLISH_KEY, pendingPublishReauthorization);
uiHelper.onSaveInstanceState(outState);
}
If the fragment is recreated and there is saved state, you'll want to check that and restore the pendingPublishReauthorization flag. Do this in the onCreateView() method in MainFragment:
....
if (savedInstanceState != null) {
pendingPublishReauthorization =
savedInstanceState.getBoolean(PENDING_PUBLISH_KEY, false);
}
return view;
....
To actually call publishStory() initially, we'll need to add an onClickListener to our Share button. In the onCreateView method, add a click listener to the share button that calls the publishStory method:
shareButton = (Button) view.findViewById(R.id.shareButton);
shareButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
publishStory();
}
});
Build and run the project to make sure it runs without errors. Tap the ''Log In'' button to log in with Facebook. Once authenticated, tap ''Share'' and verify that you are prompted to reauthorize the app. Afterward, you should see a success alert with the posted story ID. Check your Timeline to verify the story published correctly.
If you're having trouble posting to a user's feed, the Graph API Explorer can give you more detailed error information to help you debug the issue. Be sure that you have asked for the publish actions permission, and that all the fields in your postParams variable are valid.
Graph Object - Reference for GraphObject, the interface for objects in the social graph.
Request - Reference for Requests.
RequestAsyncTask - Reference for RequestAsyncTask, the class that executes Requests asynchronously.
Session - Reference for the Session object, that contains information about the session for the logged-in user.
Read the Login How To to set up for this tutorial.