X

The Mobile & Digital Assistant Blog covers the latest in mobile and conversational AI development and engagement

Integrating a custom Cordova plugin into a MAF app

Graeme Mawson
Senior Principal Product Manager

In this earlier post, I demonstrated how to create a simple Cordova plugin for each of Android and iOS.  In this post, I’ll describe how to include that plugin into a MAF 2.1.0 app and how to invoke it from Java, from AMX and from local HTML.

You can follow these same instructions to integrate any 3rd party or custom Cordova plugin into your MAF app.

How do I include a custom Cordova plugin into my MAF app?

To include a custom Cordova plugin into a MAF app, you specify the location of the plugin’s top-level folder (on your local hard drive) in the app’s maf-application.xml > Plugins UI in JDeveloper, as described in the MAF 2.1.0 Documentation.

The path to the plugin will be stored in maf-plugins.xml and will be relative to that file’s location. To avoid any deploy-time issues, make sure theplugin’s top-level folder is on the same drive as your app and that there are no spaces in its path. If your plugin is specific to your MAF app, you probably want to include it within the app’s source tree.

The plugin will appear in the maf-application.xml > Plugins UI as follows:

Note: If you want to use a 3rd party plugin, you must first download it (as a zip file) and extract it to your local hard drive as described in Step 7 of this blog post.

How do I invoke the plugin’s methods?

As described in the earlier post, a Cordova plugin includes a JavaScript interface that defines its available functions. You must write your own JavaScript in your MAF application to call the plugin’s JavaScript interface so your MAF application can interact with the plugin.

In the example “Alert” plugin created in the earlier post, we defined the following JavaScript interface in the alert.js file:

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

Our plugin’s manifest file, plugin.xml, defined the JavaScript module as follows:

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

This means that the alert function will be exported as part of the Alert JavaScript module within the consuming MAF application, which enables you to call this function from JavaScript as follows:

  Alert.alert(“My title”, “My message”, “My button label”, myCallbackFunction);

As per the plugin design, this code would cause a native popup dialog to be displayed with “My title” as the title, “My message” as the message and a single button with the label “My button label”. When the user taps the button to dismiss the dialog, the JavaScript callback function myCallbackFunction will be executed with an input parameter of 0.

For more information on using JavaScript callback functions, refer to this helpful blog post by Michael Vollmer.

Note: If you want to use a 3rd party plugin, the JavaScript interface and how to call it should be described in the plugin’s README.md file, typically found in the
plugin’s top-level folder.

How do I invoke the plugin from a Local HTML page?

You could invoke the plugin’s JavaScript interface methods directly from your HTML code, but it’s more common to provide a wrapper function that simplifies your code and abstracts you from possible changes to the plugin’s interface.

Let’s take a look at a local HTML page that calls our “Alert” plugin:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport"

          content="width=device-width, initial-scale=1, maximum-scale=1"/>
    <title>Plugin Demo</title>
    <script type="text/javascript">if (!window.adf) window.asf = ();
                                   adf.wwwPath = "../../../../www/";</script>
    <script type="text/javascript" src="../../../../www/js/base.js"></script>
    <script type="text/javascript">
      function ahowAlert(frm) {
        Alert.alert(frm.title.value,
                    frm.message.value,
                    frm.buttonLabel.value,
                    function(button) {
                      console.log("User tapped button:" + button);
                    });
      }
    </script>
  </head>
  <body>
    <form>
      <br>Title: <input type="text" name="title" value="Title">
      <br>Message: <input type="text" name="message" value="Message">
      <br>Button Label: <input type="text" name="buttonLabel" value="Button">
      <br><input type="button" value="Alert" onclick="showAlert(this.form);">
    </form>
  </body>
</html>

The <meta> tag is included here to avoid the user seeing the components on the page increase or decrease in size as she interacts with it.

You must include the two lines of JavaScript that define the window.adf object and include base.js. There is no need to explicitly include the plugin’s JavaScript file here, since at deploy-time it will be injected into base.js.

A simple showAlert function has been defined to wrap the call to Alert.alert, which in turn simplifies the calling code within the HTML form.

The showAlert function is called when the user taps on the “Alert” button. The “title”, “message” and “button label” input text values are read from the form and passed to Alert.alert, along with an anonymous callback function that will be called when the user taps on the dialog’s button. This callback function will be passed a value for the input parameter button, which we know will be 0, since that was hard-coded in our plugin’s native code.

How do I invoke the plugin from an AMX page?

AMX components cannot call JavaScript methods directly, so you must implement a Java method that is called from an AMX component, which in turn executes some JavaScript that calls the plugin’s JavaScript interface.

Let’s take a look at an AMX page that is similar to the local HTML page described above:

<?xml version="1.0" encoding="UTF-8" ?>
<amx:view xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:amx="http://xmlns.oracle.com/adf/mf/amx"
          xmlns:dvtm="http://xmlns.oracle.com/adf/mf/amx/dvt">
  <amx:panelPage id="pp1">
    <amx:facet name="header">
      <amx:outputText value="Plugin Demo" id="ot1"/>
    </amx:facet>
    <amx:inputText label="Title" id="it1"
                   value="#{applicationScope.PluginBean.alertTitle}"/>
    <amx:inputText label="Message" id="it2"
                   value="#{applicationScope.PluginBean.alertMessage}"/>
    <amx:inputText label="Button Label" id="it3"
                   value="#{applicationScope.PluginBean.alertButtonLabel}"/>
    <amx:commandButton text="Alert" id="cb1"
                       actionListener="#{PluginBean.popupAlertDialog}"/>
  </amx:panelPage>
</amx:view>

The three inputText components store their values in three attributes of an bean called PluginBean. When the user taps the commandButton component, it calls the popupAlertDialog method of PluginBean.

Let’s take a look at the PluginBean:

package mobile;

import oracle.adfmf.amx.event.ActionEvent;
import oracle.adfmf.framework.api.AdfmfContainerUtilities;
import oracle.adfmf.framework.api.AdfmfJavaUtilities;

public class PluginBean {
  private String alertTitle"Title";
  private String alertMessage"Message";
  private String alertButtonLabel"Button";

  public PluginBean() {
  }

  public void popupAlertDialog(ActionEvent actionEvent) {
    AdfmfContainerUtilities.invokeContainerJavaScriptFunction(
      AdfmfJavaUtilities.getFeatureId(),

      "showAlert",
      new Object[] {
        alertTitle,
        alertMessage,
        alertButtonLabel
      });
  }

  public void setAlertTitle(String alertTitle) {
    this.alertTitle = alertTitle;
  }

  public String getAlertTitle() {
    return alertTitle;
  }

  public void setAlertMessage(String alertMessage) {
    this.alertMessage = alertMessage;
  }

  public String getAlertMessage() {
    return alertMessage;
  }

  public void setAlertButtonLabel(String alertButtonLabel) {
    this.alertButtonLabel = alertButtonLabel;
  }

  public String getAlertButtonLabel() {
    return alertButtonLabel;
  }
}

The popupAlertDialog method calls AdfmfContainerUtilities.invokeContainerJavaScriptFunction to invoke a JavaScript function called showAlert, passing the alertTitle, alertMessage and alertButtonLabel attributes as input parameters to the JavaScript function.

You may wonder why the plugin’s alert JavaScript method is not invoked directly and we call the showAlert function instead. The reason is that the plugin’s alert method requires a JavaScript function callback to be passed as an input parameter and there is no way to do this from Java. Passing a null value may have unwanted side effects, so that is not recommended.

For this reason, we need to write a JavaScript function that wraps the call to the plugin’s alert method. There are two options for this.

One option is to create a JavaScript file and include it into each feature that wishes to invoke the plugin via the app’s maf-feature.xml UI as follows:

The JavaScript file would look similar to the following:

showAlert = function(title, message, buttonLabel) {
  Alert.alert(title,
              message,
              buttonLabel,

              function(button) {
                console.log("User tapped button: " + button);
              });
};

This JavaScript file defines a global function variable called showAlert, which accepts 3 input parameters and calls the plugin’s alert method with those 3 parameters, along with an anonymous callback function that will be executed when the user taps on the dialog’s button. The native code will pass a return value of 0 to the anonymous callback function as the input parameter button.

The other option, if the plugin is to be invoked from a single AMX page, is to include the wrapper function as verbatim JavaScript within the AMX page, similar to the following:

<?xml version="1.0" encoding="UTF-8" ?>
<amx:view xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:amx="http://xmlns.oracle.com/adf/mf/amx"
          xmlns:dvtm="http://xmlns.oracle.com/adf/mf/amx/dvt">
  <amx:panelPage id="pp1">
    <amx:verbatim id="v1">
      <![CDATA[
        <script type="text/javascript">
          function showAlert(title, message, buttonLabel) {
            Alert.alert(title,
                        message,
                        buttonLabel,
                        function(button) {
                          console.log("User tapped button: " + button);
                        });
          }
        </script>
      ]]>

    </amx:verbatim>
    <amx:facet name="header">
      <amx:outputText value="Plugin Demo" id="ot1"/>
    </amx:facet>
    <amx:inputText label="Title" id="it1"
                   value="#{applicationScope.PluginBean.alertTitle}"/>
    <amx:inputText label="Message" id="it2"
                   value="#{applicationScope.PluginBean.alertMessage}"/>
    <amx:inputText label="Button Label" id="it3"
                   value="#{applicationScope.PluginBean.alertButtonLabel}"/>
    <amx:commandButton text="Alert" id="cb1"
                       actionListener="#{PluginBean.popupAlertDialog}"/>
  </amx:panelPage>
</amx:view>

In either case, there is no need to include the plugin’s JavaScript file in the calling feature(s), since at deploy-time it will be injected into base.js.

Conclusion

You now have the knowledge required to create your own custom Cordova plugin and integrate it into your MAF 2.1.0 app, invoking it from local HTML, an AMX page, or from Java code.

Once you’ve tested your code successfully, you might consider publishing the plugin to make it available to the greater Cordova community. This can be achieved 
by calling plugman as follows:

$ plugman publish path-to-plugin-top-level-folder

Join the discussion

Comments ( 2 )
  • Sreekumar Thursday, July 21, 2016

    Excellent Article! It's very helpful.


  • guest Friday, August 26, 2016

    Nice one !

    How to debug the custom third party cordova plugin in Maf


Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.

Recent Content