Skip to main content
Version: 14 Oct 2024

Callback via AndroidJavaProxy

Overview

In this guide, we will outline how to utilize Unity's AndroidJavaProxy object for the ability to receive callbacks from our Android Plug-in. This class can be used to implement any java interface. Any java vm method invocation matching the interface on the proxy object will automatically be passed to the C# implementation.

Create a callback in Android Studio

The first thing we need to do is create a callback that we can use.

  1. Right click the folder that contains AccelerometerListener.java, select New->Java Class. Select Interface and call it SensorDataCallback
  2. Inside this file, define a function called onSensorData with 2 parameters, String sensorData, long timestamp
package com.magicleap.accelerometerlibrary;

public interface SensorDataCallback {
void onSensorData(String sensorData, long timestamp);
}

Using the callback in our Android Plug-in

Now that we have a callback, we need to use it inside of our plug-in. We will do this by calling callback.onSensorData when a callback has been assigned inside of the onSensorChanged function.

  1. First, let's modify the startSensorListening function to take a new parameter, SensorDataCallback sensorDataCallback and assign it to a variable for later use, SensorDataCallback callback
    private SensorDataCallback callback = null;
public void startSensorListening(SensorDataCallback sensorDataCallback)
{
//initialize sensorManager
sensorManager = (SensorManager) unityActivity.getSystemService(Context.SENSOR_SERVICE);
//use sensorManager to obtain the devices default accelerometer
accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
//register this sensor to begin listening and receiving SensorEventListener callbacks
sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
//assign callback
callback = sensorDataCallback;
}
  1. Now, let's modify the onSensorChanged function to call callback.onSensorData and pass the data and timestamp with it.
    @Override
public void onSensorChanged(SensorEvent event)
{
if(callback != null)
{
long timestamp = event.timestamp;
String sensorData = Arrays.toString(event.values);
callback.onSensorData(sensorData, timestamp);
}
}

Your plug-in class should now look like this:

package com.magicleap.accelerometerlibrary;

import android.app.Activity;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import java.util.Arrays;

public class AccelerometerListener implements SensorEventListener
{
private static Activity unityActivity;
private SensorManager sensorManager;
private Sensor accelerometer;
private SensorDataCallback callback = null;

public static void setUnityActivity(Activity _activity)
{
unityActivity = _activity;
}

@Override
public void onSensorChanged(SensorEvent event)
{
if(callback != null)
{
long timestamp = event.timestamp;
String sensorData = Arrays.toString(event.values);
callback.onSensorData(sensorData, timestamp);
}
}

@Override
public void onAccuracyChanged(Sensor sensor, int accuracy)
{

}

public void startSensorListening(SensorDataCallback sensorDataCallback)
{
//initialize sensorManager
sensorManager = (SensorManager) unityActivity.getSystemService(Context.SENSOR_SERVICE);
//use sensorManager to obtain the devices default accelerometer
accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
//register this sensor to begin listening and receiving SensorEventListener callbacks
sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
callback = sensorDataCallback;
}
}

Build a new Android Archive file

Now that the plug-in updates are finished, go ahead and build a new .aar file. We will need to paste this into our Unity project.

  1. In the Android Studio toolbar, navigate to Build->Make Module 'ExampleSensorPlugin.AccelerometerLibrary'.
  2. In a file explorer, navigate to
    ExampleSensorPlugin\AccelerometerLibrary\build\outputs\aar\AccelerometerLibrary-debug.aar
    Copy this file!
  3. Paste this file inside your Unity project at Assets\Plugins\Android. If you already have an AccelerometerLibrary-debug file at this location, delete it before you paste the new version.

Create AndroidJavaProxy object in Unity

Now that we have our callback setup in the Android plug-in, it's time to move over to the Unity side to begin using it!

  1. Create a new C# script called SensorDataCallbackProxy. This script will Inherit from AndroidJavaProxy.
public class SensorDataCallbackProxy : AndroidJavaProxy
{

}
  1. Add a constructor that calls the base constructor from AndroidJavaProxy. As a parameter, we will need to pass our plugin-in packagename along with the name of the callback script we made in our plug-in. For this example, the full string is com.magicleap.accelerometerlibrary.SensorDataCallback
public class SensorDataCallbackProxy : AndroidJavaProxy
{
public SensorDataCallbackProxy() : base("com.magicleap.accelerometerlibrary.SensorDataCallback")
{

}
}
  1. Finally, we need to add a function with the same signature as the one from our Android plug-in SensorDataCallback script. You can just copy the function signature from that file if you like. Inside this function, we will just log the sensorData and timestamp to the console.
public void onSensorData(string sensorDataString, long timestamp)
{
Debug.Log($"Sensor Data: {sensorDataString} at time: {timestamp}");
}

The full SensorDataCallbackProxy script should look like this:

using UnityEngine;

public class SensorDataCallbackProxy : AndroidJavaProxy
{
public SensorDataCallbackProxy() : base("com.magicleap.accelerometerlibrary.SensorDataCallback") { }

public void onSensorData(string sensorDataString, long timestamp)
{
Debug.Log($"Sensor Data: {sensorDataString} at time: {timestamp}");
}
}

Using AndroidJavaProxy object as a callback

The last step here will be modifying our AccelerometerPluginManager script to accomodate for the new parameter we added to startSensorListening.

  1. Inside of the AccelerometerPluginManager script, let's add a new variable to store our callback SensorDataCallbackProxy callbackProxy.
  2. Now, in the start function, locate the section where we call startSensorListening. Inside this function, let's create a new callbackProxy object like this: callbackProxy = new SensorDataCallbackProxy();
  3. Next, we will need to pass the callbackProxy variable we just created above to startSensorListening. The final script should look like this:
using UnityEngine;

public class AccelerometerPluginManager : MonoBehaviour
{
private AndroidJavaClass unityClass;
private AndroidJavaObject unityActivity;
private AndroidJavaObject pluginInstance;
private SensorDataCallbackProxy callbackProxy;

// Start is called before the first frame update
void Start()
{
unityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
unityActivity = unityClass.GetStatic<AndroidJavaObject>("currentActivity");
pluginInstance = new AndroidJavaObject("com.magicleap.accelerometerlibrary.AccelerometerListener");

if (pluginInstance != null)
{
Debug.Log("Plugin Instance Loaded!");
pluginInstance.CallStatic("setUnityActivity", unityActivity);
// Create the callback proxy
callbackProxy = new SensorDataCallbackProxy();
pluginInstance.Call("startSensorListening", callbackProxy);
}
}
}

Build and Run the project

That's it! Now you can build and run this Unity project on your ML2 device!
You will see the sensor values being logged to the console when the app is running.