Generate a Live feed from Facebook for the Open Event Android App

To increase the user engagement in the Open Event Android app, the feed was a welcome feature for the users. In order to fetch data from a public facebook page, firstly the facebook page id should be fetched from an API GET request. To fetch the page id, the page name have to be included in the API request along with the access token generated from the facebook developers page itself.

To fetch the data from Facebook, it requires the user to login to Facebook. Facebook requires its SDK to be set up first on the android itself to make the requests. Hence it destroys the motive to have feed of a public Facebook page.

Solution

The solution involves making custom API requests to the Facebook servers in order to fetch the data. 3 API requests have to be made. The first fetches the access token for the developer to access the facebook servers for the data. It’s going to be a one time call, so just make the API request in the browser itself and fetch the token with the help of the app ID and the app secret. The second fetches the page id of a particular facebook page from the given facebook page name. The third fetches the data using the page id with the fields specified in the query. The API requests will be made with the help of the retrofit library and the object communication throughout the app will be catered by rxJava library.

Implementation

Create a Facebook developer account. Generate the app ID for your app along with its secret.

Make a GET request to fetch the access token from the app ID and app secret.

https://graph.facebook.com/oauth/access_token?client_id={APP_ID}&client_secret={APP_SECRET}&grant_type=client_credentials

#NOTE: Remove the brackets!

Make the appropriate JSON getters and setters of the feed from the GET request. Use http://www.jsonschema2pojo.org/ to autogenerate them with the help of a JSON input.

Create the Facebook Graph API interface to make the GET requests. Make them observable for object communication using rxJava.

public interface FacebookGraphAPI {

  @GET(“/{event_name}”)
  Observable<FacebookPageId> getPageId(@Path(“event_name”) String eventName, @Query(“access_token”) String accessToken);

  @GET(“/{page_id}/feed”)
  Observable<Feed> getPosts(@Path(“page_id”) String pageId, @Query(“fields”) String fields, @Query(“access_token”) String accessToken);

}

Create an API client to build the retrofit along with the OkHttpClient

public final class APIClient {

  private static final int CONNECT_TIMEOUT_MILLIS = 20 * 1000; // 15s

  private static final int READ_TIMEOUT_MILLIS = 50 * 1000; // 20s

  private static FacebookGraphAPI facebookGraphAPI;

  private static Retrofit.Builder retrofitBuilder;

  static {
      OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient().newBuilder()
              .connectTimeout(CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
              .readTimeout(READ_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
      if (BuildConfig.DEBUG)
          okHttpClientBuilder.addNetworkInterceptor(new StethoInterceptor());
      OkHttpClient okHttpClient = okHttpClientBuilder.addInterceptor(new HttpLoggingInterceptor()
              .setLevel(HttpLoggingInterceptor.Level.BASIC))
              .build();

      retrofitBuilder = new Retrofit.Builder()
              .addConverterFactory(JacksonConverterFactory.create(new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)))
              .client(okHttpClient);
  }

  public static FacebookGraphAPI getFacebookGraphAPI() {
      if (facebookGraphAPI == null)
          facebookGraphAPI = retrofitBuilder
                  .baseUrl(Urls.FACEBOOK_BASE_URL)
                  .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                  .build()
                  .create(FacebookGraphAPI.class);
      return facebookGraphAPI;
  }

}

Create a utility method to get the relative time from the timestamp of the post.

private static final Locale defaultLocale = Locale.getDefault();
private static String iso8601WithTimezone = “yyyy-MM-dd’T’HH:mm:ssZ”;
private static final String ISO_TIMEZONE_FORMATTER = new SimpleDateFormat(iso8601WithTimezone, defaultLocale);

@NonNull
private static Date getDate(@NonNull SimpleDateFormat formatter, @NonNull String isoDateString) throws ParseException {
  return formatter.parse(isoDateString);
}

public static String getRelativeTimeFromTimestamp(String timeStamp) throws ParseException {
  Date timeCreatedDate = getDate(ISO_TIMEZONE_FORMATTER, timeStamp);

  return (String) android.text.format.DateUtils.getRelativeTimeSpanString(
          (timeCreatedDate.getTime()),
          System.currentTimeMillis(), android.text.format.DateUtils.SECOND_IN_MILLIS);
}

Create a feed fragment, download and subscribe to the feed with the help of the retrofit and rxjava library. Notify the adapter when the data set changes.

private void downloadFeed() {
  APIClient.getFacebookGraphAPI()
          .getPosts(sharedPreferences.getString(ConstantStrings.FACEBOOK_PAGE_ID, null),
                  getContext().getResources().getString(R.string.fields),
                  getContext().getResources().getString(R.string.facebook_access_token))
          .subscribeOn(Schedulers.io())
          .observeOn(AndroidSchedulers.mainThread())
          .subscribe(feed -> {
              feedItems.clear();
              feedItems.addAll(feed.getData());
              feedAdapter.notifyDataSetChanged();
              handleVisibility();
          }, throwable -> {
              Snackbar.make(swipeRefreshLayout, getActivity()
                      .getString(R.string.refresh_failed), Snackbar.LENGTH_LONG)
                      .setAction(R.string.retry_download, view -> refresh()).show();
              Timber.d(“Refresh not done”);
          }, () -> {
              swipeRefreshLayout.setRefreshing(false);
              Timber.d(“Refresh done”);
          });
}

Conclusion

The open event android project currently only fetches feed from the facebook. Feeds from twitter and youtube will be fetched too in the near future. Watch out the next blog on how to display the comments of Facebook post on a dialogfragment over the feed fragment.

For more information on the code, please take help from here.