How To Use Android Plugins in Unity Apps

This article provides an example of how to integrate a custom Android plugin (implemented natively in Java) into a Vuforia Unity project.

We’ll consider a simple Unity-based Vuforia Engine application that:

  • can detect and track an Image Target, from a Device Database
  • displays a popup message on the screen for a few seconds when a target is detected; the message will contain some basic information about the detected target (e.g. target name and size)

The skeleton of the App will be developed in Unity, using the Vuforia Unity Extension, while the specific function that displays the popup message will be implemented natively in Java, using the Android SDK.

The native Android/Java code will be packaged in a plugin library (a JAR) that will be added to the Unity project assets, so that the Unity application code will be able to access the plugin functions.

Creating a Vuforia Unity project

  • Create a new Unity project and import the Vuforia Extension package (vuforia-unity-x-y-z.unitypackage)
  • Add an ARCamera to your scene from Assets/Vuforia/Prefabs
  • Enter your License Key (Mobile) into the ARCamera inspector license field
  • Remove any previously created camera (e.g. MainCamera created by Unity)
  • Add an ImageTarget to your scene from Assets/Vuforia/Prefabs
  • Import a Device Database into your scene
  • Select Load Dataset and Activate Dataset in the ARCamera inspector
  • Select the desired dataset target for your Image Target
  • Add a simple cube (or other simple game object) as a child of your Image Target
  • Set the Unity Player Settings for Android, as recommended for Vuforia Engine (e.g. Graphics API = OpenGL ES 2.0, Device Filter = armv7)
  • Build and run once (on a connected Android device) to verify that the App works and that the Image Target can be detected and tracked

Creating your Android plugin

  • Using Android Studio or Eclipse, create a new Android project (make sure to create a basic Android project with an empty or blank Activity)
  • Take note the Java package name, and adjust it if needed, so that it matches your desired package name for the plugin (e.g. com.vuforia.android.pluginexample )
  • Add a Java class called ImageTargetLogger to the package
  • Add the following code to the class:
package com.vuforia.android.pluginexample;
    
  import android.app.Activity;
  import android.util.Log;
  import android.widget.Toast;
    
  import java.util.Locale;
    
  public class ImageTargetLogger
  {
      protected Activity mCurrentActivity;
    
      public void setActivity(Activity activity)
      {
          mCurrentActivity = activity;
      }
    
      /**
       * Displays a Toast message overlaid on screen with Target info
       */
      public void showTargetInfo(String targetName, float targetWidth, float targetHeight)
      {
          Log.d("MyPlugin", "Executing showTargetInfo native Android code");
    
          if (mCurrentActivity == null) {
              Log.e("MyPlugin", "Android Activity not set in plugin");
              return;
          }
    
          final String msg = String.format(Locale.ENGLISH,
                  "Image Target %s - size: %5.2f x %5.2f",
                  targetName, targetWidth, targetHeight);
    
          Log.d("MyPlugin", "Target Info: " + msg);
    
          mCurrentActivity.runOnUiThread(new Runnable() {
              public void run() {
                  Toast.makeText(mCurrentActivity, msg, Toast.LENGTH_LONG).show();
              }
          });
      }
  }
  • in the code above, note the 2 methods setActivity() and showTargetInfo() :

    • The setActivity() method is meant to be called by the Unity backend for setting the current Android Activity used in the Unity app, so that the Java plugin code can have access to that Activity

    • The showTargetInfo() method is also meant to be called by the Unity app, when a target is detected, so to pass the target information (name, width, height) to the Java plugin function, which will in turn display those information in a Toast message

     

  • Build the Android app

Packaging the Android plugin code in a JAR

  • In Eclipse, you can easily achieve this by opening the project Properties, selecting the Android tab and checking the IsLibrary flag; this should create a JAR file in the bin folder of your Eclipse project

  • In Android Studio, once you build the App, you ll find the compiled classes under app/build/intermediates/classes/debug ; simply create a ZIP archive of the content under the debug folder and then rename the .zip extension to .jar

Importing the Android plugin into the Unity project

  • Copy the JAR file (e.g. myplugin.jar ) to the Assets/Plugins/Android/ folder under your (previously created) Vuforia Unity project

Using the plugin from Unity

  • Open your Unity project and create a C# script called ImageTargetInfo.cs
  • Attach the script to the ImageTarget in your scene
  • The ImageTargetInfo script will handle target detected events and call the Android plugin native methods to show the target information in response to those events
  • This is the script code to achieve this:
    using UnityEngine;
    using Vuforia;
      
    public class TrackableInfo : MonoBehaviour, ITrackableEventHandler {
        private TrackableBehaviour mTrackableBehaviour;
         
        void Start() {
            mTrackableBehaviour = GetComponent<TrackableBehaviour>();
            if (mTrackableBehaviour) {
                mTrackableBehaviour.RegisterTrackableEventHandler(this);
            }
        }
      
        public void OnTrackableStateChanged(TrackableBehaviour.Status previousStatus,
                                            TrackableBehaviour.Status newStatus) {
            if (newStatus == TrackableBehaviour.Status.DETECTED ||
                newStatus == TrackableBehaviour.Status.TRACKED ||
                newStatus == TrackableBehaviour.Status.EXTENDED_TRACKED) {
                OnTrackingFound();
            }
        }
      
        private void OnTrackingFound() {
            if (mTrackableBehaviour is ImageTargetAbstractBehaviour) {
                ImageTargetAbstractBehaviour itb = mTrackableBehaviour as ImageTargetAbstractBehaviour;
                ImageTarget it = itb.ImageTarget;
                SetActivityInNativePlugin();
                ShowTargetInfo(it.Name, it.GetSize().x, it.GetSize().y);
            }
        }
      
    #if UNITY_ANDROID
        private AndroidJavaObject javaObj = null;
      
        private AndroidJavaObject GetJavaObject() {
            if (javaObj == null) {
                javaObj = new AndroidJavaObject("com.vuforia.android.pluginexample.ImageTargetLogger");
            }
            return javaObj;
        }
      
        private void SetActivityInNativePlugin() {
            // Retrieve current Android Activity from the Unity Player
            AndroidJavaClass jclass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
            AndroidJavaObject activity = jclass.GetStatic<AndroidJavaObject>("currentActivity");
      
            // Pass reference to the current Activity into the native plugin,
            // using the 'setActivity' method that we defined in the ImageTargetLogger Java class
            GetJavaObject().Call("setActivity", activity);
        }
      
        private void ShowTargetInfo(string targetName, float targetWidth, float targetHeight) {
            GetJavaObject().Call("showTargetInfo", targetName, targetWidth, targetHeight);
        }
    #else
        private void ShowTargetInfo(string targetName, float targetWidth, float targetHeight) {
            Debug.Log("ShowTargetInfo method placeholder for Play Mode (not running on Android device)");
        }
    #endif
    }