Saturday, February 12, 2011

How to make an android app with SL4A and Eclipse

Having completed my first python script using sl4a on my HTC Desire I decided the next step was to create an APK (Android PacKage) to make it easier to download and run. The process is not that easy (but easier than writing the whole thing in Java) so the following explains how it was done on my PC running Windows Vista:
  • Download and install JDK from Oracle: http://www.oracle.com/technetwork/java/javase/downloads/index.html
  • Download and install Eclipse Helios: http://www.eclipse.org/downloads/ If you already have Eclipse installed make sure you have the latest updates.
  • Download and install the Android SDK http://developer.android.com/sdk/index.html If you already have the Android SDK installed make sure you have the latest updates.
  • On start up Eclipse will ask you to create a working directory. Once you have done this install the ADT plugin to Eclipse as follows: Select Help > Install New Software. Click Add, in the top-right corner. In the Add Repository dialog that appears, enter "ADT Plugin" for the Name and the following URL for the Location:  https://dl-ssl.google.com/android/eclipse/  If you have trouble acquiring the plugin, try using "http" in the Location URL, instead of "https" (https is preferred for security reasons). Click OK. In the Available Software dialog, select the checkbox next to Developer Tools and click Next. In the next window, you'll see a list of the tools to be downloaded. Click Next. Read and accept the license agreements, then click Finish.When the installation completes, restart Eclipse.
  • In Eclipse click onWindow/Preferences/Android and set the SDK location to where the SDK was installed (probably C:\Program Files\Android\android-sdk-windows).
  • Download and install Hg from mercurial: http://mercurial.selenic.com/wiki/Download 
  • Have a cup of coffee (still some way to go!)
  • Create a clone of the latest sl4a repository: Use windows explorer to create a directory where you will store the clone. Hold down the shift key and right click on the directory then open a command window. In the command window type: hg clone https://rjmatthews62-android-scripting.googlecode.com/hg/ and some time later your clone will be there. The repository gets updated regularly so use 'hg pull -u' to get and update the latest repository. Exit the command window.
  • Open Eclipse and click on File/Import/General/Existing Projects into Workspace/Next and Browse to the cloned directory. Click on Android/OK then select all and click Finish. Your screen should look like this (with lots of errors):                    [Click on view to enlarge]

  • Don't panic! First of all you don't need all projects at this stage so right click and Close Project for python, beanshell, jruby, lua, perl, rhino, tcl, InterpreterForAndroidTemplate and Documentation Generator.
  • Next include ANDROID_SDK in the Classpath Variable list by clicking Windows/Preferences/Java/BuildPathVariables/New. Put ANDROID_SDK for the name and your SDK directory for the folder (probably c:\ProgramFiles\Android\android-sdk-windows).
  • Now click Project/Build Automatically, then Project/Clean/Clean all Projects/OK. If you get error messages of files missing try Project/Build All without cleaning first. If you still have errors check if any projects have .jar entries in their libs folders which have not yet been added to the build path (right click/BuildPath/add to Build Path if not - but don't put in duplicate entries). Then try Project/Clean/Clean all Projects/OK (and maybe once more if you still have errors). Ignore the numerous warnings.
  • All these projects now comprise a working clone of SL4A. To turn your script into an APK make a copy of ScriptForAndroidTemplate (right click/copy then right click/paste into the same area). A new project will appear with the name Copy of ScriptForAndroidTemplate. To connect this project to your clone of SL4A double click on it, right click on build.xml /Run As /Ant Build. Rename the project using Refactor/Rename to whatever name you choose for your project and Refresh/Clean/Build the project.
  • Connect your phone to the PC via the USB cable, press menu, click settings/Connect to PC/Default connection type/Charge only/Done/ then press Home. If you haven't got a suitable phone set up an emulator in the Android SDK and AVD Manager (but be aware the emulator may or may not start at the first attempt and can take 10 to 20 minutes to start working the first time!).
  • Using the phone press menu again, then click settings/Applications/Development/USB debugging/OK/ then press Home. Vista should automatically find the USB driver for the phone.
  • From Eclipse install your cloned version of SL4A on your phone by clicking on ScriptingLayerForAndroid to highlight it, then Run/Run/AndroidApplication/OK.
  • With SL4A installed click on your project to highlight it then Run/Run/AndroidApplication/OK. The Console should inform you of the Android launch and a short time later you should see 'Hello Android' appear briefly on the phone (or emulator) which is the output of the default script.
  • Now you will want to tailor your project. Double click your project/res/drawable. Script_logo_48.png is the 48x48 image file that will display on your phone's screen when installed. Replace it to suit your requirements.
  • Further down from drawable double click values then strings.xml. In the xml display window click on strings.xml and change Dummy Script to your app name (must be less than 10 characters). Right click on the screen and save your change.
  • Next double click on raw and Refactor/Rename to change the name script.py to your_script_name.py then double click src/com.dummy.fooforandroid/Script.java and change  R.raw.script to R.raw.your_script_name and save.
  • Double click on src and Refactor/Rename to change the package name com.dummy.fooforandroid to your.package.name (you must have at least 2 full stops in the name). Do the same for gen (ignore the warning that the name already exists).
  • Now highlight your project then click on Project/Properties/Android select your android target and click OK
  • Towards the bottom of your project list double click on AndroidManifest.xml. Change the package name in the manifest from com.dummy.fooforandroid to your.package.name. Change your minSdkVersion from "4" to suit your android target (this is the API level for your android target shown in Project/Preferences/Android). Uncomment the permissions you require your application to have (take off <!-- at the start and  --> at the end).
  • Right click on your_script_name then open with text editor. Delete the default script, replace it with your own and save. Then Clean/Build/Refresh your project and run it. If all goes well click on File/Export to export your application as an apk. Copy the apk to your phone's sdcard, and use astro to install it.
  • Eclipse will automatically set your project to debuggable. If you want to distribute your apk open the Manifest Application and set debuggable to False. Then export again. Warn users that the interpreter for your script will also be downloaded when they install the apk.
  • All finished! Wasn't that easy!!

Wednesday, December 1, 2010

Using Flickr for crash recorder backup

I have been looking at the possibility of using flickr to backup my crash recording videos. Since I used python and sl4a to create my crash recorder program I looked for python scripts which performed uploads to flickr. I found some and got one working (with a few updates/modifications). Unfortunately I found it very difficult to work out exactly what the script was doing due to unnecessarily complex nested calls and sorting, json, xml and mime_types methods. Flickr tells you exactly what data strings are needed (to provide you with what you require) so it really doesn't need hard to follow programs to achieve this. Therefore, I decided to write my own flickr upload script. To write your own flickr app you need to go to the flickr api garden and apply for your own flickr api_key and secret. Anybody who wants to use your app needs to have a flickr account and obtain an authorisation token. This is a 'one-off -' process so I have written 2 scripts: one to enable a user to get a token (only needs running once); the other to use the token to allow photo/video uploads.

---------------------------------

# FlickrToken.py by John Karwatzki November 2010
# Get a flickr authentication token to use an app

import hashlib, urllib, android
droid = android.Android()

# flickr key and secret for the crash recorder app
apikey = '88e6f2645d45df438689358aac3de9bb'
secret = '000f5e98af65ece4'

# write your own app with a new key and secret from flickr app garden
url = 'http://api.flickr.com/services/rest/?'

# get api signature and url
dstring = secret+'api_key'+apikey+'methodflickr.auth.getFrob'
apisig = hashlib.md5(dstring).hexdigest() # encrypt signature

# get the frob needed for a new flickrToken
args = 'method=flickr.auth.getFrob&api_key='+apikey+'&api_sig='+ apisig
f = urllib.urlopen(url + args)
resp = f.read()
f.close()
fr = resp.find('frob') # no need for json or xml routines
end = resp.find('</frob')
frob = resp[fr+5:end]

# get a flickr login signature and url using the frob
dstring = secret+'api_key'+ apikey +'frob' + frob + 'permswrite'
apisig = hashlib.md5(dstring).hexdigest()
logurl = 'http://www.flickr.com/services/auth/?api_key=' + apikey + '&perms=write&frob=' + frob + '&api_sig=' + apisig
droid.webViewShow(logurl, True) # login to flickr

# on exit from webView get a token for the app
dstring = secret+'api_key'+apikey+'frob'+frob+'methodflickr.auth.getToken'
apisig = hashlib.md5(dstring).hexdigest()
args = 'method=flickr.auth.getToken&' + 'api_key=' + apikey + '&frob=' + frob + '&api_sig=' + apisig
f = urllib.urlopen(url + args)
resp = f.read()
f.close()
print resp # this will show up any errors
tok = resp.find('token')
end = resp.find('</token')
token = resp[tok+6:end]

# save the token on your phone's sdcard
f = file('/sdcard/token.txt', 'w')
f.write(token)
f.close()
exit()
-----------------------------------------
# FlickrUpload.py by John Karwatzki November 2010

import hashlib, urllib, httplib

def upload(filename):
  apikey = '88e6f2645d45df438689358aac3de9bb'
  secret = '000f5e98af65ece4'

  # Check a flickr token exists on the phone's sdcard
  try:
    f = file('/sdcard/token.txt', 'r')
    token = f.read()
    f.close()
    if not token:
      raise IOError
  except IOError:
    print 'Token not found.  Run FlickrToken.py'
    exit()

  # get file mimetype (my phone only has jpg mp4 and 3gp)
  dot = filename.find('.')
  ext = filename[dot+1:dot+4]
  if ext == 'jpg':
    mtype = 'image/jpeg'
  elif ext == 'mp4':
    mtype = 'video/mp4'
  elif ext == '3gp':
    mtype = 'video/3gp'
  f = file(filename, 'rb') # get file data
  fdata = f.read()
  f.close()

  # get apisig for upload
  dstring = secret+'api_key'+apikey+'auth_token'+token
  apisig = hashlib.md5(dstring).hexdigest()

  # get multipart mime information string
  nl = '\r\n'
  boundary = '----boundary'
  content_type = 'multipart/form-data; boundary=' + boundary
  hd = '--' + boundary + nl + 'Content-Disposition: form-data; name='
  mp = hd + '"api_key"' + nl + nl + apikey + nl
  mp = mp + hd + '"auth_token"' + nl + nl + token + nl
  mp = mp + hd + '"api_sig"' + nl + nl + apisig + nl
  mp = mp + hd + '"photo"; filename="' + filename + '"' + nl
  mp = mp + 'Content-Type: ' + mtype + nl + nl
  mp = mp + fdata + nl + '--' + boundary + '--' + nl

  # send file to user's flickr account
  up = httplib.HTTP('flickr.com')
  up.putrequest('POST', '/services/upload/')
  up.putheader('content-type', content_type)
  up.putheader('content-length', str(len(mp)))
  up.endheaders()
  up.send(mp)
  up.getreply()
  print up.file.read() # check for any errors
  return()

# This is all you need to call upload
upload('sdcard/Photos/pic1.jpg')
upload('sdcard/Photos/pic2.jpg')
exit()

Monday, October 11, 2010

Crash recorder using android scripting and python

I have been working on a demonstrator script to determine the
feasibility of using an android phone as a vehicle crash recording
system.  I have been using SL4A and python (see the attached file) but
the final program would need integration into the Google sat nav app.
Due to the slow speed at which sensor events are recovered from the
event buffer I can't get much further with it at the moment.

# Crash recorder, John Karwatzki September 2010
# When the android phone is used in sat nav mode the
# camera will be facing the front of the vehicle.
# This python script is the first attempt at using the
# z force sensor to detect a crash. The camera provides
# video recording before, during and after the crash
# It will be developed further in the future
import android
import shutil
import os
import time
droid = android.Android()
crash = False
g = 12 # sensitivity value for g-force
# We need to use an available 'rw' directory in ram to
# avoid excessive writing to the sd card
s = 'app-cache/'
# (It would be preferable for SL4A to provide a temporary
# directory for scripting use)
# temporary video files (2 required for continuous recording)
v1 = 'vid1.mp4'
v2 = 'vid2.mp4'
# crash event files
v3 = 'before.mp4'
v4 = 'crash.mp4'
v5 = 'after.mp4'
# take initial setup video
droid.recorderCaptureVideo(s + v2, 3, True)
droid.recorderStop()  # required when recorder is restarted
os.chmod(s + v2, 0777) # allow rw access
v = 0 # swap recordings from one file to the other
# We will need a better way to stop the script in the
# final version. At the moment just shake the phone.
while crash is False:
  if v is 0:
    vid = s + v1
    v = 1
  else:
    vid = s + v2
    v = 0
  # Take 10 second video
  #  We need to disable output to the screen in the final version
  droid.startSensing(5) # Put sensor data into the event buffer
  droid.recorderCaptureVideo(vid, 10, True)
  droid.recorderStop()
  droid.stopSensing()
  os.chmod(vid, 0777)
  # check the event buffer
  e = droid.receiveEvent()
  for count in range(1, 10):  # Ignore the first 10 (no data)
    e = droid.receiveEvent()
  while e.result is not None:
    e = droid.receiveEvent() # We don't need all these
    e = droid.receiveEvent() # but we have to pull them
    e = droid.receiveEvent() # out regardless. This takes
    e = droid.receiveEvent() # 4 to 5 seconds for 300
      # events which is too slow for real time!
    if e.result is not None: # Check for end of event buffer
      z = e.result['data']['zforce'] # Check for crash
      if z > g:
        crash = True
# take video after crash
droid.recorderCaptureVideo(s + v5, 5, True)
os.chmod(s + v5, 0777)
if v is 0:
  os.rename(s + v2, s + v4) # Rename videos to Before, During
  os.rename(s + v1, s + v3) # and After
else:
  os.rename(s + v1, s + v4)
  os.rename(s + v2, s + v3)
# Save videos on sd card (or Flickr.upload if it
# becomes available on SL4A)
shutil.copyfile(s + v3, '/sdcard/' + v3)
shutil.copyfile(s + v4, '/sdcard/' + v4)
shutil.copyfile(s + v5, '/sdcard/' + v5)