Files
unicorn-utterances/content/blog/integrating-android-code-in-unity/index.md
2019-12-28 01:55:25 -08:00

4.8 KiB

Call Android code from C#

For example, take the following library: https://github.com/jaredrummler/AndroidDeviceNames

You must make your callback extend the type of callback that is used in the library. For example, take the following code sample from the README of the aformentioned library:

DeviceName.with(context).request(new DeviceName.Callback() {
  @Override public void onFinished(DeviceName.DeviceInfo info, Exception error) {
    String manufacturer = info.manufacturer;  // "Samsung"
    String name = info.marketName;            // "Galaxy S8+"
    String model = info.model;                // "SM-G955W"
    String codename = info.codename;          // "dream2qltecan"
    String deviceName = info.getName();       // "Galaxy S8+"
    // FYI: We are on the UI thread.
  }
});

While this example may seem straightforward, let's disect what we're doing step-by-step here. This will allow us to make the migration to C# code much simpler to do mentally.

// Create a new "DeviceName.Callback" instance
DeviceName.Callback handleOnFinished = new DeviceName.Callback() {
  // Provide an implementation of the `onFinished` function in the `Callback` class
  // Notice that there are two parameters for this method: one for info, the other for errors
  @Override public void onFinished(DeviceName.DeviceInfo info, Exception error) {
    // ... Assignment logic here
  }
};

// Pass the `DeviceName.Callback` instance to the `DeviceName.with` in order to get a request instance
DeviceName.Request withInstance = DeviceName.with(context);

// Use that request instance to call the `onFinished` callback from above to run the related code
withInstance.request(handleOnFinished);

You can see that we have a few steps here:

  1. Make a new Callback instance
  2. Provide an implementation of onFinished for said instance
  3. Call DeviceName.with to create a request we can use later
  4. Call that request's request method with the Callback instance

For each of these steps, we need to have a mapping from the Java code to C# code. Let's walk through these steps one-by-one

Create Callback Instance

In order to create an instance of a Callback in C# code, we first need a C# class that maps to the Java interface. To do so, let's start by extending the Android library interface. We can do this by using the base function and the name of the Java package path. You're able to use $ to refer to the interface name from within the Java package.

private class DeviceCallback : AndroidJavaProxy
{
  // $ refers to interface name
  public DeviceCallback() : base("com.jaredrummler.android.device.DeviceName$Callback") {}
}

This package path can be found in the library's code at the following path. The DeviceName is referring to the path of the .java file name.

We can then provide an implementation of the onFinished method of that Callback. Recall how we previously had two params? Well, now the implementation will require we use the AndroidJavaObject type for both of those params.

Otherwise — if we type the function with a C# interface or class that matches the Java implementation — the method will not be called when we expect it to. This is due to function overloading expecting to get the AndroidJavaObject from the code Unity has developed to call mapped functions and classes.

This AndroidJavaObject type has a myriad of methods that can be called to assist in gathering data from or interfacing with from the Java object. One of such methods is the Get method. When called on an AndroidJavaObject instance in C#, it will allow you to grab a value from Java. Likewise, if you intend to call a method from the Java code, you can use AndroidJavaObject.Call.

private class DeviceCallback : AndroidJavaProxy
{
  public DeviceCallback() : base("com.jaredrummler.android.device.DeviceName$Callback") {}
  // These both MUST be `AndroidJavaObject`s. If not, it won't match the Java method type and therefore won't be called
  void onFinished(AndroidJavaObject info, AndroidJavaObject err)
  {
    // When running `AndroidJavaObject` methods, you need to provide a type for the value to be assigned to
    string manufacturer = info.Get<string>("manufacturer"); // "Samsung"
    string readableName = info.Get<string>("marketName"); // "Galaxy S8+"
    string model = info.Get<string>("model"); // "SM-G955W"
    string codename = info.Get<string>("codename"); // "dream2qltecan"
    string deviceName = info.Call<string>("getName"); // "Galaxy S8+"
  }
}