Implementation

For information about the design and functionality of the MIDlet, see Design.

Figure: The UML diagram of the StatusShout example MIDlet

The most important classes are described in the following table:

Class

Description

FacebookService

Implements the Facebook specific authorisation and provides a simple method for sharing a message to user's Facebook feed with HTTP Post.

OAuthService

An abstract base class of OAuth service specific implementations. Contains the common implementation for OAuth.

ShareApiManager

Implements sharing of images and messages with Share API. Also implements the logic for managing invocations (sharing destination).

StatusShoutData

A singleton class managing the application specific data and its persistence.

MainView

The main user interface (UI) class of the application. Owns all the major class instances and manages the app logic.

Share API

Share API provides a single entry point for sharing images and messages using messaging, email and social media services. The developer does not have to implement user interface (UI) for sharing. Instead the UI is provided by the platform. The Share API requires two parameters: the (source of) content and the MIME type matching the content. For instance, if one is sharing an image the parameters could be as follows:

  • Content, which in this case is the image location in the file system: url=file:///MemoryCard/_my_pictures/20130921-0001.jpeg

  • MIME type of the image: image/jpeg

Respectively, the parameters for sharing a message:

  • Content: text=My test message

  • MIME type: text/plain

The API is used via Invocation (javax.microedition.content.Invocation), which finds the content handler for sharing. The following snippets launches the sharing UI with a predefined message:

import javax.microedition.content.Invocation;
import javax.microedition.content.Registry;
import javax.microedition.midlet.MIDlet;

...

    private MIDlet myMidlet; // This is the MIDlet instance of the application

...

        String[] args = new String[1]; // Only the first element is required and used
        args[0] = new String("text=My test message"); // Content to share

        try {
            Invocation invocation = new Invocation(null, "text/plain", "com.nokia.share");
            invocation.setResponseRequired(false);
            invocation.setAction("share");
            invocation.setArgs(args);

            Registry registry = Registry.getRegistry(myMidlet.getClass().getName());

            if (registry.invoke(invocation)) {
                // If application must be closed before invocation
                // This is, however, not the case with Share API
            }
        }
        catch (Exception e) {
            // Handle exception
        }

As shown by the snippet above, the MIME type is inserted during the creation of the Invocation instance. The constructor and its arguments are:

javax.microedition.content.Invocation.Invocation(String url, String type, String ID)

The ID is very important; here the given ID, com.nokia.share, defines that we want to use the Share API. The (MIME) type and the ID can also be set with methods Invocation.setType() and Invocation.setID(). The content to be shared is set with Invocation.setArgs() method. Finally, the invocation is executed with javax.microedition.content.Registry instance associated with the MIDlet.

Note that Registry.invoke() never blocks the execution, it always returns immediately (even if it fails). The method call (if successful) will display the UI for sharing, while the MIDlet is put in background. To make sure your app stays alive during this phase, try to minimise the memory usage of your app when sharing. Although it is very likely that your app will not be terminated, it cannot be guaranteed. Thus, it is recommended to prepare for possible termination by making the suitable parts of the app data persistable. For instance, the message user possibly composed for sharing should be stored in the record store. Put the method call for saving the data in MIDlet.destroyApp(), since that is always called if the app is terminated

Sharing destination

A MIDlet can declare that it is capable of sharing content, i.e. is a sharing destination. It does not - cannot - declare where the content will be shared to, but it needs to declare what kind of content it can share, e.g. images, text, etc. The static declaration is done in the application descriptor (.jad file). StatusShout declares that it can share images of any type:

MicroEdition-Handler-1: com.nokia.example.statusshout.StatusShout, image/*, , share open-shared,

Notice the MIME type information: image/*. If an app can share only JPEG images, the MIME type would be image/jpeg. You can use asterisk for a wildcard.

Adding this line to the descriptor has the effect that after the app has been installed, it will show up in the share UI when suitable content type is being shared. In the case of StatusShout, you can see it appear in the menu when sharing an image (for instance, in the gallery application provided by the platform).

The application that is a sharing destination needs to check for the invocation - which will contain the details of the content the app is supposed to share - during the start-up (in method javax.microedition.midlet.MIDlet#startApp()).

import javax.microedition.content.ContentHandlerException;
import javax.microedition.content.ContentHandlerServer;
import javax.microedition.content.Invocation;
import javax.microedition.content.Registry;
import javax.microedition.midlet.MIDlet;

...

        // 1. Get the content handler server instance
        ContentHandlerServer handler = null;
        
        try {
            handler = Registry.getServer(midlet.getClass().getName());
        }
        catch (ContentHandlerException e) {
            // Handle exception
        }

        // 2. Get the invocation instance
        Invocation invocation = contentHandlerServer.getRequest(false);

The details about the content can be retrieved with Invocation.getArgs() method:

      // 3. Parse the arguments from invocation instance
      Hashtable argsTable = parseArgs(invocation.getArgs());

      ...

  /**
   * A helper method for parsing the invocation arguments.
   */
  private Hashtable parseArgs(String[] args) {
      Hashtable argsTable = new Hashtable();
      
      for (int i = 0; i < args.length; ++i) {
          String arg = args[i];
          int j = arg.indexOf("=");
          
          if (j > 0) {
              String key = arg.substring(0, j);
              String value = arg.substring(j + 1);
              Vector valueVector;
              
              if (argsTable.get(key) == null) {
                  valueVector = new Vector();
              }
              else {
                  valueVector = (Vector) argsTable.get(key);
              }
              
              valueVector.addElement(value);
              argsTable.put(key, valueVector);
          }
      }
      
      return argsTable;
  }

In the case of StatusShout, it will get the image URIs:

      // 4. Take action based on the parsed data
      Vector urls = (Vector) argsTable.get("url");
      
      if (urls != null) {
          // Share API is capable of sharing multiple images at once but this
          // application only shares the first one in the array
          if (urls.elementAt(0) != null) {
              // imageUriFromInvocation is a member of class ShareApiManager
              imageUriFromInvocation = (String)urls.elementAt(0);
          }
      }

A MIDlet can also get notified if the sharing invocation is received when the application is running. All it needs to do is to implement RequestListener interface and register as a listener with ContentHandlerServer.setListener(RequestListener):

import javax.microedition.content.RequestListener;

public class MyShareApiImplementation implements RequestListener {
    
    ...

        // Start listening for new invocations
        handler.setListener(this); // handler is of type ContentHandlerServer
        ...

    /**
     * @see javax.microedition.content.RequestListener#invocationRequestNotify(javax.microedition.content.ContentHandlerServer)
     */
    public void invocationRequestNotify(ContentHandlerServer handler) {
        // Handle the new invocation here
    }

You can also implement a Fastlane UI integration (the event is displayed in the Fastlane UI, for example, "Me > Status Shout!" with a thumbnail of the photo you shared). This is done with ContentHandlerServer.finish(Invocation, int) method. The ShareApiManager class of this example application implements a helper method, ShareApiManager.finishInvocation(int), to achieve the integration:

public class ShareApiManager implements RequestListener {
    ...
    private ContentHandlerServer contentHandlerServer;
    private Invocation invocation;

    ...

    /**
     * Finishes the existing invocation with the given result.
     * 
     * @param result The result to use to finish the existing invocation, e.g. Invocation.OK or Invocation.CANCEL.
     */
    public void finishInvocation(int result) {
        if (contentHandlerServer != null && invocation != null) {
            contentHandlerServer.finish(invocation, result);
            invocation = null;
        }
        ...

After a photo is successfully shared to Facebook, the MainView instance of the example app calls finishInvocation with result Invocation.OK, which, as a result, will display the event in the Fastlane UI.

OAuth

OAuth is an open standard for authorisation. The typical authentication process is as follows:

  1. The user is prompted to authenticate.

  2. The user is directed to the authentication web page of the service. This can be implemented with a web component or in the case of Nokia Asha software platform, the Nokia browser is used.

  3. The user enters his/her credentials.

  4. After a successful authentication, the application receives the result, for example, an access token.

The steps 2 and 3 take place outside the application while the implementation of the steps 1 and 4 is the responsibility of the application developer. The J2ME invocation API provides the means of using the Nokia browser on Nokia Asha platform. The following snippet demonstrates how to authenticate the user to Facebook:

import javax.microedition.content.Invocation;
import javax.microedition.content.Registry;
import javax.microedition.content.ResponseListener;
import javax.microedition.midlet.MIDlet;

public class MyOAuthService implements ResponseListener {
    // Constants for Facebook authorisation

    /*
     * Note that this Facebook implementation will not work without a valid application ID!
     */
    private static final String APP_ID = "123456789"; // Facebook application ID

    /*
     * Note that "m" prefix needs to be used instead of "www"!
     * Otherwise, the app will not be able to intercept the access token!
     */
    private static final String REDIRECT_URL = "https://m.facebook.com/connect/login_success.html";

    private static final String OAUTH_URL = "https://facebook.com/dialog/oauth";

    // The full URL address used for logging in and retrieving the access token.
    private static final String LOGIN_URL = OAUTH_URL
            + "?client_id=" + APP_ID
            + "&redirect_uri=" + REDIRECT_URL
            + "&display=popup"
            + "&scope=publish_stream" // This is required for getting publishing permission
            + "&response_type=token";

    private MIDlet myMidlet; // This is the MIDlet instance of the application

...

    public void authorise() {
        Registry registry = Registry.getRegistry(myMidlet.getClass().getName());

        // Set this class as listener to get invocationResponseNotify() callback 
        registry.setListener(this);
        
        Invocation invocation = new Invocation();
        invocation.setURL(LOGIN_URL);
        invocation.setID("com.nokia.browser");
        invocation.setArgs(new String[] { "mode=proxy", "redirect_intercept=" + REDIRECT_URL });
        invocation.setResponseRequired(true);
        
        try {
            registry.invoke(invocation);
        }
        catch (Exception e) {
            // Handle exception
        }
    }

    /**
     * This implements javax.microedition.content.ResponseListener#invocationResponseNotify(
     * javax.microedition.content.Registry)
     */
    public void invocationResponseNotify(Registry registry) {
        Invocation response = registry.getResponse(true);
        
        if (response.getStatus() == Invocation.OK) {
            // Parse token from response.getURL()
        }
        else {
            // Something went wrong
        }
    }

...
}

The important part to notice is the definition of the browser, com.nokia.browser, which is set as the ID for the Invocation instance. To get the final response with the access token, you need to set your class as the ResponseListener. After the user has been successfully authenticated, you can parse the access token in invocationResponseNotify() to be used later for interacting with the service, in this case Facebook.

Note:

You need to obtain a valid Facebook application ID to share content in Facebook. The value of the APP_ID in the snippet above is just an example and will not work. For more information, see https://developers.facebook.com/. In addition, it is always recommended that you use "m" prefix in URLs instead of "www". For instance, with Facebook, if you have "www" prefix in the redirect URL, the app is unable to intercept the access token.

Sharing a message to Facebook

Sharing messages to Facebook is done with HTTP Post. The API is fairly straightforward. The following snippet demonstrates how to publish an update on user's timeline. Note that exception and error handling is omitted.

public void shareMessage(final String message, final String accessToken) {
      StringBuffer sb = new StringBuffer();
      sb.append("access_token=").append(accessToken).append("&message=").append(message);
      final String content = sb.toString();
      final String contentLength = String.valueOf(content.getBytes().length);

      new Thread() {
          public void run() {
              HttpConnection connection =
                  (HttpConnection) Connector.open("https://graph.facebook.com/me/feed", Connector.READ_WRITE);
              connection.setRequestMethod(HttpConnection.POST);
              connection.setRequestProperty("Content-length", contentLength);

              OutputStream outputStream = connection.openDataOutputStream();
              outputStream.write(content.getBytes());
              outputStream.flush();
              // Get the response code with connection.getResponseCode()
              outputStream.close();
          }
      }.start();
  }

For more information on developing with Facebook, visit Facebook Developers web site.