By Tom Reznik, on Dec 15, 2014

Content Management SDK for Java & Android

With the current hype around wearables, you're probably tired of hearing about them already. When we discovered this new way of managing content for Android devices though, we got quite excited!

The most traditional use of wearables is definitely consumption-based (i.e. browsing cards, checking notifications, etc.), but we want to ensure that you’re not restricted in adapting to more futuristic ideas.

With that in mind, we are happy to announce the Java SDK for our Content Management API (CMA).

The CMA SDK allows you to easily create and update your content from anywhere using Java. This opens a huge door for developers, making it possible to shape their content from any context, whether it be a mobile phone, tablet or even a wearable device.

In this blog post, we will take you through the SDK and our demo Fortune Cookie app, which turns your speech into entries in Contentful!

Screenshot

The source code for the SDK is available on GitHub.

Setup

The binaries for the library are published via Maven Central, and can be added to your project in several ways, depending on how you usually build your project.

Maven

xml
1
2
3
4
5
<dependency>
  <groupId>com.contentful.java</groupId>
  <artifactId>cma-sdk</artifactId>
  <version>VERSION-NUMBER</version>
</dependency>

Gradle

groovy
1
compile 'com.contentful.java:cma-sdk:VERSION-NUMBER'

The latest version number and more details are available in the Setup section of the project's README file.

Creating a client

The CMAClient class manages all your interaction with the Management API. Every client is associated with an access token, which can be obtained through the management API documentation.

Creating a client is as simple as:

java
1
2
3
CMAClient client = new CMAClient.Builder()
    .setAccessToken("access-token-goes-here")
    .build();

There are also several optional settings that can be specified through the client builder:

  • setLogLevel() - Configures a custom log level for the client. This can be useful for debugging purposes, as it outputs the network traffic according to the provided log level.

  • setClient() or setClientProvider() - Configures a custom HTTP client to carry the network requests.

Examples

Creating an entry:

java
1
2
3
4
5
6
7
client.entries().async().create("space-id", "content-type-id",
  new CMAEntry().setField("title", "Mr. President", "en-US"),
  new CMACallback<CMAEntry>() {
    @Override protected void onSuccess(CMAEntry result) {
      // ...
    }
});

Updating an entry:

java
1
2
3
4
5
client.entries().async().update(entry, new CMACallback<CMAEntry>() {
  @Override protected void onSuccess(CMAEntry result) {
    // ...
  }
});

Demo

Next we will demonstrate basic usage of the SDK from the context of an Android Wear project. For that, we have published the Cookies Demo Application for Android.

Screenshot

Implementation

As you probably know, Android Wear applications normally require two projects - one that would run on the handheld device and another for the wearable side. Hence, the demo project is structured as follows:

1
2
3
4
ROOT FOLDER
.. mobile/  -- Application module for handheld device
.. wear/    -- Application module for wearable device
.. shared/  -- Library project shared between both modules

The demo app makes use of the CMA SDK in order to create and publish entries. The CDA SDK is used for fetching random entries from the same Space.

The wearable project's MainActivity class has a layout consisting of two buttons. One for requesting a fortune cookie via the Delivery API and the other for storing new fortune cookies via the Management API. The text for the new fortune cookie will be retrieved from the user by voice, using an ACTION_RECOGNIZE_SPEECH Intent.

Since network operations should be performed by the handheld device, once one of the buttons is clicked, we deliver a message to the handheld device via the Wearable MessageApi. The message will be intercepted by a custom WearableListenerService, which in turn fetches or creates a fortune cookie according to the desired action. The result will be sent back to the wearable device by using the same mechanism.

In order to send a message from one side to the other (i.e. from the wearable to the handheld), we create a GoogleApiClient. Once the client is connected and ready, we should be able to send messages. A message consists of a path and an optional payload. We have declared the following method (from Utils.java) to send a message to other connected nodes:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Sends a message to all connected nodes via Wearable.MessageApi
private void sendMessage(final String path, final byte[] payload) {
  new Thread(new Runnable() {
    @Override public void run() {
      NodeApi.GetConnectedNodesResult result =
          Wearable.NodeApi.getConnectedNodes(googleApiClient).await();

      for (Node node : result.getNodes()) {
        Wearable.MessageApi.sendMessage(
        	googleApiClient,
        	node.getId(),
        	path,
        	payload).await();
      }
    }
  }).start();
}

Note that since we are using the synchronous versions of the getConnectedNodes() and sendMessage() methods, we spawn a new thread in order keep the main thread free.

On the handheld side, we have a custom WearableListenerService which intercepts these messages and delegates the work to an IntentService named CookieService. The following method will be invoked on a background thread when receiving a message for creating a new fortune cookie:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
private void actionCreateCookie(Intent intent) {
  RetrofitError error = null;

  try {
    // Create a new Entry
    CMAEntry entry = Clients.cma().entries().create(
      getString(R.string.cf_space_id),
      getString(R.string.cf_content_type_id),
      new CMAEntry().setField(
        "text", intent.getStringExtra(EXTRA_COOKIE), "en-US"));

    // Publish the Entry
    Clients.cma().entries().publish(entry);
  } catch (RetrofitError e) {
    error = e;
  }

  // Notify
  GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this)
    .addApi(Wearable.API)
    .build();

  googleApiClient.blockingConnect();

  if (error == null) {
    Utils.sendMessage(googleApiClient, Constants.PATH_PUBLISHED_COOKIE);
  } else {
    Utils.sendMessage(googleApiClient, Constants.PATH_ERROR, error.getMessage().getBytes());
  }
}

Here we simply create and publish a new Entry with our pre-defined Content Type and set it's text field value. If the operation was successful, we notify the wearable by firing yet another message. In case the operation has failed for any reason (i.e. network error), we fire a message with the error description.

On the wearable side, we have a custom WearableListenerService which intercepts these message and reacts accordingly:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class WearableService extends WearableListenerService {
  @Override public void onMessageReceived(MessageEvent messageEvent) {
    super.onMessageReceived(messageEvent);
    String path = messageEvent.getPath();

    if (Constants.PATH_ERROR.equals(path)) {
      // Display error message.
      Toast.makeText(this,
          getString(R.string.toast_error, new String(messageEvent.getData())),
          Toast.LENGTH_SHORT).show();
    } else if (Constants.PATH_DISPLAY_COOKIE.equals(path)) {
      // Cookie received, display in new Activity.
      startActivity(new Intent(this, CookieActivity.class)
          .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
          .putExtra(CookieActivity.EXTRA_COOKIE, new String(messageEvent.getData())));
    } else if (Constants.PATH_PUBLISHED_COOKIE.equals(path)) {
      // Cookie created successfully, show confirmation Toast.
      Toast.makeText(this, R.string.toast_cookie_published, Toast.LENGTH_SHORT).show();
    }
  }
}

Conclusion

If you haven't had a chance to develop for Android wear yet, hopefully this would show you how to start. The full source code for the demo app is available on GitHub. If you have any feedback about the SDK, feel free to communicate it through the GitHub repository.

Happy coding q:)

Credits

Tom Reznik

Former android developer at Contentful. You can follow Tom on Twitter.