This content has been archived.

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

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:

  • 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:

  1. Extract the distribution package into a local directory of your choice.
  2. Navigate to the resources directory and copy the SampleCustomModule subdirectory to the modules directory.
  3. Rename the SampleCustomModule directory to the name of your target custom module, for example, MyCustomModule.
  4. At the command prompt, navigate to the root directory of the distribution package and run the following command:
    ./gradlew :app:assembleDebug

  5. When the "BUILD SUCCESSFUL" message is displayed, run the software development tool and import the project:
    1. Click Import project.
    2. Navigate to the distribution package's root directory.

    3. Select the build.gradle file.

    4. Click OK.
  6. Confirm the default options on the next screen, and click OK to initiate the import.
  7. When the project import finishes and MyCustomModule is visible in the Modules folder of the Project View pane, navigate to the MyCustomModule folder.
  8. Modify your custom module's AndroidManifest.xml file, for example, set the package name's value to my-custom-module.
  9. Review the CustomPlugin.java and sample.js files and modify them to create your custom module.

Adding third-party dependencies

The instructions in the following sections assume the use of a third-party library that capitalizes selected letters. You can create one yourself or replace it with a library of your choice.

To add third-party dependencies to your module:

  1. Create a libs directory in your module's root directory.
  2. Paste third-party *.jar files there.
  3. Display the Gradle Projects tab on the software development tool's screen, and click the Refresh icon.
  4. 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:

  1. Create the plug-in class:
    1. 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 New > Java Class, and enter the name of the plug-in class, for example: "MyCustomPlugin".
    2. 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() {}
      }
  2. To implement the JavaScript side of the custom module:

    1. Create a myCustomModule.js file in the modules/MyCustomModule/src/main/assets/js directory.

    2. 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 the bridge.call method must be the same as the one that was defined at the beginning of this procedure.
  3. To proceed to the testing and packaging phase:

    1. 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.

    2. 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:

  1. 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 New > Java Class, and enter the name of the extension class, for example: "MyCustomExtension".
  2. 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:

  1. Modify the app.properties file, which is in the distribution package's main directory:
    1. Change a value of the bootstrap.config.dir parameter to resources/pega7OfflineBootstrap
    2. Set a value of the bootstrap.string.applicationURL to your intended instance of the Pega Platform, for example: http://test.server.com:8243/prweb/
    3. Optional: To debug the JavaScript code, change a value of the webContentsDebugging parameter to true
    4. Set the other parameters as necessary (read the inline comments for details).
  2. 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:

  1. Extend your module by adding Dagger libraries to the project.
  2. Inject a singleton instance of the PermissionRequestor object by using Dagger.
You must declare your class in the injects section of a Dagger @Module.
  1. Call the permissionRequestor.requestPermissions(), passing an array of permission names, as defined in the Android developer's reference, and a ResultHandler 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 the DeniedPermission object, from which you can retrieve the permission name and the Boolean value of the shouldShowRequestPermissionRationale 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:

  1. Import the com.pega.commontools.androidlogger.ALog class.
  2. 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:

icon_zip.gifcustom-module-assets.zip (0.83 MB)

If you want to bundle custom modules from different developers within one .zip file, you can avoid conflicts between modules from different sources:
  • 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.
  1. Optional: To package your custom module in a binary form:

    1. 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>")
        }
      }
    2. Navigate to the root directory of your distribution package and enter the following command:

      ./gradlew :MyCustomModule:assemble :MyCustomModule:uploadArchives

    3. Locate the modules-android folder and place it in the custom-module-assets.zip file.

  2. Optional: To package your custom module in a source-code form:

    1. 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 the webContentsDebugging parameter to true, consider reverting it to false and repeating the building procedure.
    1. Copy your custom module's directory and place it in the modules-android folder of the custom-module-assets.zip file.

  3. 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:

  1. Modify the app.properties file that is in the distribution package's main directory:
    1. Change a value of the bootstrap.config.dir parameter to resources/pega7OfflineBootstrap.
    2. Set a value of the bootstrap.string.applicationURL to your intended instance of the Pega Platform, for example: http://test.server.com:8243/prweb/.
    3. Optional: To debug the JavaScript code, change a value of the webContentsDebugging parameter to true.
    4. Set the other parameters as necessary (read the inline comments for details).
  2. 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.
  3. Run ant at a command prompt.

The APK file is created in the out directory.

Have a question? Get answers now.

Visit the Pega Support Community to ask questions, engage in discussions, and help others.