A Peek Under the Hood – How to Build Cordova Android Plugins

   Hybrid
A Peek Under The Hood

Hybrid applications are one of the major triumphs of web development. We get to write code that runs on the web and can be published to app stores to be downloaded and used by your customers. It’s pretty neat to be able to write code once and compile for multiple platforms. Ionic Framework has enhanced this experience to create compatibility for several platforms such as iOS, Android, Windows and even desktop apps. One of the features Ionic provides is the ability to wrap Cordova plugins in ‘Ionic Native’ plugins. Cordova plugins enable Hybrid applications to use native device features such as Camera, Geolocation, etc.

While developing unique solutions we sometimes encounter situations that make us wish we had better insight into these plugins. For example, if you want to add a little tweak to a plugin feature or (my personal nightmare) when there is a bug that needs fixing and for some reason the plugin is no longer maintained. For some time now Cordova plugins have been a black hole for many Hybrid developers where troubleshooting goes to die. It was on one of my many troubleshooting escapades that I decided to learn more about Cordova plugins and how they work.

My existing experience developing Android applications made the learning curve to Cordova Android plugin development a lot less steep. Nevertheless, I can say that one doesn’t need to be an expert native platform developer to create Cordova plugins that access native features of said platform (a theory which will be proven in my next article). This is mainly because native features are usually well-documented and mostly come with sample usage code snippets that can get us started. All you need to know is how to read the native programming language (i.e. Java/Kotlin or Objective C/Swift) and a basic understanding of the mobile platform to understand how the moving parts connect.

This article aims to bring the worlds of Hybrid and Android Native development together by building a Cordova Android plugin from existing Android native code.

You don’t have to be an Android or JavaScript expert to understand the article but as we go on I’ll assume you have some fundamental knowledge of the Android platform as well as some knowledge of JavaScript.

The Native Part

My first step was to create an Android native project to detect temperature. No UI interaction necessary, just get the temperature to a variable and that’s it. I came up with this, an Activity class that extends the SensorEventListener:

package droidada.com.temperaturesensor;
 
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;
 
public class TemperatureActivity extends AppCompatActivity implements SensorEventListener {
 
  private SensorManager sensorManager;
  private float temperature;
 
  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
  }
 
  @Override
  protected void onResume() {
      super.onResume();
      loadAmbientTemperature();
  }
 
  @Override
  protected void onPause() {
      super.onPause();
      unregisterAll();
  }
 
  @Override
  public void onSensorChanged(SensorEvent sensorEvent) {
      if (sensorEvent.values.length > 0) {
          temperature = sensorEvent.values[0];
      }
  }
 
  @Override
  public void onAccuracyChanged(Sensor sensor, int i) {
 
  }
 
  private void unregisterAll() {
      sensorManager.unregisterListener(this);
  }
 
  private void loadAmbientTemperature() {
      Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_AMBIENT_TEMPERATURE);
 
      if (sensor != null) {
          sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL);
      } else {
          Toast.makeText(this, "No Ambient Temperature Sensor !", Toast.LENGTH_LONG).show();
      }
  }
}

When the activity resumes, the sensor is initialized, the listener is registered, and then the onSensorChanged event listener listens to get temperature in order to update the temperature variable. In the onPause method, we unregister the listener when the activity is no longer being viewed. We don’t need the onAccuracyChanged listener on this occasion so I just left it blank.

The Cordova Plugin

I prefer to use the Plugman tool to create the project, so first we install Plugman and then use it to create a Cordova plugin.

npm install -g plugman
plugman create --name Temperature --plugin_id com-droidada-plugins-temperature --plugin_version 0.0.1 --path ./temperature

The above command should create a Cordova plugin for you called Temperature in the temperature directory on the path where you ran the command. The plugin is populated with sample code and uses the plugin name indicated in the command.

Your new Cordova plugin project should look like this:

Your new Cordova plugin project should look like this

Plugin.xml

The plugin.xml file contains your plugin configuration data such as
Plugin ID (which is used to reference your plugin when published), the name of your plugin, the location of your JavaScript plugin file, and the clobbers.

Clobbers are used to specify the windows variable in which to store your plugin name; in this case your plugin can be accessed by referencing cordova.plugins.[PluginName]

Here’s how it should look:

Plugin.xml Example

The src folder contains your iOS or Android native codebase – we’ll get into to that later.

Temperature.js

The www folder contains your JavaScript codebase which in turn contains your Temperature.js file. When loading the plugin from your codebase, the JavaScript plugin file is first called which in turn triggers the native codebase when necessary. It is possible to have a Cordova plugin that runs only JavaScript code; however, in our use case, this JavaScript code points to our native functionality. It should look like this:

var exec = require('cordova/exec');
 
exports.coolMethod = function (arg0, success, error) {
   exec(success, error, 'TemperaturePlugin', 'coolMethod', [arg0]);
};

This snippet exports the coolMethod and makes it accessible. The coolMethod in turn calls the exec function and passes arguments and call back functions to the native code base. The TemperaturePlugin argument is the name of the native service you will be executing and the coolMethod is the action you want performed in this service. Other user provided arguments are passed by the arg0 parameter.

Let’s tailor this class to what we need for our plugin:

var exec = require('cordova/exec');
 
exports.checkTemperature = function(success, error) {
   exec(success, error, 'TemperaturePlugin', 'checkTemperature');
};

Here I exposed the checkTemperature method and passed it as a string to use as an action in the TemperaturePlugin service. Since we don’t need user arguments, I omitted the arg0 argument.

Temperature.java

Now to convert our TemperatureActivity to a native Cordova Java Plugin class. I chose to create my new Temperature.Java file in the src/android/com/droidada/plugin directory. It’s a replication of the droidada.com.plugin package name I used for my Activity class, just like Android Studio does it.

Our class needs to extend the CordovaPlugin class as opposed to the AppCompactActivity, so we can remove AppCompactActivity and its reference. Next, we add references to add CordovaPlugin functionality:

import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaInterface;
import org.apache.cordova.CordovaWebView;

The CordovaPlugin class does not implement the onCreate activity lifecycle method, so feel free to delete it. Its onPause and onDestroy methods perform the same activity lifecycle roles. Next, we override the onReset function. It’s called when the webview does a top-level navigation or refreshes, and is recommended to stop long running processes like temperature detection. We still let the class implement SensorEventListener and leave the override methods as is.

The initialize Method

The initialize method is called when the plugin is initialized. It receives the CordovaInterface (which is used to initialize the Android context) and CordovaWebView as parameters during initialization. This is a good place to initialize services; hence, we initialize our SensorManager here and use it to initialize our temperature sensor.

   this.sensorManager = (SensorManager) cordova.getActivity().getSystemService(Context.SENSOR_SERVICE);
   this.sensor = this.sensorManager.getDefaultSensor(Sensor.TYPE_AMBIENT_TEMPERATURE);

The execute Method

The JavaScript plugin file triggers this method to initialize all actions. The method receives an action string and arguments which are used to perform tasks, as well as a callback context which returns success or failure callback to users.

Here we check for the action parameter we specified in the www/Temperature.js file and call a function to initialize the temperature sensor listener.

   if (action.equals("checkTemperature")) {
       this.loadAmbientTemperature();
       return true;
   }

Since our class still implements the SensorEventListener, we leave the interface functions as is but call the callbackContext.success methods with the detected temperature or response message. We could also call the callbackContext.error function when a device doesn’t have a sensor or if there was an error in the process.

Rounding up with Plugin.xml

Next we need to update our Plugin.xml file:

The android-package param tag indicates the name of the package where your Temperature.java file is located. The source file has the locations of the current Temperature.java file directory and the directory you want it copied into once installed on a device.

The Engine tag imports the Cordova library into the native project.

Testing it out

Next we create a new Ionic project, install our plugin and test it to see how it works.
If you don’t already have it installed, install the ionic cli globally:

npm install -g ionic

Create a new project:

ionic start testapp blank

Type “yes” when asked if you want to integrate the app with Cordova.

Navigate into the project and add the android platform

cd testapp
 
ionic cordova platform add android

When the platform has been added, run the following command:

ionic cordova plugin add <path/to/your/plugin>

This is where you troubleshoot if your plugin is properly configured; information for this is usually located in the Plugin.xml file.

Once your plugin has been installed correctly, you can reference it in your page using the location we specified in the clobbers tag of the Plugin.xml file.

Your code should look something like this:

const temp = cordova.plugins.Temperature;
temp.checkTemperature((val) => {
    console.log("This should be your temperature", val);
},
(err) => {
    console.log(err);
})

Conclusion

And there you have it folks – a temperature detection Cordova Android Plugin. This article contains a walkthrough on publishing your newly created plugin. I added some more logic to my Temperature.js file to help things run smoothly – here’s the source code. Next I’ll be writing on creating a Cordova iOS plugin.


This website uses cookies

These cookies are used to collect information about how you interact with our website and allow us to remember you. We use this information in order to improve and customize your browsing experience, and for analytics and metrics about our visitors both on this website and other media. To find out more about the cookies we use, see our Privacy Policy.

Please consent to the use of cookies before continuing to browse our site.

Like What You See?

Got any questions?


>