PilBox - Building Android Apps in PicoLispBuild your Apps in PicoLisp without an Android SDK
PilBox ("PicoLisp Box") is a generic Android App which allows to write Apps in pure PicoLisp, without touching Java, and without the need of an Android SDK.
You do not need to root your device. And - if you prefer - you do not need a separate development machine (PC or laptop): All can be done in a terminal on the device, and even in a Lisp REPL while the App is running.
Note: PilBox needs Android >= 5.0, and runs only on Arm64 CPUs!
How does it work?The PilBox App itself (called the "PilBox kernel") is written in Java, the normal Android way. It displays a WebView GUI, and starts a PicoLisp binary compiled for Arm64 CPUs. This binary may now run any PicoLisp program, by setting up a local web server where the WebView component connects to, possibly opening a database, and doing whatever is desired.
The PilBox kernel provides an interface to the Android Java runtime environment. To the PicoLisp code it looks like a remote database, where Java objects are mapped to PicoLisp DB objects, and Java functions and methods are executed via remote procedure calls.
If a ZIP file is passed to the PilBox kernel upon start-up, it is unpacked, and may override or extend whatever is installed already. This is done typically by clicking on a downloaded ZIP, or by "sharing" it from some other App.
Then it looks for a file "App.l" in the App's working directory, loads it, and listens at the port number found in a file "Port". Out of the box, "Port" is 8081, and "App.l" is a meta-App which provides a convenient runtime environment for managing Apps installed via the ZIP mechanism.
Getting startedYou can download the PilBox App from Google Play store, or get it directly from https://software-lab.de/pilBox.apk. In the latter case, you may need to enable "Unknown Sources" in your settings.
To try the examples in this article, download these files:
pilBox.apk contains the PilBox kernel App as an APK ("Android Package"), and the ZIP files are small demo applications.
The pre-installed generic AppInitially, when started without any ZIP file, the PilBox kernel shows an empty white screen with a PicoLisp logo on the top-left and a settings icon on the top-right (the screenshots were taken on a tablet, so on a phone the proportions may differ):
The REPLIf you tap on the PicoLisp logo on the left, it opens a REPL (Read-Eval-Print Loop) - a kind of terminal window into the App:
The REPL gives you control over many aspects of the App. It has a large text field for displaying results and editing text, a single-line text field at the bottom for command input, and the three buttons "Eval", "Edit" and "Clear Cache".
The main purpose is to enter commands, and look at their results in the text area. As the "Eval" button is the first on the page, it is implicitly activated if you hit Enter when typing a command.
As a special case, if a command starts with an exclamation mark:
it is taken as a Shell command, here ls -l, and shows the listing in the text area:
Otherwise (not starting with an exclamation mark), the command line is evaluated as a Lisp expression. For example, after typing (pp 'gps), we get:
If you type a file or path name like App.l in the command line,
and press the "Edit" button, the file is opened in the text area and may be edited:
Press the "Done" button to close the file again.
The "Clear Cache" button invalidates the WebView cache, and is useful after changing an App's CSS file.
Tapping on the PicoLisp logo takes you back to the start screen.
Normally, you won't edit your App's sources in the REPL, but instead edit them in a terminal program like Termux with vim or emacs, and pass them as a ZIP file to PilBox as described below.
SettingsIf you tap on the settings icon on the right of the start screen, it opens a default settings page. Initially it shows two tabs, one with all standard PicoLisp localizations
and one listing the installed PILs (PicoLisp Apps):
Initially the list is empty.
The REPL behavior and the default settings page are normally overridden by installed Apps, and are available only on the start screen.
Installing your own App(s)The empty PilBox kernel is not very useful, except for perhaps exploring the App environment in the REPL.
To install your own App, prepare a ZIP file with a directory containing
- One or more *.l Lisp files, where one of them must be named App.l
- Optionally a CSS file named lib.css
- Optionally an icon in a file named logo.png
- Optionally a directory called loc/ with localizations
The file App.l is mandatory, and is loaded to start the App. If one of the *.l files is named settings.l, then it overrides the behavior of the settings icon while this App is active.
The first Lisp expression in App.l is read in, and should be a string giving the App's name.
You pass your ZIP file to the PilBox kernel by "sharing" it from another App (e.g. the "Downloads" App). As I use the Termux App for local development, I usually generate the ZIP file directly on the device, pass it to the termux-share utility, using the world-readable storage area:
$ zip -rFS ~/storage/downloads/myApp.zip myApp/ $ termux-share ~/storage/downloads/myApp.zipWhen the PilBox kernel starts, it scans all top-level directories for other files named "App.l". Each of those directories is taken as a PIL App. If only a single one is found, it is started immediately, otherwise a list of buttons with the App names is displayed.
Hello WorldFor a minimal example for a PIL App, look at https://software-lab.de/hello.zip. It consists of a directory hello/ with a single file App.l. This file contains:
"Hello World" (menu "Hello World!" (<h1> "center blue" "Hello World!") )The first line gives the App's name, and the menu expression displays a header line in blue with "Hello World!".
If you pass this ZIP to the PilBox kernel, it immediately shows this screen (because it is the first and only App installed so far):
A tap on the PicoLisp icon has no effect now (we are no longer on the PilBox kernel start screen), but the settings page is still the default one. If we open the "PILs" tab, we see that there is a single package "PIL-hello" installed:
CalculatorLet's move on to something more interesting than the obligatory "Hello World". The package
After we pass it to the PilBox, the start screen now shows two Apps:
The calculator can be started by hitting the topmost button:
Play with it around a little. It employs PicoLisp's big numbers, so it can only handle integers (of unlimited size though).
If we go to the "PILs" tab in the settings, we see that now there are two packages,
and clicking on the "PIL-calc" link lists the package's contents:
The back-arrow brings you back to the "PILs" tab.
Enabling the checkboxes in the "PILs" tab allows for the removal of the selected packages. It removes all files in the package, so it must be handled with care!
The PilBox kernel start screen can be reached by clicking on the PicoLisp icon. There we can also get to the REPL again.
The Demo AppFinally, there is https://software-lab.de/demo.zip, an extensive but rather silly Demo App. It contains examples for conventional GUI components, as they are needed in database applications,
but also or accessing the Android toolbox, like taking pictures or scanning QR-Codes, reading GPS coordinates, and posting notifications:
Feel free to install it, play around, and study the code.
Using the Android APIPilBox is only useful if it can communicate with the Android API on the device. Therefore, it loads the Android library "lib/android.l" in the PicoLisp distribution (which in turn is built upon the Java reflection API).
The central function in "lib/android.l" is java. It maps Lisp function calls to the Java virtual machine, supporting eight types of calls:
(java "cls" 'T ['any ..]) -> obj New object (java 'obj 'msg ['any ..]) -> any Send message to object (java 'obj "fld" ["fld" ..]) -> any Value of object field (java "cls" 'msg ['any ..]) -> any Call method in class (java "cls" "fld" ["fld" ..]) -> any Value of class field (java T "cls" ["cls" ..]) -> obj Define interface (java 'obj) -> [lst ..] Reflect object (java "cls") -> cls Get classDepending on the type of the first one or two arguments (transient symbol, internal symbol, or T) it behaves differently.
This is not trivial, and requires some understanding of the Java side. For example, to aquire a WakeLock, Java code samples do
PowerManager powerManager = (PowerManager)getSystemService(POWER_SERVICE); WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakelockTag"); wakeLock.acquire();To implement the same in PicoLisp, we must first look up the constant values POWER_SERVICE and PARTIAL_WAKE_LOCK. They are found in https://developer.android.com/reference/android/content/Context.html#POWER_SERVICE as "power" and in https://developer.android.com/reference/android/os/PowerManager.html#PARTIAL_WAKE_LOCK as 1. With that, the corresponding Lisp code is:
(java (java (java CONTEXT 'getSystemService "power") 'newWakeLock 1 "MyWakelockTag") 'acquire )The gobal constant CONTEXT is defined in "lib/android.l" and holds the PicoLisp Android Service object.
Stand-alone AppsI use all the above to develop production Apps for my customers. Prototyping and testing is very fast, and I do it completely on my tablet.
It can also be used to provide a stand-alone App, simply by installing only a single PIL file (or by uninstalling all except one), so that it starts up immediately when the PilBox App is started.
Users can install the PilBox kernel the normal way, and use the ZIP file as their "App", either by passing it to the PilBox kernel, or by directly starting PilBox after the ZIP archive was unpacked once.
For a real production App, however, it may be desired also to
- Sign the App with your own keys
- Have another Android-level icon than the PicoLisp icon
- Don't require the user to download and install two files
- or even modify the PilBox kernel itself