Rate this page del.icio.us  Digg slashdot StumbleUpon

Building the XO: The Anatomy of an Activity

by Greg DeKoenigsberg

Last time, we talked about installing Sugar so that you could emulate the OLPC environment on your system. Now it’s time to explore how activities work on the XO.

Finding your activities

Let’s assume that you’ve installed Sugar on your system in /tmp/SUGAR. One level down, you’ll find a directory called build, which replicates much of the file system of the XO — or at least the parts that Sugar needs to work. The activities themselves would be found in /tmp/SUGAR/build/share/activities. This corresponds to the path /usr/share/activities on an actual XO.

Here’s a list of the activities in my latest build of Sugar:

[gdk@localhost activities]$ pwd
[gdk@localhost activities]$ ll
total 104
drwxrwxr-x 3 gdk gdk 4096 Mar 17 18:43 BlockParty.activity
drwxrwxr-x 6 gdk gdk 4096 Mar 8 12:24 Develop.activity
drwxrwxr-x 3 gdk gdk 4096 Mar 7 14:42 Etoys.activity
drwxrwxr-x 3 gdk gdk 4096 Mar 8 19:03 GroupChat.activity
drwxr-xr-x 3 gdk gdk 4096 Mar 8 18:34 HelloWorld.activity
drwxrwxr-x 3 gdk gdk 4096 Mar 7 14:43 Journal.activity
drwxrwxr-x 5 gdk gdk 4096 Mar 17 18:45 Kye.activity
drwxrwxr-x 6 gdk gdk 4096 Mar 17 18:36 Memosono.activity
drwxrwxr-x 7 gdk gdk 4096 Mar 7 14:43 News Reader.activity
drwxrwxr-x 11 gdk gdk 4096 Mar 7 14:43 TamTam.activity
drwxrwxr-x 3 gdk gdk 4096 Mar 17 18:41 Web.activity
drwxrwxr-x 3 gdk gdk 4096 Mar 17 18:44 Write.activity
drwxrwxr-x 3 gdk gdk 4096 Mar 7 14:43 Xbook.activity

Anatomy of an activity: HelloWorld

Activities are structured very simply. That’s by design. The simpler the activity structure, the more likely it is that kids will be able to modify existing activities and create new ones. Let’s take a quick look at the simplest of all activities: HelloWorld.

[gdk@localhost activities]$ ll HelloWorld.activity/
total 32
drwxr-xr-x 2 gdk gdk 4096 Mar 20 11:51 activity
-rw-r--r-- 1 gdk gdk 993 Mar 20 11:19 HelloWorldActivity.py
-rw-r--r-- 1 gdk gdk 1353 Mar 21 10:47 HelloWorldActivity.pyc
-rw-rw-r-- 1 gdk gdk 843 Mar 21 15:00 setup.py

This is the basic structure of an activity. There’s the activity code itself, in this case HelloWorldActivity.py; there’s a boilerplate setup.py script that bundles the activity for redistribution; and then there’s the activity directory, where all metadata about the activity is stored. Looking into the activity, we see:

[gdk@localhost activities]$ ll HelloWorld.activity/activity/
total 16
-rw-r--r-- 1 gdk gdk 2350 Mar 8 12:09 activity-helloworld.svg
-rw-r--r-- 1 gdk gdk 186 Mar 8 12:23 activity.info

The icon for the activity is kept here in SVG format. This is the icon that shows up in the Sugar frame, and in the activity ring once the activity has been started.

HelloWorld in the activity ring.

We can see the metadata about the HelloWorld activity in the activity.info file:

name = HelloWorld
service_name = com.ywwg.HelloWorldActivity
class = HelloWorldActivity.HelloWorldActivity
icon = activity-helloworld
activity_version = 2
show_launcher = yes

Let’s examine the activity.info file line by line:

  • name is obvious enough.
  • service_name uniquely identifies this activity with a namespace identifier, which should be particularly familiar to Java developers. One should also perhaps expect to find the latest version of the activity at the listed namespace, but this has not yet been formalized.
  • class refers to the class that will be invoked when the activity is started from the frame; in this case, we see that Sugar will look in HelloWorldActivity.py for the Python class HelloWorldActivity.
  • icon specifies the icon file to be used: in this case, the activity-helloworld.svg file we saw previously.
  • For simplicity’s sake, activity_version will always be an integer. If two activities are trying to share, and one has a greater activity version, the old activity should simply be able to replace itself with the new activity.
  • show_launcher specifies whether or not the activity will be available in the Sugar frame. As we see here, HelloWorld is available.

Next, let’s examine the activity code itself from HelloWorldActivity.py, with lots of comments inline in the code. If you’re familiar with PyGTK — and if you want to write Sugar activities, you should be — then a lot of this code will look familiar. To learn more about PyGTK, check out the excellent tutorial at pygtk.org.

# Hello World opens a big window in Sugar. When the user clicks the button,
# the text "Hello World" appears in the Sugar log files.
import logging
from sugar.activity import activity
import sys, os
import gtk

class HelloWorldActivity(activity.Activity):
    # hello is the function that writes 'Hello World' into the Sugar log.
    def hello(self, widget, data=None):
        logging.info('Hello World')
    def __init__(self, handle):
        activity.Activity.__init__(self, handle)
        # Creates a new button with the label "Hello World".
        self.button = gtk.Button("Hello World")
        # When the button receives the "clicked" signal, it will call the
        # function hello() passing it None as its argument. The hello()
        # function is defined above.
        self.button.connect("clicked", self.hello, None)
        # This packs the button into ourselves (a Sugar window).
        # The final step is to display this newly created widget.
        self.set_title('Hello World')

HelloWorld activity with frame view. The whole screen is a clickable button.

Note that logs can be viewed in the developer console (Alt-0 opens it). The developer console is very useful, with all logs kept in one place. We can see the entry for the button push in the HelloWorld log:

Developer console for Sugar.

It’s useful to have access to the logs after your Sugar session is over, though. Logs are kept in the .sugar directory in your homedir:

[gdk@localhost activities]$ ll ~/.sugar/default/logs/
total 32
-rw-r--r-- 1 gdk gdk 35 Mar 21 15:06 clipboard.log
-rw-r--r-- 1 gdk gdk 38 Mar 21 15:06 HelloWorld.log
-rw-r--r-- 1 gdk gdk 871 Mar 21 15:06 presenceservice.log
-rw-rw-r-- 1 gdk gdk 931 Mar 21 15:06 shell.log
[gdk@localhost activities]$ cat ~/.sugar/default/logs/HelloWorld.log
INFO - Hello World

Things to Keep in Mind

So what’s the difference, then, between writing a Sugar activity and a PyGTK desktop application? Not a lot of difference, really — but there are a couple of key points to remember.

First of all, a Sugar activity has no dependencies on other activities. None. At all. As any user of RPM and/or dpkg knows, dependencies bring bloat. At a svelte 128M of memory, bloat is a luxury that the XO cannot afford. Besides, dependency resolution is an unwelcome bump in the user experience. If a kid wants to run that cool music activity right now, the last thing he should have to see is a dialogue that says, “coolmusicactivity requires sdl and csound-devel: download now?”

There are some tradeoffs to this approach. It means that the activity developer is strongly tied to the libraries that ship with the XO by default; if particular libraries are not available, the developer must ship them as part of the activity. One might argue that this approach causes bloat as well, since the same library might be present in a whole bunch of different activities. Ideally, that shouldn’t be much of a problem; it should be simple to discard activities that aren’t being used, and then download them again when they are needed. That’s the goal, anyway.

The second thing to consider is the fundamental interactivity requirement for activities. Activities should be designed to be shared, and the process for sharing should be trivial. If a child sees a friend playing a neat new game, she should be able to click on that friend in her neighborhood view, receive her game code, and join in the game, immediately. In fact, Sugar practically demands this “share-ability”, as we see from the following graphic:

Share HelloWorld with a friend (even though the code doesn’t know how.)

Our version of HelloWorld can not currently be shared. There’s no code in it that allows sharing. But the “share” link is still present, and will always be present — which means that, ultimately, a good “HelloWorld” example will include sharing.

We’re not quite there yet. A lot of the details around activity sharing are still being hammered out, but when those details are worked out, we’ll bring them to you.

Next time, we’ll look at an activity with a bit more meat. Until then, happy hacking. In the meantime, here’s some great reading about Sugar from the OLPC wiki:

7 responses to “Building the XO: The Anatomy of an Activity”

  1. Nick says:

    I am running build 303 in emulation (QEMU). Is there an easy way I can use this emulated session to store and test an activity?

  2. gregdek says:

    I haven’t played much with QEMU myself, so I don’t know much about moving files between guest and host — but from the cursory research I’ve done, it ain’t exactly simple. Does networking work in your QEMU instance? There should be an ssh client in build 303.

  3. Mandell says:

    What the heck is XO? Spelling out OLPC at least once would be nice, too. Clarity is a good thing!

  4. Nick says:

    Networking works great under QEMU. I can easily use ssh, or even using the browser… I was wondering if there is some easier way…

  5. Nick says:

    I found a way, maybe not the most elegant, but it works and it’s fairly easy. I do all my development in the host, then I post it in my webserver. I then download it with the browser in sugar, and I used it from there….

  6. Racing says:

    Networking works great under QEMU. I can easily use ssh, or even using the browser… I was wondering if there is some easier way…

  7. James Simmons says:

    I have been trying to create an Activity on my XO laptop. What I want this activity to do is be launched from the Journal, just like the Read activity is. I’ll associate files in the Journal with a MIME type so that when the user resumes files of this type my application will be launched. That part I have working. I also have a Python script that works standalone from the terminal window, so that’s working. I’ve adapted the script so that it is a launchable activity. What I’m missing is a way to get the path of the file that I used to launch the activity. With that path I can open the file and do what I need to do; without it I’m stuck.

    This seems like such an obvious question I thought there must be a tutorial somewhere explaining it but I’ve searched and searched and found nothing. Every damned tutorial assumes you will never read or write a file.

    Can someone point me to the information I need? Thanks.

    James Simmons