Tuesday, August 31, 2010

Turn your Android Phone Into a Remote Spy Camera with Ruby in 15 Minutes

It's been a great year for Ruby on Android, but no one knows it.

Sinatra running on a Motorola Droid

You can start writing Ruby apps for Android devices TODAY.  You don't need to install any SDK, you don't need to install some giant Eclipse IDE, and you certainly don't need to write any Java.

Some of you may have heard of Ruboto, but Damon Kohler and the team at android-scripting have been quietly improving SL4A over the past year, adding tons of features and improving stability.
Scripting Layer for Android (SL4A, formerly known as Android Scripting Environment or ASE) brings scripting languages to Android by allowing you to edit and execute scripts and interactive interpreters directly on the Android device. These scripts have access to many of the APIs available to full-fledged Android applications, but with a greatly simplified interface that makes it easy to get things done...  Python, Perl, JRuby, Lua, BeanShell, JavaScript, Tcl, and shell are currently supported, and we're planning to add more.
There's even support for distributing  programs as APKs, so the implementation language and dependencies are totally transparent to users.

We'd love to run some simple Ruby scripts on Android, but what about running a complete, albeit simple, application?  Will it work?

Most Rubyists use Ruby to build web applications, so let's run with that.  Let's build a spy camera app, so I can figure out who's been stealing ink cartridges from our office.  The app should (a) show me a snapshot of what the camera currently sees and (b) update that snapshot on demand.

We need a simple, lightweight ruby framework to use. Sinatra is very easy to use and should even be able to run on older Android devices.

Similarly, we need a simple, lightweight, pure ruby web server.  WEBrick is pure ruby, part of the standard library, and fast enough to run on Android.  Works for me!

First, let's work from our development machine (i.e. laptop).  No need to touch the phone just yet.

Start a New Sinatra Project



But wait!  What about Sinatra itself?  We can't easily install rubygems on our Android phone, so we'll have to vendorize Sinatra and all dependencies and ship them with our application.  Since we'll be running JRuby on Android, we can't use any libraries that have native C extensions or are otherwise JRuby-incompatible.

Make Directories for Gems and Snapshots



Vendorize your Gems



Create the App

Create a file called spycamera.rb in the project directory.

First, let's set some constants for the important directories in our application. When running apps under SL4A, we need to be very explicit about directory paths.



Let's update our load path, so JRuby can find our vendorized gems.


Next, we'll load Sinatra.  We'll have to explicitly load rack first, to avoid uninitialized constant errors under SL4A.



Finally, we require the Android API interface, available under SL4A, and initialize an Android object.  This will allow us to make API calls, like one that tells the camera to take and store a picture.




Now, let's create a very simple HTML template.  The template should just display the latest snapshot, which we'll be saving to public/latest.png.  If I click on that snapshot, it should take a new snapshot and refresh the page.



Finally, create a tiny Sinatra app that takes a picture, saves it, and renders the template.



Install (Deploy) The App

Now you can finally use your phone!  Grab that Android device and install SL4A (r1 APK available here).  Run SL4A, go to the interpreters menu, and install JRuby.

Once JRuby is installed, it's time to deploy your application to the phone.

Connect your phone to your development machine via USB, and simply copy the contents of your app to SL4A's script directory on your phone.  There may be some sample ruby scripts left over from the SL4A install; you can delete them if you like.

Copying the project contents onto the deivice.

At this point, just run spycam.rb from SL4A and you're good to go!

Starting the Sinatra app from SL4A

Place your newly-activated spy camera somewhere, hide in your office/cube, start your web browser, and navigate to http://ip.address.of.phone:4567.  You can do this from inside your local network via wifi, or, carrier permitting, over 3G.

Happy spying!

Further Reading
Check out my open-source project, Broadcast, on Github.  It's a more refined, general-purpose embedded web app for Android.  It allows for further remote monitoring and control, such as location tracking, remote text-to-speech, and remote file management over HTTP.  


The source code for the spycam app is available on github here.


Check these out, too:

12 comments:

ChakYi said...

thanks for sweet script :)

connect via 3G network with ssh tunnel(using ConnectBot and my ssh server) - http://flic.kr/p/8xf3BG

jaymcgavren said...

Nice! BTW, Ruboto now supports creating .apks as well: ruboto-core

Jackson Broshear said...

Ooo, super fun! Thanks!

Sigmar said...

It doesnt work, I get this error:

spycam.rb:6: syntax error, unexpected tCONSTANT, expecting '}'
...LOAD_PATH << File.join GEM_DIR, dir, 'lib'}
^
spycam.rb:6: syntax error, unexpected '}', expecting tCOLON2 or '[' or '.'
spycam.rb:44: syntax error, unexpected $end, expecting '}'

Mike Leone said...

Sigmar, thanks for the heads up. I fixed the error, and changes have been pushed here.

spint said...

Great tutorial, thanks!
One problem however, I hear my phone taking the pictures, but they are completely black, any idea?
I'm on HTC Hero, Android 2.1

Mike Leone said...

Spint, if your camera normally works fine but isn't taking pictures properly with this app, it's probably an issue with SL4A and the HTC Hero in particular. You should file a bug here; the team is always improving support for more devices.

At first, camera support for my Motorola Droid was a bit spotty, but I filed a bug and they had it fixed and released within a week or two.

spint said...

Ok Mike, apparently there was already a related issue (#400), tnx !

feydr said...

nice stuff! I knew about sl4a but didn't know it was this easy now

AJ said...

Yo,

Doesn't work for me.

I get

Dalvik VM unable to locate class 'org/jruby/Main'
java.lang.NoClassDefFoundError: org.jruby.Main

Any ideas?

Jaime said...

Great! I tried it on my Nexus One and works! JRuby crashes from time to time, though.

I'll play a bit with this, thanks!

Patrick van Efferen said...

@AJ

I get the same error on my HTC magic, after a few minutes webrick starts up. Because my phone is rather slow i suspect thats wat's causing the error, works later on.