All Unkept
Posted in: Python, Linux  —  March 11, 2013 at 03:37 PM

Controlling Sozi remotely from an Android phone

by Luke Plant

I'm due to give a talk next Sunday evening, and I've created a presentation using the very cool Sozi. Sozi is quite like Prezi, but open source and free, and its output is a standard SVG file (with a bit of Javascript), so it can be displayed in several web browsers.

(On the other hand, for many people Sozi is probably a lot harder to use for simple presentations than Prezi, as it works as a plugin to Inkscape, a general purpose drawing program, but, to me, it is simply unacceptable to depend on the continued existence of a company, and a subscription fee, to be able to continue to view/edit your presentations).

One problem with not using ‘standard’ software like Powerpoint is that proprietary tools like hand-held remote controls for Powerpoint won't work, or won't work easily (and I don't want to find out that it won't work when I turn up). But I've found a solution that works for me:

  1. On my Android phone, I installed Coversal, which is an app designed for controlling media players, but allows custom commands to be executed, so actually works fine for presentations.

  2. Within Coversal, I installed the ‘SSH Custom’ plugin.

  3. On my Linux laptop, I installed openssh-server to allow the app to log in to my machine. To avoid putting my laptop password on the phone, I created an SSH keypair for this app, transferred to private key to the phone, and added the public key to ~/.ssh/authorized_keys.

  4. I created a script on my laptop that can control Sozi running in Chrome:

    #!/usr/bin/env python
    
    # controlsozi
    
    import os
    import subprocess
    import sys
    import logging
    
    from datetime import datetime
    
    logging.basicConfig(level=logging.DEBUG, filename='/tmp/controlsozi.log', filemode="a")
    
    def get_window():
        # Assumes that there is just a single Google Chrome window open, and this
        # has the presentation in it.
        windows = subprocess.check_output("DISPLAY=:0 wmctrl -l -x | egrep 'google-chrome|gnome-www-browser'", shell=True)
        return windows.strip().split()[0]
    
    def send_text(text):
        os.system("DISPLAY=:0 xvkbd -window %s -text '%s'" % (get_window(), text))
    
    ACTIONS = {
        'previous':      "\[Left]",
        'next':          "\[Right]",
        'fullscreen':    "\[F11]",
        'previous_fast': "\[Up]",
        'next_fast':     "\[Down]",
        'start':         "\[Home]",
        'end':           "\[End]",
        'launch':        None,
    }
    
    def log_uncaught_exceptions(*exc_info):
        logging.critical('Unhandled exception:', exc_info=exc_info)
    sys.excepthook = log_uncaught_exceptions
    
    
    if __name__ == '__main__':
        logging.debug("Starting...")
        if len(sys.argv) < 2:
            print "Usage: "
            for k in ACTIONS.keys():
                print "  " + k
        else:
            action = sys.argv[1]
            logging.debug("Action = %s" % action)
            if action == "launch":
                os.system("DISPLAY=:0 nohup google-chrome '%s' &" % sys.argv[2])
            else:
                text = ACTIONS[action]
                logging.debug("Text = %s" % text)
                send_text(text)
    
        logging.debug("Done")
    

    This relies on command-line tools wmctrl and xvkbd. With only small modifications it should work for Firefox too. It is not 100% reliable, as it works by sending keypresses to windows, but it works fine for me.

  5. I set up a profile in Coversal to connect to my laptop and use this script.

    Coversal allows you to define the commands for certain actions — the list of actions includes things like up, right, next etc., but you can add your own. You then assign actions to buttons, as desired.

    So I simply I had to set commands like "/path/to/controlsozi next", and then assign these actions to the buttons I wanted to use.

    By setting the start_playback command to "/path/to/controlsozi launch '%s'", I could even use the file browser built in to Coversal to select the presentation file to view, which is pretty neat.

    The one thing I was really missing was physical buttons — with a touch screen, it's so easy to hit the wrong place, and I wanted something to rest my finger or thumb on.

    I then realised that my phone does have a couple of physical buttons - the volume control buttons on the side. By enabling the 'volume_up' and 'volume_down' buttons in Coversal and mapping them to the 'next' and 'previous' commands, the physical volume buttons get mapped to the same actions, and they do the job just fine. I can use the rest of the controls on the screen for the less common tasks.

    /blogmedia/2013-03-14-coversal.png

For my phone to be able to connect to my laptop over SSH, there is one more piece of configuration that must be done — the IP address of the laptop has to be entered. If you have a local wifi, this is no problem — just connect the two devices to the same wifi and they will be able to talk. My phone has a more reliable option, though — it can itself operate as a wifi hotspot, creating a wifi network that the laptop can connect to.

Conclusions:

  1. Linux is awesome.
  2. Smart phones are awesome.
  3. Free Android apps are awesome (and I think it is the Linux culture which encourages this).
  4. Python is awesome.
  5. Being a programmer and being able to plug all this stuff together is awesome.

Comments §

blog comments powered by Disqus