Georg Lukas, 2011-12-29 19:10
tl;dr
APRSdroid is an Amateur Radio geo-location (APRS) app for Android licensed under the GPL. It started as a Scala learning experience two years ago, and has become a nice auxiliary income since, despite being Open Source and offering free downloads from the homepage. However, using Scala was not always the easiest path to go.
Project history
In mid-December of 2009 a HAM radio friend asked me: "it can't be too hard to make an Android APRS app, can it?" and because there was none yet, I started pondering. On December 31rd, instead of having fun and alcohol, I made the first commit. January 1st, at 03:37 local time, the first placeholder release 0.2 was created.
Over the course of the next weeks I discovered step-by-step what I had encumbered myself with. APRS is a protocol with a long history of organic growth, firmware limitation workarounds, many different ways to say the same thing (at least four just for position reports), countless protocol amendments and, to add insult to injury, base-91 ASCII encoding.
There was no Java code available to abstract away the protocol and to allow me to keep my sanity. So I read the spec, implemented position encoding, re-read the spec, implemented HTTP and UDP sending code, read the amendments, re-re-re-read the spec, etc.
The first usable release became 0.4 from the end of January. Because APRSdroid always was a leisure time project, phases of activity alternated with idle phases, and the app slowly grew features through 2010.
In early 2011, one year into it, I decided it was high time to make the project pay for itself. Real APRS gear (radio transceivers with GPS and packet radio support) was expensive (on the order of US$500), and the app was not only easier to use but also grew more and more features (except for direct access to the amateur radio spectrum, which does not work well on cell phones).
For some time, I went underground (by omitting git push
to github and
only providing nightly builds to some friends) and worked on the code
behind closed doors (there were no other people contributing source
anyway).
On April 1st, I decided to fool the community a little, but was not taken too seriously. In the meantime I was polishing a 1.0 release for Android Market.
Income Report
On April 18th, 2011, APRSdroid 1.0 was "commercially" launched to Android Market. It was important for me to keep up the OSS spirit, so I kept providing source code and APK files from the home page. By buying the app instead of just downloading it, the users got automatic updates and a good feeling of supporting what they liked. Also, I did not make it too obvious in the Market description that the app can be downloaded for free as well ;-)
So far, this scheme has paid off very well. Since the beginning, more than 60% of all app users actually bought it (it is possible to monitor the global user activity on the APRS network), with an average of 350 sales per month, at 2.99€ / 4.49US$ (minus the Google "tax" and subject to local income taxes).
Most users I had contact with were ready to pay for the app even though they knew they could download it for free. Only one person so far demanded the free version to be made available on Android Market (using CAPS and three consecutive Twitter messages, though, so I did not feel too pressed).
So far, I invested the income into real APRS hardware, a Desire Z (or G2 or HTC Vision) and am eagerly awaiting the availability of ICS tablets, aiming at finally adding Fragments support to the app.
Scala + Android = Pitfalls
I decided to use Scala because I do my coding in
vim and Java is so crammed up with boilerplate
code that you can not sensibly use it with anything but a bloated
refactoring IDE. Another reason was that I do not like to repeat myself,
and Java provides even less usable abstractions than the good old C
language with its #define
.
Scala was the language of the day, and I liked what I had read about it
so far. It sounded good enough for an experiment anyways.
Fortunately, people had already
figured
out
how to make it work on Android without carrying the bloat of the full
Scala runtime, so all I had to do were some refinements of build.xml
.
The first warning sign was that I had to
override def $tag()
to work around an issue in the 2.7 beta compiler (IIRC). I complied by
cargo-cult-copying
the code from some place and moved on.
Another major issue was Android's
AsyncTask.
The API requires the developer to override protected SomeType
doInBackground(OtherType... params)
. Unfortunately, Scala has trouble with
overriding abstract varargs methods from Java,
and thus your app crashes with the opaque java.lang.AbstractMethodError:
abstract method not implemented
exception. After triangulating the
source of the problem (who would have suspected a compiler bug?), a
wrapper class in Java
was written. Another bunch of days well spent.
One of my biggest hopes in Scala was to be able to reduce the boilerplate for Android's numerous single-abstract-method function) parameter workarounds. Unfortunately, this problem is not yet solved in Scala, requiring to write explicit implicit conversion functions for each SAM type.
However, not everything was bad in Scala-land. Scala's traits allowed to
reuse the same code
in descendants of Android's
Activity,
ListActivity
and
MapActivity.
Working string comparisons, type based match
and a huge amount of
syntactic sugar, added on top of a proper
ctags config,
actually made life good.
Further, the base-91 decoding was elegantly implemented as a map/reduce operation on the ASCII string. Other interesting solutions were: an UrlOpener for buttons and regex based packet matching (Warning: please do not try to understand the regexes!).
What remains in the end is build time (compilation + proguard), which is
subjectively higher for the Scala app than for a Java-only project of
comparable size. However, that might be due to a bug in my build.xml
and so far I was not impatient enough to investigate.
Conclusion
After two years, I am really glad to have gone this way. Learning Scala was a very pleasant experience, and it improved my ability to see problems from different points of view. However, it also significantly restricted the number of people able to contribute. Of over 500 commits to APRSdroid, only three were by another developer. The APRS parsing code has been replaced by javAPRSlib, a Java library with major contributions from several other people.
APRSdroid remains my only Scala project. My other Android projects are written in Java, either because I did not want to restrict contributors, or because I did not expect the Java code to become complex enough.
Would I start a new Scala project on Android? Probably no, as it is already hard enough to find people who would like to contribute to your pet project if it is written in Java. Scala makes that almost impossible.
Would I contribute to an existing Scala project? Yes!
P.S: Starting around March 2012, I will be looking for Android/IT-Sec related freelance jobs. Check github and Android Market for my other projects.
Hi,
I bought the software via Google market a few weeks ago with my wife's Samsung Galaxy smartphone. Payment already deducted from my credit card. However till now I have not yet receuved from you the password. I'm going to SriLanka soon and need it urgently. Thanks Dov 4Z5DZ dovdvir@netvision.net.il
The APRS-IS passcode is required by the APRS-IS server operators to authenticate that you are a radio amateur.
Typically, you can either ask an iGate operator from your area or use the request link in APRSdroid. However, due to the high amount of requests it takes several days, sometimes up to two weeks, until you will get a reply.
I have issued a passcode for your personal request already, though.
No need for a Java intermediary class :)
class MyAsyncTask() extends AsyncTask[Object, Void, Void] {
}
Note the Object as a first param. It won't work with anything else, but works with Object. Then you just cast it to a Seq of whatever your real type is supposed to be.
Now that I think about it, this could be even cleaner if MyAsyncTask had another type parameter, the type of RealType. Hmm.
Do you plan to add support for USB to serial adapters? I have an Acer Iconia A500 (ICS 4.0.3) with a full size USB port. I would like to connect it to a USB to serial adapter to connect to my TNC. I would also like to connect it to the serial port on the tnc in my Kenwood D-10. You can contact me at n7nms@arrl.net Thanks and 73 de Mike N7NMS
First of all, thank you for a fabulous app. It has been quite a bit of fun for me. I purchased the app about a month or so ago and have only encountered one issue, and it is not a flaw, but a feature request. Is there a way to have APRSDroid start automatically at boot up? Would you give any consideration to adding that feature?
Thanks,
Hi Ed,
thank you for your feedback. There are very many tasks I've got on my APRSdroid TODO list, with autorun being one of them. Unfortunately, my time is very limited, so I am picking the most important features first.
It should be possible, however, to use one of the Android automation apps (like Tasker, but there might also be a free alternative), to launch the service. The required settings are:
This just needs to be configured to run on boot.