Jul 17, 2024

ATAK Plugins Part 1

ATAK Plugins Part 1

Learn to create and customize ATAK plugins, set up the development environment, build a Hello World example, and integrate MavSDK in this hands-on tutorial.

The Android Team Awareness Kit (ATAK) has revolutionized situational awareness and operational efficiency across various sectors. As a powerful mobile platform, ATAK provides real-time data sharing and collaboration capabilities that are essential for military operations, emergency response, and field operations. In this article, we’ll explore the world of ATAK plugins and how they extend the functionality of this versatile tool. Some basic knowledge of Android app development will be required.

This will be part 1 of a 2 part series. Feel free to follow along with the video from our latest meetup hosted by RIIS President Godfrey Nolan while reading through the tutorial.


What is ATAK?

ATAK, or the Android Team Awareness Kit, is an open-source situational awareness and communication tool designed for both military and civilian use. It offers a range of features including mapping, geospatial capabilities, and communication functionalities. The modular architecture of ATAK allows for customization and extension through plugins, making it adaptable to various operational scenarios.

Originally developed by the U.S. Air Force in the early 2000s, ATAK has evolved into a civilian version known as “ATAK-CIV,” which was released about 5-6 years ago. This open-source nature has led to a thriving ecosystem of developers and users who continually enhance and expand ATAK’s capabilities.


The Power of ATAK Plugins

Plugins are at the heart of ATAK’s extensibility. They allow users to add new features, customize the user interface, or integrate with external systems. This plugin architecture is one of the most appealing aspects of ATAK, as it enables users to tailor the application to their specific needs.

Some key benefits of ATAK plugins include:

  1. Customization: Plugins can be developed to meet the unique requirements of different organizations or operational contexts.

  2. Integration: ATAK can be integrated with various external systems and data sources through plugins.

  3. Enhanced functionality: Plugins can add new tools and capabilities to the base ATAK application.


Core Features

  • Mapping and Geospatial Capabilities: ATAK supports both online and offline mapping with high-resolution imagery and fast rendering. It can handle various standard formats and allows for collaborative mapping, including points, drawings, and locations of interest.

  • Communication: The platform facilitates real-time data sharing, including chat, file sharing, photo sharing, video sharing, and streaming. It also supports radio controls and integration, as well as Voice over IP (VoIP) capabilities.

  • Navigation: ATAK provides tools for navigation, including walking, hiking, driving, and air-ground coordination. It also offers elevation tools, heat maps, computed contour maps, and dynamic profiling.

  • Situational Awareness: Features like precision targeting, surrounding land formation intelligence, and team emergency beacons enhance situational awareness. Users can mark, share, and track locations, and use network-aware geofences with triggers.

  • Customization and Extensibility: The modular architecture of ATAK allows for extensive customization through plugins, enabling users to tailor the application to specific operational needs


ATAK Architecture

The Android Team Awareness Kit (ATAK) and its related variants (WinTAK for Windows and iTAK for iOS) are part of a larger TAK ecosystem designed for situational awareness and communication. Here's an overview of the ATAK architecture and how it interacts with other TAK variants:

A common setup would potentially include setting up your own network utilizing something like goTenna. Devices are then able to share data between each other on the network regardless of their TAK variant.


Getting Started with ATAK Plugin Development

Before we get started, you should know, that you don’t need a tak.gov login to get started, but there will be additional benefits if you have one.

If you want to install any existing plugins such as the UAS Plugin Tool for flying drones, you first need to download and install the base ATAK-CIV (Civil Use) application from Google Play. This is the core ATAK app that you need before installing any plugins. You can then install any of the 30 or so plugins on Google Play or tak.gov which are then loaded into the core ATAK-CIV.

However, if you want to create your own plugin, then we don’t need ATAK-CIV just yet. We’ll talk about how to deploy our own plugins in Part 2 of this blog.


The Hello World Example Plugin

Let’s take a look at some of the sample plugins to get us started. The Hello World example is not what I’d expect from its name. It’s a comprehensive plugin that shows many of the capabilities of ATAK plugins. The second example plugin the plugintemplate is more of a traditional HelloWorld example.

Download the ATAK SDK from the official GitHub repository. Unzip the SDK and create a “plugins” directory within it. Within the SDK folder from the version you’ve downloaded navigate to atak-civ/plugins or plugin-templates folder. Copy the HelloWorld example in “plugin-examples” into the newly created “plugins” folder.

Click on the helloworld file within it.

It would be nice if you could just launch this and be ready to go, but there’s a couple of things we need to do to get Android Studio to behave and not harass us with error messages.

  1. Go into the local.properties file add the SDK directory and any other information seen in the example below that you are missing:

sdk.dir=c\:\\Users\\admin\\AppData\\Local\\Android\\Sdk
takDebugKeyFile=<ABSOLUTE_PLUGIN_PATH>\\debug.keystore
takDebugKeyFilePassword=android
takDebugAlias=androiddebugkey
takDebugKeyPassword=android

takReleaseKeyFile=<ABSOLUTE_PLUGIN_PATH>\\release.keystore
takReleaseKeyFilePassword=android
takReleaseAlias=androidreleasekey
takDebugKeyPassword=android
  1. Create debug and release keystores using the keytool command.

  1. For a debug keystore

keytool -genkey -v -keystore debug.keystore -storepass android -alias androiddebugkey -keypass android -keyalg RSA -keysize 2048 -validity 10000

b. Fore a release keystore

keytool -genkey -v -keystore my-release-key.keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 10000
  1. Change Gradle plugin to 4.2.2 and the Gradle version to 6.9.1

  1. Update the Gradle version by going to Settings>Build, Execution, Deployment>Build Tools>Gradle to Gradle JDK: 11

  1. Change the build.gradle file at the app level to the following:

buildscript {
		ext.PLUGIN_VERSION = "1.0"
		ext.ATAK_VERSION = "4.6.0"
		
		def takdevVersion : String = '2.+'
		
		ext.getValueFromPropertiesFile = { propFile, key ->
				if(!propFile.isFile() || !propFile.canRead())
						return null
				def prop = new Properties()
				def reader = propFile.newReader()
				try {
						prop.load(reader)
				} finally {
						reader.close()
				}
				return prop.get(key

  1. Edit the app configuration to work with the plugin. Since plugins don’t launch as standalone apps, you’ll need to modify the configuration to prevent Android Studio from attempting to launch an activity.

  1. Install the ATAK APK from the SDK, not the one from Google Play. This ensures compatibility with your development environment.

  1. Change the build variants and change :app to civDebug

When you try to run the app, most Android devices will try and prevent you from running applications not downloaded from Google Play, so you will probably see the following message.

Click on more details and then Install Anyway.

Okay, if all goes well, you should see the following app interface load:

Wow, that’s quite the big Hello World example, but it’s kind of nice because it gives you a great overview of the stock capabilities within the the framework. Be sure to have a peek around and get familiar with all of the options.

You can see the work that goes into this beautifully retro layout in the AndroidTacticalAssaultKit-CIV/plugin-examples/helloworld/app/src/main/res/layout/hello_world_layout.xml file.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:orientation="vertical" >

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="3dp"
        android:layout_marginRight="3dp"
        android:fadeScrollbars="false">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <TextView
                android:id="@+id/textView"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Hello, World"
                android:textColor="@android:color/white"
                android:textSize="20sp" />

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="1dp"
                android:layout_margin="3dp"
                android:background="@android:color/white">

            </RelativeLayout>

            <TextView
                android:id="@+id/longPressTextView"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Long press a button for a brief descripton"
                android:textColor="@android:color/white"
                android:textSize="15sp" />

            <TextView
                android:id="@+id/clickTextView"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Tap a button for a demonstration"
                android:textColor="@android:color/white"
                android:textSize="15sp" />

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="1dp"
                android:layout_margin="3dp"
                android:background="@android:color/white">

            </RelativeLayout>

            <TextView
                android:id="@+id/layoutExamples"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Layout Examples"
                android:textColor="@android:color/white"
                android:textSize="18sp" />

            <Button
                android:id="@+id/largerButton"
                style="@style/darkButton"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:padding="6dp"
                android:text="Larger"

We stopped at the largerButton button because you can sort of get the idea of where this goes.

Each of those buttons need to be attached to a specific activity for them to do anything. You can see where this is happening in the plugin-examples/helloworld/app/src/main/java/com/atakmap/android/helloworld/HelloWorldDropDownReceiver.java file. For each of these buttons there’s going to be a listener and a function like this one below for the largerButton:

        final Button larger = helloView
                .findViewById(R.id.largerButton);
        larger.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                resize(FULL_WIDTH, HALF_HEIGHT);
            }
        });

This is pretty simple. It just says listen for a button click and when you see one resize to full width and half height.

Feel free to poke around and investigate to learn more about ATAK capabilities. The code in the Hello World app includes lots of helpful comments to point you in the right direction.


Creating Your Second ATAK Plugin: Integrating MavSDK

After successfully creating the “Hello World” plugin, let’s move on to a much simpler example that integrates drone functionality using MavSDK. This plugin builds on the pluginTemplate example and will demonstrate how to connect to a MavSDK server and display real-time position data from a drone.

Before we get started, if you are unfamiliar with the MAVSDK, check out our tutorial from last month’s meetup MAVSDK on Android. We will only briefly cover the MAVSDK-specific steps in this tutorial.

Okay, navigate back to the plugins or plugin-templates folder and look for the plugintemplate file. This file functions more like a traditional Hello World example as it is the barebones you will need to build your own. Refer to steps 1 through 8 of the Hello World section for the basic setup. The main files we are going to be concerned with going forward are PluginTemplateDropDownReceiver.java, PluginTemplateMapComponent.java and main_layout.xml .


Creating the User Interface

To keep things simple for our first MAVSDK plugin, we are going to focus on displaying the latitude and longitude of our drone. First let’s set up a simple layout in our main_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:fadeScrollbars="false"
    android:layout_marginLeft="3dp"
    android:layout_marginRight="3dp">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/app_name" />
            
        <TextView 
		        android:id="@+id/positionTextView"
		        android:layout_width="wrap_content"
		        android:layout_height="wrap_content"
		        android:text="@string/position_lat_long" />


Integrating MavSDK

Modern Android applications are rarely coded in Java anymore. The good news is you can easily convert your Java code to Kotlin within Android Studio by going to Code > Convert Java File to Kotlin File. This is great, because we can easily transmute the code in PluginTemplateDropDownReceiver.java into a PluginTemplateDropDownReceiver.kt file.

package com.atakmap.android.plugintemplate

import android.content.Context
import android.content.Intent
import android.view.View
import com.atakmap.coremap.maps.coords.GeoPoint
import com.atak.plugins.impl.PluginLayoutInflater
import com.atakmap.android.maps.MapView
import com.atakmap.android.plugintemplate.plugin.R
import com.atakmap.android.dropdown.DropDown
import com.atakmap.android.dropdown.DropDownReceiver
import com.atakmap.coremap.log.Log

class PluginTemplateDropDownReceiver(
    mapView: MapView,
    private val context: Context
) : DropDownReceiver(mapView), DropDown.OnStateListener {

    companion object {
        private const val TAG = "PluginTemplateDropDownReceiver"
        const val SHOW_PLUGIN = "com.atakmap.android.plugintemplate.SHOW_PLUGIN"
    }

    private val templateView: View = PluginLayoutInflater.inflate(context, R.layout.main_layout, null)

    override fun disposeImpl() {}

    override fun onReceive(context: Context, intent: Intent) {
        val action = intent.action ?: return
        if (action == SHOW_PLUGIN) {
            Log.d(TAG, "showing plugin drop down")
            showDropDown(templateView, HALF_WIDTH, FULL_HEIGHT, FULL_WIDTH, HALF_HEIGHT, false, this)
        }
    }

    override fun onDropDownSelectionRemoved() {}

    override fun onDropDownVisible(v: Boolean) {}

    override fun onDropDownSizeChanged(width: Double, height: Double) {}

    override fun onDropDownClose() {}
}

We are going to add the following code between our private val templateView and our public override fun disposeImpl() to inject our position coordinates into the layout.

private val positionTextView: TextView = templateView.findViewById(R.id.positionTextView)

init {
		CouroutineScope(Dispatchers.IO).launch {this: CoroutineScope
				position.collet {pair ->
						val newPositionString = "Position: (${pair.first}, ${pair.second}"
						withContext(Dispatchers.Main) {this: CoroutineScope
								positionTextView.text = newPositionString
						}
				}
		}
}

Now we can do the same conversion to our PluginTemplateMapComponent.java file.

package com.atakmap.android.plugintemplate

import android.content.Context
import android.content.Intent
import com.atakmap.android.ipc.AtakBroadcast.DocumentedIntentFilter
import com.atakmap.android.maps.MapView
import com.atakmap.android.dropdown.DropDownMapComponent
import com.atakmap.coremap.log.Log
import com.atakmap.android.plugintemplate.plugin.R

class PluginTemplateMapComponent : DropDownMapComponent() {
    private companion object {
        private const val TAG = "PluginTemplateMapComponent"
    }

    private lateinit var pluginContext: Context
    private lateinit var ddr: PluginTemplateDropDownReceiver
   

    override fun onCreate(context: Context, intent: Intent, view: MapView) {
        context.setTheme(R.style.ATAKPluginTheme)
        super.onCreate(context, intent, view)
        pluginContext = context

        ddr = PluginTemplateDropDownReceiver(view, context)

        Log.d(TAG, "registering the plugin filter")
        val ddFilter = DocumentedIntentFilter()
        ddFilter.addAction(PluginTemplateDropDownReceiver.SHOW_PLUGIN)
        registerDropDownReceiver(ddr, ddFilter)
    }

    override fun onDestroyImpl(context: Context, view: MapView) {
        super.onDestroyImpl(context, view)
    }
}

First, let’s add a few variables for our position data just under private var ddr:

private val _position = MutableStateFlow(Pair(0.toDouble(), 0.toDouble()))
private val position : StateFlow<Pair<Double, Double> = _position

Then we need to add position to the ddr variable within the override fun onCreate()

ddr = PluginTemplateDropDownReceiver(view, context, position)

Finally within the override fun onCreate() we want to add the following at the very end.

if (!isMavsdkServerRunning) {
		runMavSDKServer()
}


Testing the Plugin

Here’s one last reminder to check out last month’s MAVSDK on Android tutorial for how setup the dependencies and SITL test.

To test the plugin, you’ll need to run a PX4 simulator. You can use a Docker container we used in the above tutorial to start a headless Gazebo PX4 simulator.

Once the simulator is running, compile and deploy your plugin to ATAK. You should see the drone’s position updating in real-time in the logcat output.

Conclusion

In this tutorial, we’ve explored the process of creating ATAK plugins, from a not so simple “Hello World” example as well as MavSDK integration for drone control. We’ve covered the basics of setting up the development environment, creating layouts, and integrating with external systems like MavSDK.

ATAK plugins provide a powerful way to extend the functionality of the ATAK platform, enabling custom solutions for various operational scenarios. By leveraging the plugin architecture, developers can create tailored tools that enhance situational awareness and operational efficiency.

As you continue to develop ATAK plugins, consider exploring more advanced features such as custom map overlays, sensor integrations, or specialized communication modules using the HelloWorld example as a guide. The ATAK community and resources mentioned earlier can provide valuable support and inspiration for your future projects.

Remember to always test your plugins thoroughly and consider the security implications when working with sensitive data or in operational environments. Happy coding, and enjoy building powerful situational awareness tools with ATAK!


Additional Resources


Stay tuned for Part 2 and be sure to join us at our next Drone Software Developer Meetup for more great walkthroughs like this one!