Developing custom modules for Android for Pega 7.1.9
This tutorial describes how Android developers can extend their applications by using custom modules, which are developed in Java and expose their functionality through the JavaScript API. Custom modules enable features and functions that are available to native Android applications in Pega Platform mobile apps.
For example, a custom module can allow a Pega® Platform mobile app to use an embedded laser scanner to scan barcodes to be recognized and pasted into a product ID field, or to use a tablet's projector module to display wiring layout in equipment that needs to be serviced.
After they are created, custom modules are bundled with the Pega Mobile Client application in Designer Studio. For more information, see Uploading custom modules.
This article describes how to build a custom module that is based on a template.
- Technical overview
- Prerequisites
- Importing the Android custom module template
- Adding third-party dependencies
- Extending the client with additional plug-in classes
- Extending the client with extension classes
- Testing the custom module
- Requesting a permission to perform an action
- Enabling the logger API
- Packaging the custom module
- Testing ready-made custom modules
Technical overview
Custom modules are developed in Java for the Android platform. After you develop them in an external development environment, you can compile, debug, and test them before building the final application.
Plug-in classes
Custom modules contain one or more plug-in classes. These classes use a JavaScript-native side bridge to respond to JavaScript requests and fire JavaScript events. On Android, JavaScript plug-in classes must implement the com.pega.mobile.bridge.Plugin
interface. Classes implementing this interface are instantiated for each WebView instance when it is created, and remain valid within the context of this instance for its lifetime.
During the application-building phase, all assets related to the plug-in class are placed in a directory in the Android application (for example, assets). This is where to direct the JavascriptAppender
to load the JavaScript implementation (it uses the assets as its base directory).
Pega Mobile Client asks for the JavaScript code by calling the getJavascriptImplementation()
method on the Plugin instance. JavaScript code returned by plug-in classes is injected into the WebView during the web application's startup, before the onLaunchboxLoaded
method is called. An instance of the bridge object is available during the injection phase. JavaScript code must use this instance to communicate with the native code.
Our template contains a plug-in class already. To create additional plug-in classes, refer to Extending the client with plug-in classes.
Extension classes
To extend the client, for example, to grant access to client life cycle events, you can use extension classes. Custom modules contain one or more such classes. On Android, JavaScript plug-in classes must implement the com.pega.mobile.bridge.Extension
interface. An extension class enables simple initialization, such as creating singleton instances or registering for Android Application Lifecycle events. Its instance is created once, at client startup, and remains valid within the context of the client for its lifetime. If the custom module's library contains more than one extension class, Pega Mobile Client creates an instance for each of them.
Our template does not contain an extension class. To create extension classes, refer to Extending the client using extension classes.
Product modules
Pega Mobile Client consists of several modules. Our sample uses the Base
module, which contains the client's basic functionality and is a required dependency. For your custom module to have dependencies to modules other than Base
, modify the build.gradle file, found in your custom module's directory, by adding a dependency, for example:compile 'com.pegasoftware:media:+'
All Pega Mobile Client modules are in a local Maven repository, in the project's repository directory.
Source-code versus compiled custom modules
The procedure below describes options for packaging your module in a source-code form or compiled (binary) form. Custom modules in a source-code form are easier to package. However, transferring them and building the app in the Pega Platform takes longer. Binary custom modules help you save time needed to transfer the files and compile the app; therefore the compiled (binary) form is the preferred method.
For more information about packaging custom modules in source-code and binary form, see Packaging the custom module.
Android 6 permission model
Pega Mobile Client supports the runtime permission model that was introduced in Android 6. The user is asked to accept permissions at run time and can revoke certain permissions later. You must list the permissions in your custom module's AndroidManifest.xml file, and check the permission status every time you want to use it.
Pega Mobile Client provides a simplified API to support the new permission mechanism. This API is transparent for devices that do not support this functionality. To become familiar with the API, review the Javadoc information that is provided in the distribution package. Short usage instructions are in Requesting a permission to perform an action.
Prerequisites
To develop custom modules for Android, make sure that you have downloaded and configured the following items:
An up-to-date Android development environment:
IntelliJ Idea, configured for the Android SDK, plus:
- Android SDK Tools 23.0.5 or later
- Android SDK build tools 21.1.2
- Android SDK platform for API level 21
- Android SDK extras: Google Repository (rev. 18 or later), Google Play services (rev. 24 or higher), Android Support Repository (rev. 14 or later), Android Support Library (ver. 22.1.1 or later)
- Java Runtime Environment (JRE) 7 x64 or 8 x64
- A code-signing key (refer to Signing Your Applications)
- The distribution package for your platform, obtained from your Pegasystems Global Customer Support representative
Before you continue, review the documentation in the doc folder of the distribution package to become familiar with the native custom module API and JavaScript API.
Importing the Android custom module template
To import the custom module template:
- Extract the distribution package into a local directory of your choice.
- Navigate to the resources directory and copy the SampleCustomModule subdirectory to the modules directory.
- Rename the SampleCustomModule directory to the name of your target custom module, for example, MyCustomModule.
At the command prompt, navigate to the root directory of the distribution package and run the following command:
./gradlew :app:assembleDebug
- When the "BUILD SUCCESSFUL" message is displayed, run the software development tool and import the project:
- Click
Navigate to the distribution package's root directory.
Select the build.gradle file.
- Click
- Confirm the default options on the next screen, and click to initiate the import.
- When the project import finishes and MyCustomModule is visible in the Modules folder of the Project View pane, navigate to the MyCustomModule folder.
- Modify your custom module's AndroidManifest.xml file, for example, set the package name's value to
my-custom-module
. - Review the CustomPlugin.java and sample.js files and modify them to create your custom module.
Adding third-party dependencies
To add third-party dependencies to your module:
- Create a libs directory in your module's root directory.
- Paste third-party *.jar files there.
- Display the Gradle Projects tab on the software development tool's screen, and click the icon.
- To build your application by using the release build type (clearing the Build debuggable app check box on the Mobile tab), you must create a proguard-rules.pro file in your custom module's directory. This file lists rules needed to preserve all necessary classes/interfaces/enums that are used in your third-party libraries, for example:
- keep class com.samsung.** {*;}
Proguard removes obsolete classes by determining if they are directly referenced. It does not detect classes loaded via reflection. Therefore, the following lines must be included in the proguard-rules.pro file:
-keep class * implements com.pega.mobile.bridge.Extension { *; } -keep class * implements com.pega.mobile.bridge.Plugin { *; }
Extending the client with plug-in classes
The module template includes a plug-in class. To create additional plug-in classes:
- Create the plug-in class:
- In the Project View pane of the software development tool, create the following subfolder: modules/MyCustomModule/src/main/java/com.organization.custom.plugins.
Right-click the folder's icon, click > , and enter the name of the plug-in class, for example: "MyCustomPlugin". - Edit the Java class file and define the method implementations, for example:
package com.organization.custom.plugins; import com.pega.mobile.JavascriptAppender; import com.pega.mobile.bridge.EventLauncher; import com.pega.mobile.bridge.JavascriptCallback; import com.pega.mobile.bridge.Plugin; import com.pega.mobile.bridge.PluginInitializationContext; import java.util.Timer; import java.util.TimerTask; public class MyCustomPlugin implements Plugin { private static final String EVENT_NAME = "myCustomPlugin"; private static final String ON_TIMER_TICK = "onTimerTick"; private EventLauncher eventLauncher; private int counter = 0; private Timer timer; private TimerTask timerTask; @Override public void init(PluginInitializationContext pluginInitializationContext) { this.timer = new Timer(); this.eventLauncher = pluginInitializationContext.getEventLauncher(); } @Override public void appendJavascript(JavascriptAppender javascriptAppender) { javascriptAppender.appendFile("js/myCustomPlugin.js"); } @Override public String getName() { return "MyCustomPlugin"; } @SuppressWarnings("unused") public void startTimer(Object data, JavascriptCallback callback) { if (timerTask != null) { callback.call("onFailure", null); return; } String message = (String)data; timerTask = new TimerTask() { @Override public void run() { eventLauncher.fireEvent(EVENT_NAME, ON_TIMER_TICK, counter++); } }; timer.schedule(timerTask, 0, 1000); String outputMessage = my.third.party.library.Capitalize.capitalize(message) + " : " + counter; callback.call("onSuccess", outputMessage); } @SuppressWarnings("unused") public void finishTimer(Object data, JavascriptCallback callback) { callback.call("onFinished", null); timerTask.cancel(); timerTask = null; } @Override public void onPageLoaded() {} @Override public void onReload() {} @Override public void onDestroy() {} }
- In the Project View pane of the software development tool, create the following subfolder: modules/MyCustomModule/src/main/java/com.organization.custom.plugins.
To implement the JavaScript side of the custom module:
Create a myCustomModule.js file in the modules/MyCustomModule/src/main/assets/js directory.
Edit the resource to create code that is similar to the following example:
(function() { window.launchbox.MyCustomPlugin = { startTimer: function(message, callback) { bridge.call("MyCustomPlugin", "startTimer", message, { onSuccess: callback.onSuccess, onFailure: callback.onFailure }); }, finishTimer: function(callback) { bridge.call("MyCustomPlugin", "finishTimer", undefined, { onFinished : callback}); }, registerListener : function(listener) { var wrapper = { onTimerTick : listener }; bridge.registerForEvent("myCustomPlugin", wrapper, false, listener); } }; })();
The name of the plug-in class used in thebridge.call
method must be the same as the one that was defined at the beginning of this procedure.
To proceed to the testing and packaging phase:
Place any additional resources that are required by your module, such as JavaScript files, graphics, and so on, in the modules/MyCustomModule/src/main/assets directory.
To avoid build errors, remove all the attributes in the application section of the AndroidManifest.xml file in your custom module's folder.
Extending the client with extension classes
The preceding example does not contain any extension classes. To create an extension class, follow the procedure below.
When you create the main extension, your class should implement the basic Extension
interface provided by the API in the /extensions/API directory. The implemented methods are called in response to application life cycle events, which are described in the Android APIs reference.
The ExtensionInitializer
interface is optional and is implemented when the extension needs contextual information.
To create the main extension class:
- In the Project View pane, go to the module's package subfolder:
modules/MyCustomModule/src/main/java/com.organization.custom.extensions.
Right-click the folder's icon, click > , and enter the name of the extension class, for example: "MyCustomExtension". - Edit the Java class file and define the method implementations, similar to the following example:
package com.organization.custom.extensions; import android.content.Context; import android.os.Bundle; import com.pega.commontools.androidlogger.ALog; import com.pega.mobile.bridge.Extension; import com.pega.mobile.bridge.ExtensionContext; import com.pega.mobile.bridge.ExtensionInitializer; public class MySampleExtension implements Extension, ExtensionInitializer { private Context context; @Override public void init(ExtensionContext extensionContext) { this.context = extensionContext.getActivityContext(); } @Override public void onCreate(Bundle bundle) {} @Override public void onStart() {} @Override public void onRestart() { ALog.d("MySampleExtension", "On Restart callback invoked!"); } @Override public void onResume() {} @Override public void onPause() {} @Override public void onStop() {} @Override public void onDestroy() {} }
Testing the custom module
To test your custom module locally:
- Modify the app.properties file, which is in the distribution package's main directory:
- Change a value of the
bootstrap.config.dir
parameter toresources/pega7OfflineBootstrap
- Set a value of the
bootstrap.string.applicationURL
to your intended instance of the Pega Platform, for example:http://test.server.com:8243/prweb/
- Optional: To debug the JavaScript code, change a value of the
webContentsDebugging
parameter to true - Set the other parameters as necessary (read the inline comments for details).
- Change a value of the
- Run
./gradlew :app:assembleDebug
at a command prompt to build a binary Pega Mobile Client package that includes your custom module.
The APK file is created in the app/build/outputs/apk directory. This file contains a native application that can be installed and debugged on a device or an emulator.
Requesting a permission to perform an action
To perform an action that requires a permission in Android 6 and later:
- Extend your module by adding Dagger libraries to the project.
- Inject a singleton instance of the
PermissionRequestor
object by using Dagger.
- Call the
permissionRequestor.requestPermissions(),
passing an array of permission names, as defined in the Android developer's reference, and aResultHandler
handler, which should contain actions to be performed when the permission request result is retrieved.
When the result is retrieved:
- If all permissions are granted, the
onPermissionsGranted
callback is called. - If any of the permissions are denied, the
onPermissionsDenied
is called, with a list of permissions that are not granted.
Each permission is represented by an instance of theDeniedPermission
object, from which you can retrieve the permission name and the Boolean value of theshouldShowRequestPermissionRationale
parameter. This parameter passes the user's requirement to provide a justification for the permission. If the user refused the permission before, the value of this parameter is true.
Enabling the logger API
The Logger API is superior to an Android's original Log API, which does not allow access to its logs or setting a logging level. To use the Logger API in the custom module's native code, do the following steps:
- Import the com.pega.commontools.androidlogger.ALog class.
- Use the ALog class in your code. In terms of its use, it is identical to the android.util.log class.
Packaging the custom module
When your custom module is ready and has been tested, prepare it for packaging within a .zip file, for example custom-module-assets.zip file. To obtain a sample file, right-click and save the following link:
custom-module-assets.zip (0.83 MB)
- Place all binary modules from one developer in a subfolder with a unique folder name and combine their individual proguard-rules.pro files into one.
- Ensure that all source-code modules have unique names.
Optional: To package your custom module in a binary form:
- Open the build.gradle file, which is in your custom module's directory, and modify paths in the following code to match your environment:
apply plugin: 'maven' uploadArchives { repositories.mavenDeployer { pom.groupId = 'com.pega.' pom.artifactId = 'my-custom-module' pom.version = '1.0.0' repository(url: "file:///home/user/Documents/modules-android/<unique_folder_name>") } }
Navigate to the root directory of your distribution package and enter the following command:
./gradlew :MyCustomModule:assemble :MyCustomModule:uploadArchives
Locate the modules-android folder and place it in the custom-module-assets.zip file.
- Open the build.gradle file, which is in your custom module's directory, and modify paths in the following code to match your environment:
Optional: To package your custom module in a source-code form:
If you tested your custom module by doing the steps in the Testing the custom module section, navigate to your custom module's directory, for example, MyCustomModule, and remove the build folder that is created there as part of the testing process.
When you build an app that is intended for production and you have set the value of thewebContentsDebugging
parameter to true, consider reverting it to false and repeating the building procedure.Copy your custom module's directory and place it in the modules-android folder of the custom-module-assets.zip file.
Upload your custom module to the Pega Platform. For more information, see Uploading custom modules.
Testing ready-made custom modules
If you want to test a custom module that has already been built, for example, when it comes from a different developer:
- Modify the app.properties file that is in the distribution package's main directory:
- Change a value of the
bootstrap.config.dir
parameter toresources/pega7OfflineBootstrap
. - Set a value of the
bootstrap.string.applicationURL
to your intended instance of the Pega Platform, for example:http://test.server.com:8243/prweb/
. - Optional: To debug the JavaScript code, change a value of the
webContentsDebugging
parameter to true. - Set the other parameters as necessary (read the inline comments for details).
- Change a value of the
- Paste your custom module files, either in source-code or compiled format, into the modules directory of the distribution package. For additional information, refer to the README.txt file in the modules directory.
- Run
ant
at a command prompt.
The APK file is created in the out directory.