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 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
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 is an open standard for authorisation. The typical authentication process is as follows:
The user is prompted to authenticate.
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.
The user enters his/her credentials.
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.
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 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.