It’s been a long time, but hey, you know what work’s like — yeah, I’ve been busy busy busy.  It’s about time I got back to some play time, eh?

Anyway, I wanted to demonstrate some of the new dialog features in ASE (Android Scripting Environment), and at the time of writing this is from ase_r25.apk release.

I was pondering what to build, and then thought of a simple news reader, something that you can modify to view any RSS feed, but initially to demonstrate it with BBC News.

Now, one thing to remember about coding in Python with ASE is that it’s still Python.  Yeah, stating the obvious, I know, but what that means is you can use all those Python modules that are already out there.  Ok, there’s some restrictions based on whether the modules call underlying C code, and if so you need to do some cross-compiling of your own.  But other than that, if you have a pure Python module, you can include it in your code very easily.

Now, there’s a nice module called feedparser which is great for parsing RSS and Atom feeds.  All you have to do to use it within your ASE Python script is to copy the feedparser.py to your ASE extras directory on your Android, e.g. /sdcard/ase/extras/python/feedparser.py

Once the file is there, you can reference it just like any other Python module with a simple import.

import feedparser

There’s some great examples on the feedparser.org site, so I won’t repeat those here.  Instead I’ll dive straight into the code, and I’ll pull out interesting sections afterwards.

The BBC News site has an RSS feed that anyone can freely subscribe to, and as with the BBC’s usual excellent service they cover everything from World news, politics, sport, entertainment.  You name it, they’ll have it covered.  Of course, coming from the UK, I’m more interested in UK news, so that’s what I’ll be looking at in this code example.

The main RSS URL for the UK news is http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/uk/rss.xml and so that’s what we’ll use for our example.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
"""
Google Android Python Development example, using the Android Scripting Language (ASE).
This example uses dialog alerts and dialog lists, plus how to use an external python module
to read an RSS feed
Author: Howard Sandford, July 2010
Released under CC BY-SA-NC 3.0
"""


import android
import feedparser
import time

droid = android.Android()

def show_news_list():
    """
    @brief show a dialog on the screen with a list of news items
    @return int index of the selected news item
    """

    droid.dialogCreateAlert(channel_title, ' ')
    droid.dialogSetItems(news_items)
    droid.dialogShow()

    response = droid.dialogGetResponse().result
    selected_news_index = response['item']
    return selected_news_index

def show_news_summary(selected_news_index):
    """
    @brief Show a dialog with a brief synopsis of a particular news item
    @param[in] selected_news_index int The news index item to show
    @return string Representation of the button that was pressed e.g. "neutral", "positive", or "negative"
    """

    droid.dialogCreateAlert(
        news_items[selected_news_index],            
        news_summaries[selected_news_index]
    )

    droid.dialogSetPositiveButtonText('Read News')
    droid.dialogSetNegativeButtonText('Back')
    droid.dialogSetNeutralButtonText('Done')
    droid.dialogShow()

    response = droid.dialogGetResponse().result
    return response['which']

def remove_news_item(selected_news_index):
    """
    @brief Remove a news item from our data structures
    @param[in] selected_news_index int The news item index to remove
    """

    # once we've shown one news item,
    # we don't need to see it again
    news_items.remove(news_items[selected_news_index])
    news_summaries.remove(news_summaries[selected_news_index])
    news_urls.remove(news_urls[selected_news_index])


# Let the user know that something is happening
droid.dialogCreateSpinnerProgress('Please wait', 'Downloading news items from the BBC news website ...', 100)
droid.dialogShow()

# set up the feedparser to look at the BBC news site
news_feed = feedparser.parse("http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/uk/rss.xml")

# initialise our data structures, ready for the parsed RSS feed
news_items = []
news_urls = []
news_summaries = []
show_news_items = True

# grab the title of the feed
channel_title = news_feed.feed.title

# sort through our data
for news_item in news_feed.entries:
    news_items.append(news_item.title)
    news_urls.append(news_item.id)
    news_summaries.append(news_item.summary)

# we're done with the long process ... so stop the spinner
droid.dialogDismiss()

while (show_news_items):
    try:
        selected_news_index = show_news_list()
    except KeyError:
        # the user pressed the back button on the Android
        show_news_items = False

        # and clear any selected item
        selected_news_index = None      

    try:
        button_pressed = show_news_summary(selected_news_index)
        if button_pressed == 'positive':
            # open up the URL (in the browser)
            # making sure we open the mobile version of the news rather than the hi bandwidth version
            droid.view(news_urls[selected_news_index].replace('/hi/', '/mobile/'))

            # we don't want to see this news item again, so remove it from the list
            remove_news_item(selected_news_index)

            # here's the hack!
            time.sleep(30)

            # and kill the browser so we return to our script
            droid.forceStopPackage('com.android.browser')

        elif button_pressed == 'negative':
            # just remove the news item, as we're done with it
            remove_news_item(selected_news_index)

        elif button_pressed == 'neutral':
            show_news_items = False

    except KeyError:
        # the user pressed the back button on the Android
        remove_news_item(selected_news_index)

    except NameError:
        # raised because of the previous back button press
        # so we just want to stop - show_news_items is already false
        pass

    except TypeError:
        # raised because of user pressed back twice
        # so we just want to stop - show_news_items is already false
        pass

# and finish
droid.exit()

So let’s start dissecting this code a little more.

The main part of the script starts on line #61

61
62
63
64
65
66
# Let the user know that something is happening
droid.dialogCreateSpinnerProgress('Please wait', 'Downloading news items from the BBC news website ...', 100)
droid.dialogShow()

# set up the feedparser to look at the BBC news site
news_feed = feedparser.parse("http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/uk/rss.xml")

We’ve seen this before. It’s simply a spinner that shows the user that some long process is happening, and in this case we’re showing this whilst we download the BBC news feed, which is easy to do thanks to the feedparser module.

68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# initialise our data structures, ready for the parsed RSS feed
news_items = []
news_urls = []
news_summaries = []
show_news_items = True

# grab the title of the feed
channel_title = news_feed.feed.title

# sort through our data
for news_item in news_feed.entries:
    news_items.append(news_item.title)
    news_urls.append(news_item.id)
    news_summaries.append(news_item.summary)

# we're done with the long process ... so stop the spinner
droid.dialogDismiss()

We then initialise our python list data structures where we store the parsed news stories, and then run through each item in turn, assigning titles, urls and summaries of each news item.

Once we’ve got all the data we need, we dismiss the spinner dialog from the screen.

Now we get to the fun stuff from ASE. We create a dialog alert which contains a list of items, in our case the titles of news items given to us by parsing the RSS feed.

20
21
22
droid.dialogCreateAlert(channel_title, ' ')
droid.dialogSetItems(news_items)
droid.dialogShow()

The dialog simply takes the Python list, news_items, and displays each element as an item in the ASE list.

We determine which item the user selects from the droid.dialogGetResponse() call.

24
25
26
response = droid.dialogGetResponse().result
selected_news_index = response['item']
return selected_news_index

It’s possible that the selection fails with a KeyError, i.e. ‘item’ is not in the list. This can happen if the user presses the “back” button on the Android, so we catch this in a try/except. Obviously, if the user has pressed back, we make sure we don’t show any items and remove any selection.

85
86
87
88
89
90
91
92
try:
    selected_news_index = show_news_list()
except KeyError:
    # the user pressed the back button on the Android
    show_news_items = False

    # and clear any selected item
    selected_news_index = None

Ok, so then, with a news item selected, we want to show the details of that news item to the user. Well, when I say details, I mean a summary of the news story. The ASE dialog calls allow us to set up to three buttons. In our example we’ve used the “positive” button to open up a web-browser to see the full version of the news, the “negative” button to go back to the list of news items, and the “neutral” button “quits” out of the application.

34
35
36
37
38
39
40
41
42
43
44
45
droid.dialogCreateAlert(
    news_items[selected_news_index],
    news_summaries[selected_news_index]
)

droid.dialogSetPositiveButtonText('Read News')
droid.dialogSetNegativeButtonText('Back')
droid.dialogSetNeutralButtonText('Done')
droid.dialogShow()

response = droid.dialogGetResponse().result
return response['which']

Now, here comes the hack. When we open up the web-browser to see the full story, it would be great to wait in the background for when the web-browser finishes and then we take over from where we left off. However, ASE doesn’t yet (as of ase_r25.apk) provide the startActivityForResult call which allows us to open another activity and wait for a result. [Ed: since writing, ASE has been renamed Scripting Layer for Android – SL4A – and the latest version sl4a_r2.apk implements the startActivityForResult call] As such, we set up a timer which kills the web-browser after 30 seconds. This is fine for me, and you might want to adjust this timeout to suit your own purposes. [Ed: either that, or rewrite it using startActivityForResult!]

96
97
98
99
100
101
102
103
104
105
106
107
108
if button_pressed == 'positive':
    # open up the URL (in the browser)
    # making sure we open the mobile version of the news rather than the hi bandwidth version
    droid.view(news_urls[selected_news_index].replace('/hi/', '/mobile/'))

    # we don't want to see this news item again, so remove it from the list
    remove_news_item(selected_news_index)

    # here's the hack!
    time.sleep(30)

    # and kill the browser so we return to our script
    droid.forceStopPackage('com.android.browser')

Incidentally, when we open the webpage with the full news item, we simply change “/hi/” to “/mobile/” in the URL because the BBC News website provides both a hi definition and a mobile version of its news items. Nice.

So there we have it, an RSS news reader on the Android using the ASE Android Scripting Environment in Python.

Next time I’ll make sure I download the latest SL4A and work from that release.

[All files are released under Creative Commons BY-SA-NC 3.0]

One thought on “Google Android Python Scripting ASE example — an RSS News Reader

  1. So as a quick update, having taken a quick look at SL4A, it’s OSSM!

    Seriously. Go and have a look, it gives you a prefect environment to use for scripting your Android.

    Anyway, if you use SL4A, here’s a few changes you need to make to the script.

    First of all, the feedparser.py module needs to be moved to the following directory on your Android:

    /sdcard/com.googlecode.pythonforandroid/extras/python/

    which you can achieve by running the command:

    $ adb -e push feedparser.py /sdcard/com.googlecode.pythonforandroid/extras/python/

    And instead of using the hack (or even using startActivityForResult) you can simply delete the call to sleep in line 105, and replace the droid.view line with the following:

    99
    droid.webViewShow(news_urls[selected_news_index].replace('/hi/', '/mobile/'), True)

    which opens a web view with the passed in URL, and the True parameter tells the script to block until the web view has returned.

    Absolute magic from the guys who have worked really hard on SL4A

Leave a Reply

Your email address will not be published. Required fields are marked *