Today’s tutorial is an introduction to Corona Enterprise development on Android, showing Corona developers how to extend or create a Corona project that leverages native Android functionality. Using Enterprise, Corona developers can combine the best of both the native world and the rich feature set of Corona SDK. Sign up for a 30-day trial and get started today.

Note: If you’re interested in using Enterprise for iOS, see the iOS quickstart tutorial.

Setting Up

Enterprise for Android is a different environment than iOS. While you can use Eclipse as an IDE or the preview of the new Android Studio IDE, it may be easier to work from the command line and use the editor of your choice. This tutorial won’t discuss in detail how to use the command line, but it’s an essential skill to learn, especially for developing with Android.

At the minimum, you should become familiar with your computer’s file system (folder structure) and learn how to use the cd command. If it’s not already running, first open your Terminal application:

Applications → Utilities → Terminal

Here you’ll see the traditional $ prompt — this is where you’ll run various commands in this tutorial.

Install Corona Enterprise

First, install Corona Enterprise in the root Applications folder. Do not install it in a subfolder or elsewhere, as the product requires a specific linking association.

/Applications/CoronaEnterprise

Install the Android Tools

If you’ve already been building Android apps for Corona SDK, you may have done some of this before, but it won’t hurt to repeat the process and get the latest updates from Google. Essentially, you need to install Google’s Android SDK and its tools. To do so, visit https://developer.android.com/sdk/ and click on the Download the SDK ADT Bundle for Mac button. On the next page, agree to the terms and click the download button. This will download a file with a name like adt-bundle-mac-x86_64-20140321.zip.

If your system does not automatically unzip this for you, double-click the file to unpack the archive. If you’ve done this in the past, it will suffix an extra number on the end like: adt-bundle-mac-x86_64 2 or adt-bundle-mac-x86_64-20140321. If this occurs, rename the file so that it’s just adt-bundle-mac-x86_64. You may need to remove the old version from your download folder first.

Move that entire folder to your home directory. This folder contains some files that you’ll want to access from the command line without typing the entire path each time. To facilitate this, you can tell your computer where to find them by creating a file in your home directory called .bash_profile. This is a hidden file and you cannot easily access it from the Finder. So, from the Terminal, type in these commands:

touch ~/.bash_profile
echo "export ANDROID_HOME=~/adt-bundle-mac-x86_64/sdk" >> ~/.bash_profile
echo "export PATH=${PATH}:$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools" >> ~/.bash_profile
exit

Reopen the Terminal at this point and run the android command. This should load the Android SDK Manager which looks similar to this:

android_sdk_manager

From here, you’ll want to install several things including:

  • Android SDK Tools
  • Android SDK Platform-tools
  • Android SDK Build Tools (latest version)
  • Several of the Android n.n.n (API X) entries

Which Android SDK entries you need to install varies based on what you’re building. You should install Android 4.1.2 (API 16) since this is the current version that Corona SDK uses. You should also install the minimum SDK version that you want to support — for Corona SDK, this is Android 2.2 (API 8) for builds earlier than #2264 and Android 2.3.3 (API 10) for builds #2264 and later. Finally, you need to install the target and minimum SDK for any library that you plan to use. While it may seem easier to just install everything, these SDKs take up disk space and they also take time to download/unpack. If you download them all, expect a long wait.

Once you have the necessary items selected, click on the Install [n] packages… button. On the next screen, accept the license agreement(s) and sit back while everything installs. When the process is finished, close the Android SDK Manager and return to the Terminal command line.

App Signing

This may seem early in the process to talk about signing, but its good to understand how it impacts the overall process. Essentially, there are two types of Android apps: those signed for debugging purposes and those signed for release purposes. Apps signed with a debug keystore can be installed directly on devices but cannot be downloaded from stores. Apps destined for the marketplace require a release keystore which you may have already created for use with Corona SDK.

This tutorial assumes that you already have a release keystore and that you know the passwords for the keystore and the alias. If you haven’t yet created one, see the Signing for Release Build section of the Signing and Building — Android guide.

Preparing the Project

Let’s start by preparing the project. Because every Corona developer stores their projects in different locations, for example a folder called Corona in their Documents directory, or perhaps a folder named Projects in the home directory, it’s your responsibility to understand the command line path to this location. This tutorial assumes that you have a folder called Projects located inside your home directory.

From the Terminal, change directory to the folder where you want to create your project:

cd ~/Projects

When you type in commands and you see a tilde (~), that indicates your home directory. As such, the above command places you in the Projects folder.

Next, let’s copy over some template files. The simple project in this tutorial will check if the device is currently connected to WiFi, so consider a project folder name like getWiFi. When you’ve decided, type the following command, substituting ./getWiFi for the folder path/name that you desire:

cp -R /Applications/CoronaEnterprise/ProjectTemplates/App ./getWiFi

This command will copy the folder from /Applications/CoronaEnterprise/ProjectTemplates/App to the folder you specified. If this folder does not exist, the command will create it. You can also do this step visually within the Finder by simply copying the App folder from the /Applications/CoronaEnterprise/ProjectTemplates folder and renaming it to the name of your project.

There’s one last step to prepare your project — you need to change directory to the getWiFi/android folder and run a process to update the project.

cd getWiFi/android
android update project --path $PWD --target "android-16"
android update project --path /Applications/CoronaEnterprise/Corona/android/lib/Corona --target "android-16"

Setting the Package Name

We’re all in the habit of naming our projects with a “reverse domain” name like com.yourcompanyname.yourapp. The template you copied uses the name com.mycompany.app, but if you plan to deploy this app, you need to change this as follows:

1. Rename Folders

Let’s start with renaming the folders inside the src folder. In Android native development, your source files are located here, however they’re also subdivided such that each part of your package name is another folder. For instance, if your package name is com.supercompany.getWiFi, your .java files will live inside:

src/com/supercompany/getWiFi/

However, the project template you copied uses this path:

src/com/mycompany/app/

You must rename each of these folders to match whatever you choose for your package name. If you don’t use .com for your package name, you’ll need to change the com to org, net, etc.

Assuming the sample above, make sure you’re in the project’s android folder and:

cd src/com
mv mycompany supercompany
cd supercompany
mv app getWiFi

2. Edit the Java File

Next, open your favorite text editor and open the following file here:

Projects/getWiFi/android/src/com/supercompany/getWiFi/CoronaApplication.java

Inside it, change the package line at the top to use your reversed package name of com.supercompany.getWiFi.

3. Edit the “AndroidManifest.xml” File

The last step in the package name process is editing the AndroidManifest.xml file:

Projects/getWiFi/android/AndroidManifest.xml

Inside, there are several places where you must change com.mycompany.app to your package name. Be sure to check for all occurrences and change each one.

Also, look for both occurrences of android:label="App" and change them to the desired app name:

android:label="getWiFi"

Finally, since this app needs extra permissions, you’ll need to specify it. Look for this line…

<uses-permission android:name="android.permission.INTERNET"/>

And add an additional permission below it…

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>

The “build.xml” File

In the android folder under your main project, there’s also a build.xml file. Change this line…

<project name="App" default="help">

To this…

<project name="getWiFi" default="help">

To ensure that you’ve done everything correct, return to the Terminal and change directory up three levels to the android folder where the AndroidManifest.xml and build.xml files are:

cd ../../..

Now type pwd (“print working directory”). The result in the Terminal should end in getWiFi/android.

There should be a file inside called build.sh — this is what you’ll use to compile your app. Type this command now:

./build.sh $ANDROID_HOME

If you’ve done everything right, your app should build with out error. You can verify this by looking inside the bin folder for a file named for your app with a -release-unsigned.apk extension, for example getWiFi-release-unsigned.apk. You can confirm with this command…

ls -l bin

Which should yield a Terminal output similar to this…

-rw-r--r-- 1 rmiracle staff 5869 Mar 23 20:31 AndroidManifest.xml
-rw-r--r-- 1 rmiracle staff 174 Mar 23 20:31 AndroidManifest.xml.d
-rw-r--r-- 1 rmiracle staff 932 Mar 23 20:31 R.txt
-rw-r--r-- 1 rmiracle staff 182 Mar 27 21:44 build.prop
drwxr-xr-x 4 rmiracle staff 136 Mar 23 19:59 classes
-rw-r--r-- 1 rmiracle staff 882504 Mar 23 20:31 classes.dex
-rw-r--r-- 1 rmiracle staff 2680 Mar 23 20:31 classes.dex.d
drwxr-xr-x 7 rmiracle staff 238 Mar 23 19:59 dexedLibs
-rw-r--r-- 1 rmiracle staff 2881703 Mar 27 21:44 getWiFi-release-unaligned.apk
-rw-r--r-- 1 rmiracle staff 2879621 Mar 27 21:44 getWiFi-release-unsigned.apk
-rw-r--r-- 1 rmiracle staff 1916 Mar 27 21:44 getWiFi-release-unsigned.apk.d
-rw-r--r-- 1 rmiracle staff 120 Mar 27 21:44 jarlist.cache
drwxr-xr-x 3 rmiracle staff 102 Mar 23 19:59 lib
-rw-r--r-- 1 rmiracle staff 1402 Mar 23 20:31 proguard.txt
drwxr-xr-x 2 rmiracle staff 68 Mar 23 19:59 res
drwxr-xr-x 2 rmiracle staff 68 Mar 23 20:06 rsLibs
drwxr-xr-x 2 rmiracle staff 68 Mar 23 20:06 rsObj

Signing the App

At this point, you have a compiled Android app that doesn’t do much. It also has not been signed for either debug or release, and it’s unaligned. To correct this, you can run the following two lines, replacing the capitalized cues with your keystore details.

jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore YOURKEYSTOREPATH/YOURRELEASE.keystore bin/getWiFi-release-unsigned.apk YOURALIAS
zipalign -v 4 bin/getWiFi-release-unsigned.apk bin/getWiFi-release.apk

As you can see, the jarsigner command is complicated. Fortunately, the build.sh script also knows how to sign your app if you tell it where to find the keystore files.

To make your life easier, create a new file called ant.properties. This file should be created in the android folder (the same folder as build.xml and build.sh) and it should contain two lines:

key.store=YOURKEYSTOREPATH/YOURRELEASE.keystore
key.alias=YOURALIAS

Of course, you must substitute your custom keystore details in these lines. Let’s say that you created your keystore named androidrelease.keystore with an alias of androidrelease and saved it in the Projects folder off of your home directory. In this case, the ant.properties file would look like the following, with YOURHOMEDIR changed to your home directory.

key.store=/Users/YOURHOMEDIR/Projects/androidrelease.keystore
key.alias=androidrelease

Release or Debug Builds

If you want to swap between release and debug builds, the easiest approach is to duplicate the build.sh file and rename it build_debug.sh. Once duplicated, edit build_debug.sh and change the bottom line from…

ant release -D"CoronaEnterpriseDir"="$CORONA_PATH"

To this…

ant debug -D"CoronaEnterpriseDir"="$CORONA_PATH"

Then, you can run either build script depending on which type you want:

./build.sh $ANDROID_HOME
./build_debug.sh $ANDROID_HOME

Installing the App

Now tether an Android device to your computer via its USB cable. This tutorial assumes that you’ve already set your device into Developer Mode, that you’re familiar with the adb command (guide), and that you successfully built the app using the method above. When ready, enter the command:

adb install -r bin/getWiFi-release.apk

This should install the app on your device. Find it and run it, and — if you wish — monitor errors in the console log using the command:

adb logcat

Making it Interesting

At this point, you’ve built an empty app project template which does virtually nothing. Let’s add some functionality that you cannot do from within Corona SDK.

1. Edit “CoronaApplication.java”

To begin, open the CoronaApplication.java file located here:

Projects/getWiFi/android/src/com/supercompany/getWiFi/CoronaApplication.java

Look for the following code starting around line 35…

      @Override
      public void onLoaded(com.ansca.corona.CoronaRuntime runtime) {
      }

Replace this code with…

      @Override
      public void onLoaded(com.ansca.corona.CoronaRuntime runtime) {
         com.naef.jnlua.NamedJavaFunction[] luaFunctions;

         // Fetch the Lua state from the runtime.
         com.naef.jnlua.LuaState luaState = runtime.getLuaState();

         // Add a module named "myTests" to Lua having the following functions.
         luaFunctions = new com.naef.jnlua.NamedJavaFunction[] {
            new IsWiFiEnabled(),
         };
         luaState.register("getWiFi", luaFunctions);
         luaState.pop(1); 
      }

To summarize: in this Java code, you’re working in a class called CoronaApplication which has a method called onLoaded(). This method is called after the Lua Runtime has been created but before your main.lua has executed. The com.naef.jnlua is the Lua class for Java.

This code block does four things:

  1. Get the current Lua state for communicating with Lua.
  2. Define a list of functions that Lua can execute.
  3. Define the library name that Lua will use to run your functions.
  4. Tell Lua to get those functions.

In this case, there’s one method that you’re making available: IsWiFiEnabled(). Then you register the library name "getWiFi" that you’ll use on the Lua side to access these features.

2. Create “IsWiFiEnabled.java”

Next, create a new Java file in the same folder named IsWiFiEnabled.java with this source:

package com.supercompany.getWiFi;

import android.app.Activity;
import android.content.Context;
import android.net.wifi.WifiManager;

/**
 * Implements the isWiFiEnabled() function in Lua.
 */
public class IsWiFiEnabled implements com.naef.jnlua.NamedJavaFunction {
   /**
    * Gets the name of the Lua function as it would appear in the Lua script.
    * @return Returns the name of the custom Lua function.
    */
   @Override
   public String getName() {
      return "isWiFiEnabled";
   }

   /**
    * This method is called when the Lua function is called.
    * 

    * Warning! This method is not called on the main UI thread.
    * @param luaState Reference to the Lua state.
    * Needed to retrieve the Lua function's parameters and to return 
    * values back to Lua.
    * @return Returns the number of values to be returned by the Lua function.
    */
   @Override
   public int invoke(com.naef.jnlua.LuaState luaState) {
      com.ansca.corona.CoronaActivity activity = com.ansca.corona.CoronaEnvironment.getCoronaActivity();
      if (activity == null) {
         return 1;
      }

      boolean value = false;
      WifiManager wifi = (WifiManager) activity.getSystemService(Context.WIFI_SERVICE);
      if (wifi.isWifiEnabled()){
         value = true;
      }

      // Push the boolean value to the Lua state's stack.
      // This is the value to be returned by the Lua function.
      luaState.pushBoolean(value);

      // Return 1 to indicate that this Lua function returns 1 value.
      return 1;
   }
}

The public class IsWiFiEnabled() line has to match what you put in the other Java file when defining the functions for Lua, and it’s case sensitive. Next, just below that, you define a function called getName(). This returns the name of the function that your Lua code will use. The combination of this, plus the library name in the CoronaApplication.java file, will produce the call for Lua, in this case getWiFi.isWiFiEnabled().

The invoke method runs your native code. The first block gets the Corona activity. The next block of code accesses the WiFiManager object. The variable value is defaulted to false and if the WiFiManager says it’s enabled, that value is set to true. Then the value is pushed as a boolean onto the luaState stack, and this value will be returned to your Corona SDK code.

The Corona SDK Side

Because Corona Enterprise templates are set up with the assumption that you may want to build for multiple platforms, the Corona folder is a peer to the ios and android folders and is located here:

Projects/getWiFi/Corona

Inside you’ll find the Corona project. Open us the main.lua file and change it to match the following:

local myText = display.newText( "Checking", 0, 0, native.systemFontBold, 32 )
myText.x = display.contentCenterX
myText.y = display.contentCenterY

timer.performWithDelay( 2000, function()
   local isEnabled = getWiFi.isWiFiEnabled()
   if ( isEnabled ) then
      myText.text = "WiFi is enabled"
   else
      myText.text = "WiFi is not enabled"
   end
end, 0 )

This creates a display.newText() object that shows the status of the WiFi. Every 2 seconds, the timer will fire, get the value of the getWiFi.isWiFiEnabled() function, and update the text string accordingly.

Now go to your Terminal and repeat the building, signing, and installation process. If everything is correct, it should run and you should see the string “Checking” show up on your device for a couple of seconds. Then it should update the string depending on the status of your WiFi connection.

Conclusion

Clearly this tutorial is just a basic example of what can be done with Corona Enterprise, but hopefully it has shown you what can be accomplished when you combine the nearly unlimited power of native programming with the ease and simplicity of Corona SDK.

  1. Is it possible to change the directory that the apk will be built into when running ant release -D”CoronaEnterpriseDir”=”$CORONA_PATH”

    I have the simulator build all of my apks in a single “builds” folder so I know where to find them, but the ant method always builds it into the projects bin folder. Or is it possible to access the “result” of the ant release function? I have a shell script which compiles multiple projects at once, and then builds there apks, so it would be handy if I could build them all directly to a single directory.

    • Yes. If you don’t do the ant.properties to automatically sign the build and hand run jarsigner and zipalign, then you can zipalign output to wherever you want. The last parameter is the path and file to output.

  2. Amazing thanks.

    Just a comment:
    “Finally, since this app needs extra permissions, you’ll need to specify it. Look for this line…”… but the lines are empty on the tutorial.

    • Brent Sorrentino says:

      Hi David,
      Sorry about that, the system stripped them out. I’ve replaced them just now… basically, you need to grant permissions to both .INTERNET and .ACCESS_WIFI_STATE.

      Brent

    • That is telling me that it could not find your “android” command which means you have not edited your .bash_profile file to add your Android SDK files location to your system path.

  3. I had a trouble in following this tutorial.

    After type the command of ./build.sh $ANDROID_HOME, the authorization error occurs.

    The Terminal output is as follows:

    ( … )

    [exec] Creating application.metadata …
    [exec] Archiving /Users/lee/Projects/GameSample/android/assets/*.lu into /Users/lee/Projects/GameSample/android/assets/resource.car
    [exec] ERROR: Failed to generate resource.car.
    [exec] ERROR: Could not verify authorization.
    [exec] ERROR: This computer is not properly authorized. You must have an account with the Enterprise (Native extensions) subscription. Please use the ‘authorize’ command or check that your subscription type is valid.

    So, I check my account in Corona Simulator > Preferences.
    There is as follows:
    User Account : ekkorrgames@naver.com
    Subscription : Enterprise (Native extensions)

    I am using Daily Builds 2014.2358.
    Corona and CoronaEnterprise folders are in Applications folder.

    What is wroing?

      • For everyone’s benefit. The solution to ekkorr’s problem, which I had to, is to active the corona enterprise license using the Corona Enterprise binary. Setting the account int CoronaSDK is not enough.

        /Applications/CoronaEnterprise/Corona/mac/bin/CoronaBuilder.app/Contents/MacOS/CoronaBuilder authorize your_email your_password

  4. Just to register here for future users: if you are getting the following error when trying to build your enterprise project:

    [dex] Converting compiled files and external libraries into /Users/MyName/Projects/myCoronaApps/App/android/bin/classes.dex…
    [dx]
    [dx] UNEXPECTED TOP-LEVEL EXCEPTION:
    [dx] java.util.zip.ZipException: error in opening zip file
    [dx] at java.util.zip.ZipFile.open(Native Method)
    [dx] at java.util.zip.ZipFile.(ZipFile.java:128)

    it is probably because you forgot to run the following command when setting up your new Enterprise project:

    android update project –path $PWD –target “android-16″

  5. I followed all the steps. When I installed the app I got this error: [INSTALL_PARSE_FAILED_NO_CERTIFICATES].

    I just want to install the app onto the device. I was successfully built the app. I copied build.xml, renamed it to build_debug.xml and changed ant release to ant debug since i just need the debug build. Any suggestion?

      • I can’t test this at the moment, but if you want to use a release keystore, you can create a file called: secure.properties that contains:

        key.store.password=yourpassword
        key.alias.password=yourpassword

        Then in your build.xml, add:

        just under the entry.

        Rob

  6. Hmmm, looks like the comments stripped out my XML: Add:

    <property file=”secure.properties” />

    just after the:

    <project name=”yourproject”>

  7. Hi,
    Good tutorial but would be good if you mention that we must have “apache-ant” installed.

    I had the next error when I tried to run this line in console “./build.sh $ANDROID_HOME”
    ERROR: corona enterprise ./build.sh: line 86: ant: command not found.
    The solution is installing macport “http://www.macports.org/install.php” then type in console “sudo port install apache-ant”

    Omar

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title="" rel=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>