Thursday Feb 12, 2015

Introduction to custom Cordova plugin development

Oracle Mobile Application Framework (MAF) v2.1.0 uses Cordova version 3.6.3 on Android and 3.7.0 on iOS to provide access to native device functionality.  The MAF 2.1.0 extension to JDeveloper 12.1.3 enables you to easily include any of the hundreds of published 3rd party Cordova plugins into your MAF app.

But what if you can’t find a suitable 3rd party plugin? You could elect to write your own, which, depending on the functionality required, may not be as difficult as you think.

In this post I’ll provide an introduction to developing a Cordova plugin by creating a very simple plugin for each of Android and iOS.

How does a Cordova plugin work?

In a nutshell, Cordova provides a bridge between JavaScript and native code, enabling you to write native code that gets exposed to your app via a common JavaScript interface.

Each method exposed by a plugin’s JavaScript interface is mapped to a method in the plugin’s native code via the Cordova bridge, which also enables you to pass parameters back and forth between the JavaScript and native methods.

What comprises a Cordova plugin?

A Cordova plugin typically consists of:

  • Native code for each supported platform
  • A common JavaScript interface
  • A manifest file called plugin.xml

The conventional structure for a Cordova plugin supporting both Android and iOS is:

  - Plugin top-level folder
     - plugin.xml
     - src/
        - android/
           - <Java source code>
        - ios/
           - <Objective-C source code>
     - www/
        - <JavaScript interface>

Ideally, community-published plugins also include release notes, author and license information, and a README file.  

A plugin may also include additional native resources and these are identified in the plugin.xml manifest file. This manifest file is read by the plugman command-line tool, which is used by the Cordova command-line interface and also by the MAF extension for JDeveloper.

In some rare cases, a plugin may be created that simply executes some native code on initialization and requires no JavaScript interface.

For more detailed information about the plugin.xml manifest file, refer to the Cordova Plugin Specification.

How do I create my own custom Cordova plugin?

To create your own custom Cordova plugin, you must write:

  • JavaScript that provides the interface for calling your plugin from within a Cordova-based app, such as a MAF app.
  • Native code that provides the functionality you need.  Since MAF supports both Android and iOS, you should write native code for both platforms.
  • A plugin.xml manifest file that defines your plugin and how plugman should incorporate it into a MAF app (or any Cordova-based app).

What tools do I need to create my own custom Cordova plugin?

You really only need a text editor to create your own custom Cordova plugin, which is all I’ve used to create the custom plugin described in this post.

For more complex plugins, you may wish to develop and test the plugin’s native code in each platform’s native IDE, which means downloading and installing the Android Studio and/or Apple’s Xcode.

You don’t need Cordova installed to develop a Cordova plugin.  Once you have developed your Cordova plugin, you can incorporate it directly into your MAF app for testing. However, if you wish to test your plugin within a Cordova app, you must download and install Cordova using the Cordova Command-Line Interface.

How do I write the plugin’s JavaScript interface?

We start with the JavaScript interface since it provides a common interface to both the Android and iOS native code.  This interface effectively defines what is required in the native code.

The JavaScript interface must call the cordova.exec method to invoke a native plugin method, as follows: 

cordova.exec(successCallback, failureCallback, service, action, [args]);

This call invokes the action method on the service class on the native side, passing the arguments in the optional args array.  If the native code completes successfully, the successCallback function is executed, along with any return parameters.  If the native code fails, the failureCallback function is executed, with an optional error parameter.  For more information on JavaScript callback functions, refer to this helpful blog post by Michael Vollmer.

Let’s create a very simple plugin that displays a native popup dialog.  Thus plugin shall present one method that takes 3 parameters – title, message and button label – and shall return the result in the ‘success’ callback. No ‘failure’ callback will be implemented. The service class shall be named “Alert” and we shall call the sole method “alert”. 

The following JavaScript defines the interface:

module.exports = {
  alert: function(title, message, buttonLabel, successCallback) {
    cordova.exec(successCallback,
                 null, // No failure callback
                 "Alert",
                 "alert",
                 [title, message, buttonLabel]);
  }
};

The call to module.exports exports the JavaScript function alert as part of the JavaScript module that will be defined in the plugin manifest file, plugin.xml

Your app will call this alert JavaScript function, which will invoke the alert method on the Alert class in the plugin’s native code.

Save this JavaScript into a file called alert.js, within a www subfolder of your plugin’s top-level folder.

How do I write the plugin’s native code?

Android

Based on the JavaScript interface, we must define a class called Alert in a Java source file called Alert.java.  Let’s take a look at the Alert.java source file:

package com.acme.plugin.alert;

import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.DialogInterface;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaInterface;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CordovaWebView;
import org.apache.cordova.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

public class Alert extends CordovaPlugin {
  protected void pluginInitialize() {
  }

  public boolean execute(String action, JSONArray args, CallbackContext callbackContext)
      throws JSONException {
    if (action.equals("alert")) {
      alert(args.getString(
0), args.getString(1), args.getString(2), callbackContext);
      return true;
    }
    return false;
  }

  private synchronized void alert(final String title,
                                  final String message,
                                  final String buttonLabel,
                                  final CallbackContext callbackContext) {
    new AlertDialog.Builder(cordova.getActivity())
    .setTitle(title)
    .setMessage(message)
    .setCancelable(
false)
    .setNeutralButton(buttonLabel,
new AlertDialog.OnClickListener() {
      public void onClick(DialogInterface dialogInterface, int which) {
        dialogInterface.dismiss();
        callbackContext.sendPluginResult(
new PluginResult(PluginResult.Status.OK, 0));
      }
    })
    .create()
    .show();
  }
}

The Alert class should be part of a package that will be referenced in the manifest file, plugin.xml.

The Alert class must extend the CordovaPlugin class, the definition of which you can find here.

The Alert class must override the execute method, since this will be called each time the JavaScript cordova.exec method is called, providing the name of the plugin method, the input parameters and a callback context.  The execute method should return true if a valid action was passed in, otherwise false.

Once the code has completed, it should return a result and optional return parameters to the calling JavaScript by invoking the sendPluginResult method on the callbackContext object.   Returning a result of PluginResult.Status.OK will cause the JavaScript ‘success’ callback to be executed.  Any other result (apart from PluginResult.Status.NO_RESULT) will cause the JavaScript ‘failure’ callback to be executed.

In our plugin, a ‘success’ result is returned when the user taps the button on the popup dialog and a value of 0 is returned.

The Alert class may override the pluginInitialize method if any initialization logic is required when the plugin is first constructed.

Save this code into a file called Alert.java, within a src/android subfolder of your plugin’s top-level folder.

For more detailed information on aspects such a threading and event handling, refer to the Cordova page Android Plugins.

iOS

For our example, we must define a class called Alert in an Objective-C source file called Alert.m and corresponding header file Alert.h

Let’s take a look at the Alert.h header file:

#import <Cordova/CDV.h>

@interface Alert : CDVPlugin <UIAlertViewDelegate> {}
- (void)alert:(CDVInvokedUrlCommand*)command;
@end

@interface
MyAlertView : UIAlertView {}
@property (nonatomic, copy) NSString* callbackId;
@end

The Alert class must be a sub-class of CDVPlugin, the definition of which you can find here.

The Alert class must provide an alert method, since this will be called each time the plugin’s JavaScript alert method executes the cordova.exec method.

Save this code into a file called Alert.h, within a src/ios subfolder of your plugin’s top-level folder.

Let’s take a look at the Alert.m source file:

#import "Alert.h"

@implementation Alert
- (void)pluginInitialize
{
}

- (void)alert:(CDVInvokedUrlCommand*)command
{
  NSString* callbackId = command.callbackId;
  NSString* title = [command argumentAtIndex:
0];
  NSString* message = [command argumentAtIndex:
1];
  NSString* button = [command argumentAtIndex:
2];

  MyAlertView *alert = [[MyAlertView alloc]
                        initWithTitle:title
                        message:message
                        delegate:
self
                        cancelButtonTitle:button
                        otherButtonTitles:
nil];
                        alert.callbackId = callbackId;
  [alert show];
}

- (void)alertView:(UIAlertView*)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
  MyAlertView* myAlertView = (MyAlertView*)alertView;
  CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
                             messageAsInt:
0];
  [
self.commandDelegate sendPluginResult:result callbackId:myAlertView.callbackId];
}
@end

@implementation
MyAlertView
@synthesize callbackId;
@end

The alert method receives the input parameters and a callback id.

Once the code has completed, it should return a result and optional return parameters to the calling JavaScript by invoking the sendPluginResult method on the commandDelegate object. Returning a result of CDVCommandStatus_OK will cause the JavaScript ‘success’ callback to be executed. Any other result will cause the JavaScript ‘failure’ callback to be executed.

In our plugin, a ‘success’ result is returned when the user taps the button on the popup dialog and a value of 0 is returned.

The Alert class may implement the pluginInitialize method if any initialization logic is required when the plugin is first constructed.

Save this code into a file called Alert.m, within a src/ios subfolder of your plugin’s top-level folder.

For more detailed information on aspects such a threading and event handling, refer to the Cordova page iOS Plugins.

How do I write the plugin’s manifest file (plugin.xml)?

The manifest file, called plugin.xml, is an XML document that defines the plugin and tells plugman how to incorporate the plugin into your MAF app (or any Cordova-based app) for each platform it supports.

The Cordova Plugin Specification is comprehensive, but we will focus on the manifest file used for our “Alert” plugin:

<?xml version="1.0" encoding="UTF-8"?>
<plugin 
xmlns="http://apache.org/cordova/ns/plugins/1.0"
        id="com.acme.plugin.alert"
        version="0.0.1">

  <name>
Alert</name>
  <description>
A Cordova plugin that displays an alert popup dialog</description>

  <engines>
    <engine
name="cordova" version=">=3.6.0" />
  </engines>

  <js-module
src="www/alert.js" name="Alert">
    <clobbers
target="Alert" />
  </js-module>

  <!-- android -->
  <platform name="android">
    <config-file
target="res/xml/config.xml"
parent="/*">
      <feature name="Alert"/>
        <param name="android-package" value="com.acme.plugin.alert.Alert" />
      </feature>
    </config-file>
    <source-file src="src/android/Alert.java" target-dir="src/com/acme/plugin/alert" />
  </platform>

  <!-- ios -->
  <platform name="ios">
    <config-file target="config.xml" parent="/*">
      <feature name="Alert"/>
        <param name="ios-package" value="Alert" />
      </feature>
    </config-file>
    <header-file src="src/ios/Alert.h" />
    <source-file src="src/ios/Alert.m" />
  </platform>

</plugin>

The plugin element must contain the plugin’s XML namespace (xmlns), id and version.

The name and description elements should always be defined.  If you intend to publish your plugin for public use, you should include additional elements such as those identifying the author, license, keywords and repository.

The child elements of the engines element define which version(s) of the Cordova framework the plugin supports.  Since this plugin has been developed based on Cordova 3.6.0 documentation and will be tested within a MAF 2.1.0 app, we should set the minimum required Cordova version to 3.6.0.

The JavaScript interface is defined by including a js-module tag for each JavaScript file.  Any file defined here is automatically copied into your MAF app and injected into any HTML using a <script> tag so that you don’t have to specifically add this tag yourself, or include the JavaScript as content in your AMX features.  The clobbers tag indicates that the module shall be inserted into the window object as Alert, so your MAF app code should call the JavaScript method Alert.alert to execute the plugin’s alert method.

If your plugin has a dependency on another Cordova plugin, you can define this using a dependency tag.  Since our plugin does not have any dependencies, there is no such tag defined.

The platform tag is used to define the platforms that are supported by the plugin.  Within each platform tag, you specify the native source files, changes required to configuration files, any additional native resources or frameworks and any platform-specific JavaScript files.

For Android, it is important to specify the full package name of the plugin as the value for the android-package parameter, for example “com.acme.plugin.alert.Alert”, and to specify the target-dir for any source files (since this indicates the location to which the source file should be copied and it must match the package name), for example “src/com/acme/plugin/alert”.

Save this XML into a file called plugin.xml, within your plugin’s top-level folder.

Conclusion

We have now developed a custom Cordova plugin that can be incorporated into a MAF 2.1.0 app, or any app based on Cordova 3.6.0 or above.  In a follow-up post [Edit: here], I’ll describe how to integrate this plugin into a MAF app.

For developers looking for more detailed information about how to create a Cordova plugin, refer to the Cordova Plugin Development Guide, which contains specific guides for each native platform.

Wednesday Feb 04, 2015

Migrating MAF apps with 3rd party Cordova plugins

Cordova is used in Oracle Mobile Application Framework (MAF) to provide access to device features.   MAF 2.1.0 uses Cordova version 3.6.3 on Android and 3.7.0 on iOS, dramatically increasing the number of available plugins that developers can leverage.

MAF 2.1.0 greatly simplifies the process of adding 3rd party Cordova plugins into a MAF app by internally using the plugman command-line interface to build the plugin and incorporate all the required artifacts into your MAF app.

So how do you migrate an existing MAF app that uses a 3rd party Cordova plugin to MAF 2.1.0?

The following steps and screenshots show the process of migrating the BarcodeDemo sample app from MAF 2.0.1 to MAF 2.1.0 using JDeveloper.

Step 1 – Download JDK 8 and update MAF extension

MAF 2.1.0 requires JDK 8. Download JDK 8 before updating the MAF extension to 2.1.0 via Check for Updates in JDeveloper. Following a restart, you will be prompted to enter the JDK 8 location on your local machine and to reconfigure the Android and iOS Platform Preferences in JDeveloper. Please refer to the Installation Guide for more detailed information.

Step 2 – Automatic app migration

Open your MAF app within JDeveloper and you will be prompted to allow JDeveloper to automatically migrate your app. The Installation Guide describes this process in more detail. 



Step 3 – Remove the old plugin code

In a later step, you will point JDeveloper to the location of the plugin’s source code on your local machine and JDeveloper will use plugman to build the plugin and incorporate it into your MAF app.

Before doing that, you should remove the plugin artifacts that you had manually added to your MAF app previously.  This includes:

  • Android native code found in Projects > ApplicationController > Application Sources > plugins.BarcodeScanner.Android
  • iOS native code found in Projects > ApplicationController > ApplicationSources > plugins.BarcodeScanner.iOS
  • JavaScript files found in Projects > ViewController > Web Content > plugins



Step 4 – Remove unnecessary iOS app icons and launch images

This is an optional step to reduce the size of your app. You can remove the following iOS app icons and launch images for unsupported devices and operating system versions, found in Application Resources > Resources > ios:

  • Default-1135h@2x.png
  • Default-Land.png
  • Default-Landscape-Ipad.png
  • Default-Landscape.png
  • Default-Landscape@2x.png
  • Default-Landscape@2x~Ipad.png
  • Default-Portrait-Ipad.png
  • Default-Portrait.png
  • Default-Portrait @2x.png
  • Default-Portrait @2x~Ipad.png
  • Default.png
  • Default@2x-Landscape.png
  • Icon-50.png
  • Icon-50@2x.png
  • Icon-57.png
  • Icon-57@2x.png
  • Icon-72@2x.png
  • Icon-80.png
  • Icon-144.png
  • icon-72.png
  • icon.png
  • icon@2x.png


Step 5 – Remove old plugin details from the Plugins UI

To complete the removal of the old plugin, you should remove the references to it from within the maf-application.xml Plugins UI, by selecting each reference and clicking the red ‘x’ icon.


Step 6 – Save all, Clean all

At this point, save all modifications and then do a ‘clean all’.

Step 7 – Download a new version of the plugin

MAF 2.1.0 requires you to have the plugin source code residing on your build machine, located on the same drive as your app source code, in a path that contains no spaces.

It is likely that there is a later version of the plugin that you have used previously which is compatible with the Cordova versions used by MAF.  You may even find that more recently someone has written a plugin that provides a better match to your requirements.

Anyone can build a Cordova plugin and they can choose to keep it to themselves or to publish it for use by others. The most common way to publish a Cordova plugin is to store it in a Git repository and to register it with the Cordova plugin registry.  Launch your browser and open http://plugins.cordova.io.


To find a barcode scanner plugin, type in “barcode” and search.


Click on com.phonegap.plugins.barcodescanner to view the details of this plugin.


You can see that this plugin requires Cordova version 3.0.0 or greater, works on Android and iOS, and was last updated 3 months ago to version 2.0.1. This page also provides a description and API documentation.

To access this plugin, click on the link to its Git repository.


To download this plugin, click on the link to Download Zip.

Once the plugin has been downloaded, extract the zip file contents into a location relative to your app’s source code which does not contain spaces. For example, you wish to extract the plugin into your app’s src folder as follows:


Step 8 – Add the plugin via the Plugins UI

To import the downloaded version of the plugin into your MAF app, return to the maf-application.xml Plugin UI in JDeveloper and click the green ‘+’ icon. Navigate to the location of the plugin’s top-level folder and click the Select button.


The details of the plugin, which are read from the plugin.xml file, will be displayed in the Plugins UI.


Step 9 – Update the JS that calls the plugin API

In most cases, you will need to update the JavaScript that calls the plugin API. Firstly, you no longer need to include the plugin’s JavaScript file because plugman will include this into your application at deploy-time. Secondly, the manner in which you invoke the plugin’s JS API may have changed.

For the BarcodeDemo sample app, this means editing the scanner.amx file to remove the inclusion of barcodescanner.js and to change window.plugins.barcodeScanner.scan to cordova.plugins.barcodeScanner.scan as described in the plugin’s README.md file.


Step 10 – Save all, deploy and test

All that’s left now is to save all modifications, deploy your app (most likely to a device, since many plugins won’t work on a simulator or emulator) and test it.

A tip for anyone using plugins with dependencies

If a 3rd party Cordova plugin has a dependency on another plugin, this will be defined in the plugin.xml file. At deploy-time, JDeveloper calls plugman to build and incorporate the 3rd party plugins defined in the maf-application.xml Plugins UI. If one of these plugins has a dependency, plugman will automatically attempt to download the required plugin's source code so it can be built and incorporated into your MAF app.

If your build machine is behind a firewall, this attempted download may cause the JDeveloper deployment to hang for a while before reporting error messages similar to the following:

[10:21:35 AM] Fetching plugin "https://github.com/apache/cordova-plugin-device" via git clone

[10:21:35 AM] Deployment Successful

[10:21:35 AM] Failed to install 'de.appplant.cordova.plugin.local-notification':Error: Command failed: Cloning into '/var/folders/h9/84x8qcsj391c355v3v137n580000gn/T/plugman/git/1423102819927'...

[10:21:35 AM] fatal: unable to access 'https://github.com/apache/cordova-plugin-device/': Failed to connect to github.com port 443: Operation timed out

[10:21:35 AM] at ChildProcess.exithandler (child_process.js:648:15)

[10:21:35 AM] at ChildProcess.emit (events.js:98:17)

[10:21:35 AM] at maybeClose (child_process.js:756:16)

[10:21:35 AM] at Socket.<anonymous> (child_process.js:969:11)

[10:21:35 AM] at Socket.emit (events.js:95:17)

[10:21:35 AM] at Pipe.close (net.js:465:12)

[10:21:35 AM] Command failed: Cloning into '/var/folders/h9/84x8qcsj391c355v3v137n580000gn/T/plugman/git/1423102819927'...

[10:21:35 AM] fatal: unable to access 'https://github.com/apache/cordova-plugin-device/': Failed to connect to github.com port 443: Operation timed out

The solution for this is:

  • Download and extract the required plugin as per Step 7 above.
  • Remove the dependent plugin from the Plugins UI.
  • Add the required plugin to the Plugins UI.
  • Add the dependent plugin to the Plugins UI. 

This will ensure that each plugin is built and incorporated into your MAF app in the correct order.

Sunday Dec 07, 2014

Rethink Mobile: Beyond Mobile First with Oracle

Get.Ready.

December 17, 2014 - Mark your calendars to get an update on Oracle's mobile strategy: Rethink Mobile: Beyond Mobile First with Oracle. We will be available to answer your questions via live chats. The Oracle guest lineup includes: Suhas Uliyar, Vice President, Mobile Strategy, Product Management, Laurent Pacalin, Group Vice President, Product Management, Jeanne Lowell, Vice President, E-Business Suite, and Rimi Bewtra, Sr. Director of Product Marketing.

And if you happen to be at Gartner AADI this week (Las Vegas!) stop by and say hello.  I'll give you a mobile tour at no extra charge and perhaps some free poker lessons. ;-) 

Here's a great trailer for the main event you do not want to miss.

Friday Nov 14, 2014

Oracle leaps ahead of SAP and IBM in MADP

Oracle leaps ahead of SAP and IBM in MADP

Gigom Research ranks Oracle at the top of the MADP list of vendors

[Read More]

Tuesday Oct 28, 2014

Oracle Alta Mobile UI Design Guidelines

During Oracle OpenWorld 2014 we announced the Oracle Alta Mobile UI Design Guidelines.

Oracle Alta UI is a new user interface design system that enables developers to create modern, visually pleasing, consistent and engaging interfaces across delivery channels. It provides a simplified component structure with more open space to keep the focus on the data. A broad and rich component set is available, with a native look and feel. The system is based on years of design and usability analysis from both platform and application UI specialists.

Oracle Alta Mobile UI provide a set of guidelines and best practices. This includes information on:

  • Design style including colors, icons, etc...
  • Details on MAF components used with this theme
  • Common enterprise design solutions shown in Mobile patterns
  • Examples shown in demos, sample code and galleries
  • Help getting started with Oracle Alta UI and MAF
  • Resources for Oracle Alta UI on the browser and the icon system
Oracle Alta Mobile UI Design Guidelines provides a good background on the default styles and capabilities of the MAF components, while giving design solutions on how to put those building blocks together into common enterprise design components.  

You can access the Oracle Alta Mobile UI Design Guidelines with a phone, tablet or desktop. Please take a few moments to learn more about Oracle Alta UI and how it can help with your current and future applications. 

Original Oracle.com Press Release

Monday Oct 13, 2014

Updated MAF Academy Course

Now available! The FREE online Oracle Developer Academy course, Developing Applications with Oracle Mobile Application Framework (MAF) has just been re-released to include all you need to know about securing a MAF application. Carve out two hours of time in your day* with this interactive, engaging online course and get a comprehensive education on Oracle MAF. You'll master the nuts and bolts of the architecture, be able to design and develop a MAF application with web services, integrate with native device capabilities, and secure the app and the services it accesses.

*or multiple days – the course player remembers where you were so you can easily pick up where you left off if necessary

http://bit.ly/MAFCourse

Note: The course requires the following:

Flash Player 10 or later, and one of the following browsers:

Internet Explorer 6 and later, Firefox 1.x and later, Google Chrome, Opera 9.5 and later, Safari 3 and later.

About

This blog is is dedicated to announcements,tips and tricks and other items related to developing, integrating, securing, and managing mobile applications using Oracle's Mobile Platform. It is created and maintained by the Oracle Mobile product development team.

Archive of past entries

Even More Mobile Development Blogs

Oracle A-Team Site - Mobile Related Entries

Code samples from the Community

Fusion Middleware Blogs

Search

Archives
« March 2015
SunMonTueWedThuFriSat
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
31
    
       
Today