Build a Talking Calendar with GoogleCL and Android Scripting Environment

At the end of my post on how to Setup GoogleCL on WinXP I joked about running GoogleCL, Google's new command line tool for accessing Google service, on the Android OS. Trying to make that joke a reality, I found the Android Scripting Environment project which allows running Python scripts on Android - step one: accomplished.

Warning: this is not for the faint of heart. The instructions that follow will require you to not only install software on your Android device, but also to download and modify Python source code. I will assume that you are capable of using SVN to get source code from code.google.com and applying a .patch file. And you'll probably end up having to type on a really tiny keyboard at some point. Don't say I didn't warn you.

Install Android Scripting Environment (ASE)

The easiest way to install ASE is to open up the ASE home page on your PC and scan the barcode.

Hack GoogleCL

Note: this change is now in trunk and won't be necessary in the 0.9.8 release.

Now grab the source code for GoogleCL (see here).

I needed to apply a small patch to get my setup to work with the 0.9.7 release.

The Patch

Index: src/googlecl/__init__.py
===================================================================
--- src/googlecl/__init__.py (revision 299)
+++ src/googlecl/__init__.py (working copy)
@@ -156,7 +156,7 @@
   import pickle
   token_path = os.path.join(GOOGLE_CL_DIR, TOKENS_FILENAME_FORMAT % user)
   if os.path.exists(token_path):
-    with open(token_path, 'r') as token_file:
+    with open(token_path, 'rb') as token_file:
       token_dict = pickle.load(token_file)
     try:
       token = token_dict[service.lower()]
@@ -184,7 +184,7 @@
   token_path = os.path.join(GOOGLE_CL_DIR, TOKENS_FILENAME_FORMAT % user)
   success = False
   if os.path.exists(token_path):
-    with open(token_path, 'r+') as token_file:
+    with open(token_path, 'rb+') as token_file:
       token_dict = pickle.load(token_file)
       try:
         del token_dict[service.lower()]
@@ -236,12 +236,12 @@
   import stat
   token_path = os.path.join(GOOGLE_CL_DIR, TOKENS_FILENAME_FORMAT % user)
   if os.path.exists(token_path):
-    with open(token_path, 'r') as token_file:
+    with open(token_path, 'rb') as token_file:
       token_dict = pickle.load(token_file)
   else:
     token_dict = {}
   token_dict[service] = token 
-  with open(token_path, 'w') as token_file:
+  with open(token_path, 'wb') as token_file:
     # Ensure only the owner of the file has read/write permission
     os.chmod(token_path, stat.S_IRUSR | stat.S_IWUSR)
     pickle.dump(token_dict, token_file)

I made it small on purpose because it's hard to read anyway. Hopefully, this way you'll be able to select it all and copy it easily. Apply the patch to your GoogleCL code.

The patch makes GoogleCL open the auth token file in binary mode. I needed this because later I'll be copying a token file from my desktop to my phone. Without the patch, the phone rejected token the file.

Install GoogleCL

Now you need to copy your GoogleCL source onto the phone. Your Android phone may vary. On mine, I just connect to my desktop computer via USB and select "Disk Drive" as the USB connection type. This mounts the phone's storage as two removable disks. You can also remove the SD card from your phone and put it in a reader connected to your computer, since it's the card we're putting files on.

Find ase/extras/python/ on your SD card. This directory should contain a number of Python modules already, such as gdata and xml. Copy your googlecl/src/googlecl in next to them. Also copy googlecl/src/google to ase/scripts/google.py. You'll use this later when you write your talking calendar scripts.

Write a Script to Call GoogleCL

This script changes your HOME dir to /sdcard so that GoogleCL can write your .googlecl configuration file. I wasn't able to figure out how to let GoogleCL write to the default HOME location. The other settings are what ASE uses when it runs Python scripts. I don't know how many of them are required to actually make this work, and I haven't felt like fiddling with it too much since it's working.

Replace [USERNAME] with your Google account username and save it as ase/scripts/calendar_today.sh.

#!/bin/bash 
export HOME=/sdcard 
export TEMP="/sdcard/ase/extras/pythontmp" 
export PYTHONHOME="/data/data/com.google.ase/python" 
export PYTHONPATH="/sdcard/ase/extras/python:/sdcard/ase/scripts/"
/data/data/com.google.ase/python/bin/python /sdcard/ase/scripts/google.py 
    --user [USERNAME] calendar list --date `date +%Y-%m-%d`

Make It Talk

I love this part because it's so easy with Android. This script calls the one above and passes the output to the phone's text-to-speech service. Save it as ase/scripts/say_calendar.py.

import android 
import subprocess  
p = subprocess.Popen(
        ['/system/bin/sh',  '/sdcard/ase/scripts/calendar_today.sh'],
        stdout=subprocess.PIPE, 
        stderr=subprocess.PIPE)
output, errors = p.communicate()
print errors 
print output 
droid = android.Android() 
droid.ttsSpeak(output)

Important Note for Windows Users: be sure to convert any files written on Windows to UNIX line endings before copying them to your phone. If you need to, you can also open up the files on your phone and remove the extra line end characters there. ASE has a file editor built in and I use ASTRO for editing other files.

Preauthentication

I couldn't get the GoogleCL OAuth process to work on my phone. When I returned to ASE from completing the auth process in the browser the script had already stopped running, interrupting the authentication. I got around that by preparing a auth token file on my desktop and copying it to the phone.

Using your modified copy of GoogleCL on the desktop, authenticate to each of the services you want to use from your phone. Then take the access_tok_[username] file from the .googlecl directory under your home directory and put that file in /sdcard/.googlecl/ on your phone.

Dismount your phone's drives from your desktop, or reinstall the SD card in your phone.

Make It So

Now, assuming you've followed my instructions exactly and I haven't left out anything important, you should be able to:

  • Open up ASE on your phone
  • Select Menu->View->Interpreters
  • Select Menu->Add->Python
  • Press back to return to the list of scripts

You should see a number of scripts listed, including the ones you just wrote. Select say_calendar.py. Your calendar entries for today should be displayed and the phone should speak them soon after. Now, it may not sound the way you expect - the TTS isn't perfect. But I think that's pretty good for a few minutes setting up some free scripting tools.

I would like to extend a very big thank you to Jason Holt and Tom Miller. Their encouragement and advice made this possible. Thanks, guys.

7 comments:

  1. I installed the apk but can not find the ase directory on sd, weird.

    ReplyDelete
  2. Matt,

    That does seem weird. Does ASE run? Can you find the ase directory if you put the card in a card reader?

    ReplyDelete
  3. Isaac, yeah ASE runs from the SD card installation. I don't have a card reader with me right now but I'll try checking that later. Thanks.

    ReplyDelete
  4. sl4a moves a lot of the SDcard directories around for these scripts

    ReplyDelete
  5. Brian, I'm sure you're correct. A lot has changed with sl4a in the last few months. I've been otherwise occupied, but I do hope to revisit these scripts at some point at do an updated post.

    ReplyDelete
  6. I downloaded googlecl 0.9.12 and followed your installation procedure. Ran couple times and failed due to "could not locate runnable browser". Review webbrowser.py and add the following line in the /sdcard/.config/google/config file:

    auth_browser = '/data/local/mybrowser.sh %s &'

    Next, found another error that you need to create the following directory to allow google.py to create access_tok_[username] file:

    /sdcard/.local/share/googlecl

    What's in my mybrowser.sh:

    #!/system/bin/sh
    am start -a android.intent.action.VIEW -d $*

    Executed the following command once python environment settings had been set:

    /data/data/com.googlecode.pythonforandroid/files/python/bin/python /sdcard/sl4a/scripts/google.py

    For the first time, at the prompt, type docs list. The output prompted an url (didn't launch mybrowser.sh though?); copy and paste the url to any local web browser. Then authenticated services at your Google account. Back to google.py, hit return and I got a list of docs displayed.

    ReplyDelete
  7. Is anyone still following this thread? I am interested if anyone got this correctly I am interested in trying but its not working for me.

    ReplyDelete