Published Sunday 18th November 2018
We picked up a new client project recently. Without going into too much detail, in a nutshell the brief is to develop a mobile app that incorporates a measurement tool, and will eventually need releasing on both Android and iOS. Weighing up the pros and cons of building such an app in the various cross-platform frameworks available, I opted for Corona SDK. I love Corona - it's built around the Lua programming language which I find really intuitive and quick to develop in, and although it's prodominantly a game programming kit, there's no reason why general apps can't be built with it too.
For the measurement tool we need users to be able to see what they're pointing their phone at, and to do this we want to show a camera preview in the background. Corona has a really nifty feature for this - you can simply create a standard rectangular display object and fill it with the camera preview. I'd planned this part of the app around this ability, but it turns out this only works on iOS, and we need Android support first...
The Android API does offer camera device access at this level, so with native Android code (Java) it's possible to receive a camera preview image to a surface object. One really cool feature of Corona is the ability to bridge between Lua code and native device code, by creating native plugins. On Android, a native plugin is a Java library that you load in to your Corona project during the compile, opening up its methods to your Lua code. As far as I understand it, when you build a Corona project, Lua is compiled into native Java when building for Android, or native C when building for iOS, so native plugins are simply pre-compiled Java or C classes that get added in during this phase, which is pretty logical really.
Since Corona doesn't offer a native camera preview method for Android, I decided to learn native plugin development. This proved to be a difficult process as there aren't any concise tutorials available, and the documentation and forum posts I managed to find were exceptionally brief and/or out of date, making references to things like Corona Native which is a tool for Mac users only, or Corona Enterprise which is now discontinued. Put simply, I struggled to figure out a lot of the steps needed or how to work with Android Studio, despite the fact that creating a native plugin is actually pretty easy.
So, if like me, you're trying to figure out how to build a native Android Java plugin for Corona SDK, here's a proper, concise tutorial concatenating the various steps I found throughout the documentation, my own findings, and some much appreciated guidance from Corona staff member, Vlad. This tutorial assumes that you're using Windows and building for Android. The steps for Mac users building for iOS differ.
new start(),
new stop(),
new status()
package plugin.myFirstPlugin;
import com.naef.jnlua.LuaState;
import com.naef.jnlua.NamedJavaFunction;
import com.ansca.corona.CoronaLua;
import com.ansca.corona.CoronaRuntime;
import com.ansca.corona.CoronaRuntimeListener;
import com.ansca.corona.CoronaRuntimeTask;
import com.ansca.corona.CoronaRuntimeTaskDispatcher;
import com.ansca.corona.storage.FileContentProvider;
import com.ansca.corona.storage.FileServices;
public class start implements com.naef.jnlua.NamedJavaFunction {
// This reports a class name back to Lua during the initiation phase.
@Override
public String getName() {
return "start";
}
// This is what actually gets invoked by the Lua call
@Override
public int invoke(final LuaState luaState) {
shared.status = "running";
return 1;
}
}
package plugin.myFirstPlugin;
import com.naef.jnlua.LuaState;
import com.naef.jnlua.NamedJavaFunction;
import com.ansca.corona.CoronaLua;
import com.ansca.corona.CoronaRuntime;
import com.ansca.corona.CoronaRuntimeListener;
import com.ansca.corona.CoronaRuntimeTask;
import com.ansca.corona.CoronaRuntimeTaskDispatcher;
import com.ansca.corona.storage.FileContentProvider;
import com.ansca.corona.storage.FileServices;
public class stop implements com.naef.jnlua.NamedJavaFunction {
// This reports a class name back to Lua during the initiation phase.
@Override
public String getName() {
return "stop";
}
// This is what actually gets invoked by the Lua call
@Override
public int invoke(final LuaState luaState) {
shared.status = "waiting";
return 1;
}
}
package plugin.myFirstPlugin;
import com.naef.jnlua.LuaState;
import com.naef.jnlua.NamedJavaFunction;
import com.ansca.corona.CoronaLua;
import com.ansca.corona.CoronaRuntime;
import com.ansca.corona.CoronaRuntimeListener;
import com.ansca.corona.CoronaRuntimeTask;
import com.ansca.corona.CoronaRuntimeTaskDispatcher;
import com.ansca.corona.storage.FileContentProvider;
import com.ansca.corona.storage.FileServices;
public class status implements com.naef.jnlua.NamedJavaFunction {
// This reports a class name back to Lua during the initiation phase.
@Override
public String getName() {
return "status";
}
// This is what actually gets invoked by the Lua call
@Override
public int invoke(final LuaState luaState) {
luaState.pushString(shared.status);
return 1;
}
}
package plugin.myFirstPlugin;
public class shared {
// global variables, basically
public static String status = "waiting";
}
At this point, we have a Java plugin consisting of start(), stop(), and status() methods which effectively just update a shared variable when start() or stop() are called, and pass that variable back when status() is called. The red entries throughout should now all be resolved and your first Java plugin is effectively complete. We still need to package it up and drop it into a Lua project though.
local library = require "plugin.myFirstPlugin"
print("Status is currently: " .. library.status())
print("Calling our start() function!")
library.start()
print("Status is currently: " .. library.status())
print("Calling our stop() function!")
library.stop()
print("Status is currently: " .. library.status())
print("Done!")
That's it, you're done!
Blog posts are written by individuals and do not necessarily depict the opinions or beliefs of QWeb Ltd or its current employees. Any information provided here might be biased or subjective, and might become out of date.
Ric, Saturday 14th December 2019 20:31
Since writing this article, Corona have changed from the Gradle build system, to Gradle KTS so some things are slightly different now. Most notably, there's no longer an exportPlugin.jar task so compiling your plugin for release is a slightly different process.
Now, from the Gradle tab on the right, unfold "Corona Native Android -> Plugin -> Tasks -> build", and double click "assemble". Once completed, open a file browser and within your project folder, look for \android\plugin\build\outputs\aar where you should find the plugin-release.aar mentioned above.
claudio, Sunday 16th February 2020 16:54
Hi Ric, i am not able to find the project files
Thank you in advance for help
Ric, Sunday 16th February 2020 20:04
On Windows you should find the referenced "\Native\Project Template\App" folder within "C:\Program Files (x86)\Corona Labs\Corona".
Spencer, Monday 20th April 2020 16:41
I had error messages regarding com.ansca.corona.storage.FileContentProvider missing when running on a device.
Using the Gradle Tab on the right I ran scripts such as App/Corona/setUpCoronaAppAndPlugins , android/androidDependencies etc and that fixed missing dependencies
Your email address is used to notify you of new comments to this thread, and also to pull your Gravatar image. Your name, email address, and message are stored as encrypted text. You won't be added to any mailing list, and your details won't be shared with any third party.