Skip to Content

This post explains how we can create an Android PhoneGap plugin for SAPUI5 mobile app to achieve certain native functionality. Let’s say we have a user of SAPUI5 mobile app who travels across places to collect transaction data from different vendors where there is no proper network, so he/she wants to maintain the data locally in a device log file and when he connects to network the same transaction data should be read from the log file and uploaded to the server, this log file can be referred anytime offline or even could be shared with others as an attachment in mail.

SAPUI5 being a client side UI library doesn’t have access to create/maintain a file locally in the device. For the same reason we need to use hybrid web container framework PhoneGap. PhoneGap provides standard APIs for certain functionalities and Native Plugin development capability. By creating PhoneGap plugins we can interact in a more detailed manner with device features and functions that are not already exposed through the PhoneGap API.

The prerequisite for this post is the basic knowledge on Creating SAPUI5 Applications and fair idea on PhoneGap.

Step 1) Creating a SAPUI5 mobile Application

First create a simple SAPUI5 mobile application with just one input box and button on the UI to trigger the Plugin call (in our case this button will trigger the transaction in the app from UI5 side passing the value of amount entered in the input box and sends a method call to plugin for maintaining log, once the log is maintained in a local file by the plugin java code, it will return back the status to ui5).

DemoAppUI5.png

Step 2) Downloading Cordova

Download the latest version of Cordova from the Cordova site (preferably the zip format – cordova-3.4.0-src.zip), link below…

http://cordova.apache.org/

Extract the main folder (cordova-3.4.0) and then the subfolder (Cordova-android) and place the same in C:\ drive

Step 3) Creating the Android Cordova package

Open Command prompt and navigate to the below link

Cd C:\cordova-3.4.0\cordova-android\cordova-android\bin

Then give the command – create D:\PhoneGap\demoapp org.sample DemoApp and hit Enter, This will create a DemoApp with the package org.sample in the location D:\PhoneGap\demoapp

Cordova package.png

Inside the location D:\PhoneGap\demoapp you can find the folder CordovaLib\src copy the contents of this src to the main src folder D:\PhoneGap\demoapp\src

Step 4) Setting up the project in Eclipse

The following step requires the Eclipse installed with Android SDK and ADT plugins for the development of Android native app.

Open eclipse and import the DemoApp project (New->other->Android->Android project from existing code), exclude the CordovaLib sub project since we have already copied the contents of this src folder to the main DemoApp src folder.

Eclipse android project.png

Copy the contents from the SAPUI5 project created before to the Android project assests/www folder (index.html, views, and controllers)

DemoApp.png

Include a reference in the head tag of the document index.html to the Cordova and index JavaScript file.


<script type=”text/javascript” src=”cordova.js”></script>
<script type=”text/javascript” src=”js/index.js”></script>






Step 5) Creating a Plugin (JS)

Create a new JavaScript file in the assets/www directory called MyPlugin.js and place the following code at the top of the new file:


var MyPlugin = function(){};
MyPlugin.prototype.savelog = function(message, successCallback, errorCallback)
{
try{      
cordova.exec(successCallback,errorCallback,’MyPlugin’,’savelog’,message);
}
catch(e){}
};





Include a reference in the head tag of the document index.html to the newly created plugin JavaScript file.

<script type=”text/javascript” src=”MyPlugin.js”></script>

Below this, add a new script tag block, inside of which place the deviceready event listener and savelog method. The code to be added is given below.


<script type=”text/javascript”>
        var onDeviceReady = function(){};
        function savelog(amount)
        {
            var msg = window.plugins.MyPlugin.savelog(amount,
                                    function(sMessage)
                                    {
                                    return sMessage;
                                    },
                                    function(eMessage)
                                    {
                                    return sMessage;
                                    }
                        );
                        return msg;
        }
        function init()
        {
            document.addEventListener(“deviceready”,onDeviceReady,true);
        }
</script>





Call the init() function in the onLoad of body

<body onLoad=”init();” class=”sapUiBody” role=”application”>

Firstly, we define the class name for our JavaScript plugin, and then create the first method we will invoke called savelog.

Add the following code to the bottom of the file to load the MyPlugin class into the window.plugins object


if(!window.plugins) {
            window.plugins = {};
}
if (!window.plugins.MyPlugin) {
window.plugins.MyPlugin = new MyPlugin();
}






Step 6) Creating a Plugin (Java)

Create a new Java class file in your project by accessing File|New|Class from the main Eclipse menu. You can also access the wizard by selecting File|New|Other from the main menu and finding the Java class option in the list, as shown in the following screenshot:

The New Java Class wizard will open to help you complete this task. Provide the reverse domain format package name, and set the class name to MyPlugin. As we want to hook into the Cordova framework, we will also make sure it extends the org.apache.cordova.CordovaPlugin class, which is part of the Cordova core API.

Click on the Finish button to complete the wizard. The new Java class file will now be open in your Eclipse editor window.  Let’s now add the native code for our plugin. We’ll begin by wrapping our code in a try/catch block to trap and handle any errors.

We’ll qualify the action value sent through to the plugin to ensure we have a valid method to run. In this case, we’ll check to make sure that it matches savelog. Path of the log file is maintained in the code as /sdcard/Apps/logs.txt,

The full code is given below.


package com.sample;
import org.apache.cordova.CordovaPlugin;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import org.json.JSONException;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.PluginResult;
public class MyPlugin extends CordovaPlugin {
            @Override
            public boolean execute(String action, String args, CallbackContext callbackContext) throws JSONException
            {
                        boolean stat = false;
                        if (“savelog”.equals(action)) {
                                     try {
                                                File file = new File(“/sdcard/Apps/log.txt”);
                                                boolean fileCreated;
                                                if (file.exists()) {
                                                            fileCreated = true;
                                                }
                                                else
                                                {
                                                            fileCreated = file.createNewFile();
                                                }
                                                if(fileCreated)
                                                {
                                                            PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(“/sdcard/Apps/log.txt”,true)));
                                                            out.println(args);
                                                            out.close();
                                                }
                                                else
                                                {
                                                            String result = “Internal Error”;
                                                            PluginResult.Status status = PluginResult.Status.ERROR;
                                                            PluginResult pluginResult = new PluginResult(status, result);
                                                            callbackContext.error(result);
                                                            stat = false;
                                                            return false;
                                                }
                                                String result = “Transation saved”;
                                                PluginResult.Status status = PluginResult.Status.OK;
                                                PluginResult pluginResult = new PluginResult(status, result);
                                                callbackContext.success();
                                                stat = true;
                                                return true;                               
                                    } catch (Exception e) {
                                                String result = e.getMessage();
                                                PluginResult.Status status = PluginResult.Status.ERROR;
                                                PluginResult pluginResult = new PluginResult(status, result);
                                                callbackContext.error(result);
                                                stat = false;
                                                return false;
                                    }
                        }
                        return stat;
            }
}






Step 7) Mapping our plugin and Maintaining the uses permission

Before we can run the application, we need to add a mapping to our plugin within the Cordova config.xml file in the location res/xml/. Right-click on the file and select Open With Text Editor. Locate the plugins section and add a reference to our plugin within the following node:


<feature name=”MyPlugin”>
<param name=”android-package” value=”com.sample.MyPlugin” />
</feature>





Also don’t forget to add the uses permission in the AndroidManifest.xml for creating and writing a file in the external storage and to access the SAPUI5 libraries.


<uses-permission android:name=”android.permission.INTERNET” />
<uses-permission android:name=”android.permission.WRITE_EXTERNAL_STORAGE” />





Ideally for our use case the INTERNET permission is not applicable since the user mainly wants this functionality to work in remote parts by offline mode, for the same we also have to package the required SAPUI5 libraries in to the app resources instead of accessing the same from online, we will see about packaging the required UI5 library into the app in the following posts.

Step 8) Running our App

Let’s test our application and make sure we get a response from the plugin. Right-click on the main project folder and select Run as Android Application from the context menu to launch the application on the emulator. Here is the final output of the App running in mobile.

Final DemoApp.png

The complete project structure is as shown below

Complete project.png

How it works…

We created a JavaScript file to hold our client-side method calls and to handle success or error call-backs. We then created the native portion of the plugin, which extended the org.apache.cordova.CordovaPlugin class, thereby providing us with access and the ability to communicate between the JavaScript and native code.

Our JavaScript code called the cordova.exec method to invoke methods within our custom class:


cordova.exec(
successCallback,
errorCallback,
'MyPlugin',
'savelog',
[message]
);





The exec () method accepts the following parameters:

Success: The success call-back function, which will run following a successful response from the plugin.

Error: The error call-back function, which will run following an unsuccessful operation and response from the plugin.

Service: This is mapped to the name of the native plugin class.

Action: The name of the action within the custom class to call and invoke.

Arguments: This is an optional parameter that allows you to send through data in an array, which can then be processed and used by the custom class.

The cordova.exec JavaScript method then invokes the execute function on the corresponding native plugin class.

The complete codes are given below.

Index.html


<!DOCTYPE html>
<html>
    <head>
        <meta charset=”utf-8” />
        <meta name=”format-detection” content=”telephone=no” />
        <meta name=”viewport” content=”user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi” />
        <meta http-equiv=”X-UA-Compatible” content=”IE=edge”>
                        <meta http-equiv=’Content-Type’ content=’text/html;charset=UTF-8’/>
                        <script src=”https://sapui5.netweaver.ondemand.com/sdk/resources/sap-ui-core.js”
                                                id=”sap-ui-bootstrap”
                                                data-sap-ui-libs=”sap.m”
                                                data-sap-ui-theme=”sap_bluecrystal”>
                        </script>
                        <script>
                                sap.ui.localResources(“demoappui5”);
                                 var app = new sap.m.App({initialPage:”idmainView1”});
                                  var page = sap.ui.view({id:”idmainView1”, viewName:”demoappui5.mainView”, type:sap.ui.core.mvc.ViewType.XML});
                                    app.addPage(page);
                                    app.placeAt(“content”);
                        </script>
        <link rel=”stylesheet” type=”text/css” href=”css/index.css” />
        <title>DemoApp</title>
        <script type=”text/javascript” src=”cordova.js”></script>
        <script type=”text/javascript” src=”js/index.js”></script>
        <script type=”text/javascript” src=”MyPlugin.js”></script>
        <script type=”text/javascript”>
        var onDeviceReady = function(){};
        function savelog(amount)
        {
            var msg = window.plugins.MyPlugin.savelog(amount,
                                    function(sMessage)
                                    {
                                    return sMessage;
                                    },
                                    function(eMessage)
                                    {
                                    return sMessage;
                                    }
                        );
                                    return msg;
        }
        function init()
        {
            document.addEventListener(“deviceready”,onDeviceReady,true);
        }
        </script>
        <script>
                   sap.ui.localResources(“demoappui5”);
                   var app = new sap.m.App({initialPage:”idmainView1”});
                    var page = sap.ui.view({id:”idmainView1”, viewName:”demoappui5.mainView”, type:sap.ui.core.mvc.ViewType.XML});
                                                app.addPage(page);
                                                app.placeAt(“content”);
                        </script>
    </head>
    <body onLoad=”init();” class=”sapUiBody” role=”application”>
        <div id=”content”></div>
    </body>
</html>



mainView.view.xml


<core:View xmlns:core=”sap.ui.core” xmlns:mvc=”sap.ui.core.mvc” xmlns=”sap.m”
                        controllerName=”demoappui5.mainView” xmlns:html=”http://www.w3.org/1999/xhtml”>
            <Page title=”DemoApp”>
                        <content>
                                    <Input id=”amount” type=”Text”></Input>
                                    <Button press=”logHandler” text=”Save Offline (plugin call)”/>
                                    <Button press=”sync” text=”Sync Data”/>
                        </content>
            </Page>
</core:View>






mainView.controller.js


sap.ui.controller(“demoappui5.mainView”, {
            logHandler: function(){
                        var amt = this.oView.byId(“amount”).mProperties.value;
                        //do your functionality here
                        var status = savelog(amt);
                        this.oView.byId(“amount”).mProperties.value = “”;
                        if(status == “OK”)
                        {
                                    sap.m.MessageToast.show(“Transaction log saved”);
                        }
                        else
                        {
                                    sap.m.MessageToast.show(“Transaction log failed; ” + status);
                        }
            }
});





I hope this post would be helpful to you all, feel free to comment for any queries. Thank you.

Kishore Kumar

To report this post you need to login first.

7 Comments

You must be Logged on to comment or reply to a post.

    1. Kishore Kumar Post author

      Hey Jason,

      Apologize for the late reply, I hope you would have sorted out this issue, But still you can find the demoappui5 folder inside the asset of your package. Let me know if you need any further help

      (0) 
  1. Patrick Both

    Hi Kumar,

    first of all: Thx for the blog. Helped me a lot.

    But finally I still have a Problem. The App shows the Message “TRANSACTION LOG FAILED; UNDEFINED”!

    In my LogCat it tells me that a second callback on the Tag CordovaPlugin for ID: MyPlugin was done and the Result was “Invalid Action”.

    Perhaps someone can help me with that.

    Perhaps I placed the windows.plugin to call the MyPlugin Class at the wrongh place.

    Thx and Best Regards

    Patrick

    (0) 

Leave a Reply