Tuesday, December 29, 2009

Talking Twitter client

So I learnt this afternoon about this Linux utility called festival. It's a text to speech conversion program. Running it is as simple as

echo "Hello world" | festival --tts

Moreover, installing it on Fedora is as easy as

sudo yum install -y festival

After that, a bit of a bash and a bit of a python and I had a twit-to-speech utility running.

The code is simply this much:

#!/bin/bash

TWITTERURL="http://twitter.com/statuses/friends_timeline.json"
JSON="/tmp/twittline.json"
SPEECH="/tmp/twt.message"
PYCODE="/tmp/twt2speech.py"

read -p "Username: " TUSER && \
read -sp "Password: " TPASS && \
curl -s -u $TUSER:$TPASS $TWITTERURL > $JSON

cat > $PYCODE << "EOF"
import json
import sys
import re
urlp="(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]"
twits = json.load(open(sys.argv[1]))
for twit in twits:
    text = twit['user']['name']+' says: '+twit['text']
    text = re.sub(urlp, '', text) 
    print text
EOF

python $PYCODE $JSON > $SPEECH

while read line
do
notify-send -t 15000 "$line"
echo $line | festival --tts
sleep 1
done < $SPEECH

echo "THE END" | festival --tts

code syntax highlighting by GVIM

The above script will ask your twitter credentials, fetch latest 20 twits in friends' timeline, save into a JSON file. A short python script parses the JSON, extracts twit text and user's name from it and outputs in a sanitized format (it removes URLs, because there is no use hearing them).

The sanitized output is  saved in another text file, which is piped one line at a time to festival. In case the speech is not clear, it also shows the text in a pop-up using notify-send.

A full script with some error checking can be found here.

Saturday, December 26, 2009

ReaderScope 1.5 - quicker, better, prettier

ReaderScope 1.5 is ready for the holidays. With the latest improvements it gives the best Google Reader experience out there for your Android phone.

ReaderScope now uses non-blocking UI and the items are now loaded progressively. What does that mean? See it yourself.



As you know, the app is free in various markets. The themes (including the 'Santa' theme in above video) are part of a Theme Pack, which can be purchased for a ONLY $2.49 $3.49

Big thanks to the beta testers who helped testing the unstable 1.5 builds for over a week. Thank you guys!

Also if you like ReaderScope, vote for it at Android Arcade Facebook app.

Tuesday, December 15, 2009

Preview android.R.drawable.* images

This morning I was going through the Android SDK docs- specifically android.R.drawable class, to find out what stock icons android has that I can use in my project. Android has lot of icons in this class and one is encouraged to use stock icons whenever possible.

However I couldn't find any way to see how these icons look like. Their names are supposed to tell something about them, but they are vaguely descriptive at best. So I asked around on stackoverflow for help. I got a helpful link, but it is still a subset of all the drawables.

After a bit of thinking, I figured java reflection can help here and I put some code together. I ended up with a simple app which lists all the icons alongside their names. So now when you want to check out how a particular android.R.drawable.* looks like just scroll through this list.


Get the tarball and the apk. For the basic code snippet, check out my reply on stackoverflow.

Ads:
Professional Android 2 Application Development
The Busy Coder's Guide to Advanced Android Development
Unlocking Android: A Developer's Guide

Sunday, December 13, 2009

on Music: Battle Studies

Past couple of weeks I have been listening to John Mayer's (@johncmayer) latest album - Battle Studies. I am huge fan of JM music. His last album Continuum became my favorite when I listened to it non-stop for three days on a cross country road trip. (You can hear those songs in background in these trip videos).

I love the lyrics among all things. They have deeper meanings which you only understand after listening to them over long period of times. Maybe those meanings are not intentional, maybe we form our own meanings depending on the context or situation we hear them in. I believe that's the sign of true art. A true artwork does not convey a single full baked meaning. Instead, it provokes the audiences' mind to derive their own meanings.



I loved the "Who Says" video at first. Then when I got hold of the entire album, I found other gems like "Heartbreak Warfare" and "All we ever do is say goodbye".



I was more thrilled when I found a collection of JM's guitar tabs from all his albums at ultimate-guitar. I never knew such thing existed. Although, I have never played Guitar, but always hoped to learn it one day, this collection of tabs looked like a solid incentive to work on that plan. If you are serious about Guitar, these youtube videos are also a great source for inspiration and guidance.






So if you are looking for music recommendations, try JM.

Friday, December 11, 2009

ReaderScope 1.4 (with a Theme Pack)

Yes, now you don't have to stare at the black and khaki interface of ReaderScope. With 1.4, ReaderScope adds a theme pack. Right now it comes with three themes which look far more pleasant than the legacy color scheme. Also the Theme Pack is the first premium add-on to ReaderScope. You can buy it for only $3.49, from here. You can see the preview in the Theme switcher, just go to Settings -> Themes.



In future more themes will be added to the theme pack and you can get access to all of them, for just $3.49.

Go on, give it a try.

Thursday, November 19, 2009

On Chrome OS

Last night (IST) Google presented the Chrome OS details and opened the source code. I did some early twits looking inside the source code. Found the bits of clutter, debian, moblin down under there. My point of view was traditional. What a long time Linux user sees in a brand new OS - Window Manager, Package manager, RPC mechanism, etc. This was as soon as @mattcutts had posted that chromium source was out and before the actual presentation even started. When I however saw the presentation (vicariously that is - through the glasses of many tweeters and Gizmodo's live report), I found that, what I was looking for in the OS was totally irrelevant ...

If you haven't seen the ChromeOS presentation videos, do check them out.

I won't go over all the features here, but this is what I liked the most

1. Security. Automatic patching of OS everytime it boots. I liked the thinking behind this model.
The basic application security model was developed 20 years ago. Right now, the application runs with “your” privileges, so if you can modify something, the app you’re running can do it also. So a rogue app can damage your data or leak your data. It also forces users to make hard decisions about whether or not you trust the application. “These are decisions that are hard for normal users to make decisions about.”

- as reported by Jason Chen of Gizmodo

2. Local file system. It is treated merely as a cache before the settings are pushed up into the cloud. It is completely encrypted, so in case of device loss, there is no fear of data theft.

3. Only SSDs. By the time first ChromeOS devices will ship, SSDs will be commonplace. Besides it seems the cloud will make local storage less important than the network connection. Also only supporting this very limited storage hardware, eases the driver support. Good move.

4. Firmware. When I first studied about the LinuxBIOS project, I came to realize how wasteful the booting process is. The BIOS initializes the hardware, reads the kernel from the storage device, starts the kernel and the kernel initializes the hardware again. The root of this setup is in the way our computers evolved and not much in the form of technical challenge. ChromeOS is going to support only specific firmware. Their boot process is tuned so that no redundant initalization steps are required, thus improving the boot time greatly. As I said, this is not a new invention. Projects like LinuxBIOS had the same implementation, but only on the hacked up devices in their labs. Now however, with Google putting its weight behind this new family of ChromeOS devices, it seems that we will be freed from the wasteful boot up process after all.

Putting things in perspective... I have liked Apple's devices. Simple, elegant, functional, work out of the box. Why is that so? Why don't they freeze once in a while or give BSOD like Windows. Why don't they have configuration problems like Linux, after all at the core they have a community developed open source kernel just like linux. The reason lies in Apple completely owning the entire OS stack. They only design, implement, test, debug and deliver for a specific set of hardware. That's why they can concentrate all their design and engineering skills into making the best computing devices in the market.

I think Google is going to do the same with ChromeOS. Except in their case, the kernel will be Linux and Google won't be "owning" the stack, but will be tightly controlling it. Most of the stack will be open source, for anyone to look at and adapt to their devices if they want to. They will however have complete control on what goes and doesn't go in the official ChromeOS, just like Android (and that is entirely fair and necessary IMO, no matter what FOSS extremists say). On top of this (and most importantly), they will have a unique cloud based computing vision in building these next generation devices.

I like the way Google uses its power in completely reinventing certain fields with their products. ChromeOS is yet another example and worthy of cheer at that.

Android AsyncTask template

Any meaningful GUI application needs to employ multiple threads, otherwise it is very likely to lock while doing any I/O operations - leaving bad impression on the user. Same is true for Android apps. In fact, android framework prompts user to close the app if it doesn't respond in 10 seconds or so. So it's absolutely essential to perform any task - that has even a remote possibility of taking a bit longer - in a background thread.

Fortunately Android framework has some useful constructs built into the framework that make our job relatively easy while doing multithreaded GUI programming. android.os.AsyncTask is one of them.

The basic steps to make your app multithreaded using AsyncTask are as follows:
1. Identify the code segment that you want to execute in background thread. (for e.g. code that does network i/o, bulk file transfer on local disk, etc)

2. Model it as an AsyncTask object.
(Subclass the AsyncTask object and put your code segment in the doInBackground(...) method)

3. Figure out what parameters you have to pass to the task, the result that the task should return and how to handle the exception in case something goes wrong.

This is pretty simple, but the third step gets bit complicated as your app grows and there are multiple asynchronous tasks that you want to code in.

When I was faced with this problem during the development of CuTewit, I stumbled through some steps. After a while I managed to create a template for modelling with AsyncTasks. In my second app - ReaderScope - the same template worked very nicely. ReaderScope has over 50 different tasks that are run on background thread at one time or another. The template I use, has made it pretty easy to add new AsyncTasks.

In this post, I want to share that template with you. I have written a small example app that demonstrates the use of the template. Download source tarball and apk to try it yourself.

Here is the source code of AppTask.java (the derivative of AsyncTask) for discussion.

package com.altcanvas.asynctemplate;

import android.os.AsyncTask;

public class AppTask extends AsyncTask<AppTask.Payload, Object, AppTask.Payload>

{
    public static final String TAG = "AppTask";

    public static final int APPTASK_1 = 1001;
    public static final int APPTASK_2 = 1002;

    /*
     * Runs on GUI thread
     */
    protected void onPreExecute() {
    }

    /*
     * Runs on GUI thread
     */
    public void onPostExecute(AppTask.Payload payload)
    {
        switch(payload.taskType) {

        case APPTASK_1:
            AsyncTemplateActivity app =
                (AsyncTemplateActivity) payload.data[0];

            if(payload.result != null) {

                // Present the result on success

                int answer = ((Integer) payload.result).intValue();
                app.taskStatus.setText("Success: answer = "+answer);

            } else {
                // Report the exception on failure

                String msg = (payload.exception !=null) ?
                                payload.exception.toString() : "";
                app.taskStatus.setText("Failure: error ="+msg);
            }

            break;

        case APPTASK_2:
            break;
        }
    }

    /*
     * Runs on GUI thread
     */
    public void onProgressUpdate(Object... value)

    {
        int type = ((Integer) value[0]).intValue();

        switch(type) {

        case APPTASK_1:
            AsyncTemplateActivity app = (AsyncTemplateActivity) value[1];
            int progress = ((Integer) value[2]).intValue();
            app.progressBar.setProgress(progress);
            break;

        case APPTASK_2:
            break;
        }

    }

    /*
     * Runs on background thread
     */
    public AppTask.Payload doInBackground(AppTask.Payload... params)

    {
        AppTask.Payload payload = params[0];

        try {
            switch(payload.taskType) {
            case APPTASK_1:

                // extract the parameters of the task from payload

                AsyncTemplateActivity app =
                    (AsyncTemplateActivity) payload.data[0];
                int numSteps = ((Integer) payload.data[1]).intValue();

                if(numSteps < 0) throw new AppException("Invalid input");

                // perform the task

                int progress = 0;
                for(; progress < numSteps; progress++) {
                    try {
                        // pretend to work for 1 second

                        Thread.currentThread().sleep(1000);
                    } catch(InterruptedException ie) {
                        break;
                    }
                    publishProgress(new Object[] {
                            new Integer(APPTASK_1), app, progress});
                }

                publishProgress(new Object[] {
                        new Integer(APPTASK_1), app, progress}); 
                // Return result of the task
                payload.result = new Integer(42);
                break;

            case APPTASK_2:
                break;
            }
        } catch(AppException ape) {
            payload.exception = ape;
            payload.result = null;
        }

        return payload;
    }

    public static class Payload
    {
        public int taskType;
        public Object[] data;
        public Object result;
        public Exception exception;

        public Payload(int taskType, Object[] data) {
            this.taskType = taskType;
            this.data = data;
        }
    }
}

code syntax highlighting by GVIM,

Let's go over different aspects of this code.

What is Payload?
To simplify the management of passing parameters and carrying result/exception back, we create a special object called Payload. Payload carries four entities - the type of the AsyncTask (int taskType), parameters to the task (Object[] data), result of the task (Object result), exception that took place during the task (Exception exception).

Note the signatures of doInBackground and onPostExecute. The same payload object is passed around.

How do you start a new task?

new AppTask().execute(new AppTask.Payload(
    AppTask.APPTASK_1,
    new Object[] { AsyncTemplateActivity.this,
    new Integer(numSteps) })); 
   
[You can find it in AsyncTemplateActivity.java in the sample app]

How do you define a new async task?
You don't have to create another AsyncTask derivative object. Just define a new taskType, e.g. APPTASK_2 in AppTask.java. Create a switch case for that taskType in each of 'doInBackground', 'onPostExecute', 'onProgressUpdate'. Depending upon the nature of the tasks, the contents of payload.data will vary. Since this data is passed as generic Object[], you can pass around any types of objects that suite the task's needs. On the other hand, you have to be careful to not type cast them to wrong classes.
[We loose Java's static type checking by defining params and result as generic 'Object's. But it saves us from repetitively defining similar AsyncTask derivatives]

How is the progress of the task updated?
When you call publishProgress(...), it results in a call to onProgressUpdate(...) which is running in the GUI thread. Here, you can manipulate any widget like progress bar to indicate the task progress.

...

This may not be the only way to code AsyncTask, but it certainly has worked for me - not just for one, but for two sizeable projects. Before this, it used to be pretty cumbersome to define a new task.

You can download the source code and use it in your app if you like.

Any comments and suggestions are welcome.


Flattr this



Ads:


Thursday, November 12, 2009

ReaderScope v1.3.3 - offline podcasts

ReaderScope primarily started as an Android app to access Google Reader from the phone. Over time however, various features were added as they proved complimentary to the act of news reading - like sharing the news with friends (Twitter, SMS, Email); bookmarking it (Del.icio.us); reading what others think about it (Digg, Reddit, FriendFeed); support for rich forms of news, i.e. podcasts and youtube videos, etc.

This release improves one of those fringe features, that of Podcasts. ReaderScope supports scheduled download of news items for reading offline. However that wasn't supported for podcasts yet. This release tries to address that problem a bit.

Now as you click on a podcast beacon, you will be asked whether you want to play the podcast right away or just download it for later listening.


If you choose to download, it will start downloading in the background (with progress in the notification). When the download is complete, you can listen to it by clicking on the notification. You can do this when you are offline too.

Furthermore, you can choose to download multiple podcasts simultaneously. They will download in parallel and will be accessible via their independent notifications.

You can listen to them one by one after they are downloaded. When the podcast ends or if you choose to stop it; the local downloaded file is automatically deleted freeing the storage it occupies.

How do you like it? Let me know.

Also in this release there are some bug fixes. There was a glitch in the Google News and Twitter news features. If you were to sync your feeds with "Thorough" option, the feeds under Google News and Social Channels would disappear. This release fixes that problem.

That's it for now. Upgrade to v1.3.3 and let me know if there's any problem.

Cheers!

Friday, November 06, 2009

ReaderScope v1.3.2 - Social Channels

Few months back I realized that all the stories I read in my slashdot or ars-technica feeds seemed kinda ... stale. Why? Because most of the interesting ones I had already read - through links posted by the people I follow on Twitter. It is now a cliché to say that Twitter is the fastest medium of spreading news. Consequently, it's the news channel that delivers the latest news. Moreover, it's the news that you care about most; because it is coming from the people you have chosen to follow.

So how can a modern news reader be complete without incorporating this real time news channel.

In v1.3.2, ReaderScope introduces Social Channels.


You will see a third tab "Social" when you start the upgraded ReaderScope. You will find a preset Twitter subscription there. When you tap on it, you will be asked to login to Twitter (if you haven't already). Once it gets Twitter credentials, you can tap it again and it will download you twitter timeline. It won't show you all the twits (that is for dedicated twitter clients). Instead, it will go through all the twits and will pick the ones that have embedded links in them. ReaderScope will fetch the stories/pictures/videos pointed to by those links and when it's done, they will be presented to you as news items (with the original twits as yellow subtext).



How do you like that?

You can also schedule the Twitter subscription for periodic download.

You will see lot of other improvements in this version too, especially the item list (it now has author and timestamp, right in the list). The news fetched from Google News channels, looks fresh (unread) now; instead of dull in color. The unread counts have been fixed too.

On Podcast front: the ones that take too long "Preparing" (because of long time in buffering over slow network) can now be stopped.

So download the upgrade and enjoy.

And yes... in case you haven't noticed yet, ReaderScope is now accepting donations. ReaderScope is available FREE of cost for everyone to use. Some users who are avid news readers, will see great value in it. While others may not. So it's up to you to decide what's ReaderScope worth to you. If you can express it in terms of $$, you will help towards further development of ReaderScope. You can find the donate button on this page

... better yet, let me paste that button right here. :)






Thanks for all your support. More features are coming to ReaderScope. Show your support and help it improve.

Cheers!

Saturday, October 31, 2009

ReaderScope v1.3 - Beyond Google Reader

Google Reader gives us choice in News Reading. With the advent of RSS aggregators, we got the power to subscribe to selective sources of news. That way we can fetch the up-to-date information from the selective sources that we prefer. This saves us time. We build a focused channel to acquire our daily diet of news.

However there is a flip side to that. What if an important news is unrolling that is outside the scope of our news channel. It's being reported by a publisher that we are not subscribed to. In that case we miss out the hotness of a news.

What if after the same routine of reading your regular list of feeds, you get bored. You miss that element of surprise that one gets by reading random headlines.

For the times like this, ReaderScope v1.3 introduces some new features that go beyond Google Reader.

You can now read "Google News" in a separate tab, alongside Google Reader feeds. The tab lists six different categories of news: Headlines, World News, Sports, Entertainment, Business, Sci/Tech. You can now stay up-to-date with the latest happenings in any of these areas.


In addition to that, ReaderScope now offers the latest Google Reader feature: "Popular Items". As the official Google Reader Blog says:

We use algorithms to find top-rising images, videos and pages from anywhere (not just your subscriptions), collect them in the new Popular items section and order them by what we think you'll like best. Now you don't have to be embarrassed about missing that hilarious video everyone is talking about — it should show up in your "Popular items" feed automatically.

Now you can access the same list from ReaderScope.




In addition, you can now access other lists that were absent in ReaderScope untils now: "All Items", "Starred", "Shared".

You may feel that there is not enough space on screen for all this stuff. If you feel so, you can customize the elements to get more space. The tab panel that lets you choose between Google news and Reader, disappears automatically as you start browsing the news. In the Google Reader's feed list, if you don't want the extra labels, just go to "Settings -> Customize Layout" and you will be able to rearrange all of them.

And yes, ReaderScope is now Android 1.6 ready - for bigger and smaller screens.

So update your copy and enjoy!

Wednesday, October 28, 2009

Frets on Fire for Maemo - Update

Back in March, when Fremantle SDK was released, I had ported Frets on Fire to it. Subsequent discussion made it clear that modifications were required to actually make it run on the device. I created a stripped down version which could run without the OpenGL code. This made it suitable for n900 (and even n810). But due to lack of actual device, I couldn't test it.

Fast forward 7 months. Now I have access to actual n900, so recently I went back to the FoF code. After couple weeks of getting re-acquainted with the code, I have it satisfactorily running on the n900.

Frets-On-Fire code primarily has three parts -
  • playing the song (loading song, computing notes, etc. uses pygame for audio),
  • showing the visuals (PyOpenGL+pygame code) and
  • handling input.
All these components are pretty involved and hard to rewrite - especially so with the audio part (I hardly have any experience in musical notes and midi files). So my priority was to get almost all of the audio code as it is. It was easy to tweak the input code - to make it suite the n900 keyboard. I bypassed the visual part completely - replaced OpenGL with simple pygame sprites. (You may get disappointed by the stark looks).

I have created a project on googlecode to host the code - maemofof. It shows all the changes I had to make, starting from the base Frets-on-Fire code from v1.3.110.

The next step is to package it nicely and push to extras-devel. There are few problems with packaging though.

I couldn't get ogg files to play through pygame. I installed the ogg-support package, but for pygame/sdl to work with it, something more is required - which wouldn't install due to broken dependencies (libsdl-mixeroggwav1.2-dev). So for now I converted all ogg files to wav. But the wav files are huge (~80MB if a single song is included as sample). That makes them inconvenient to package and they also take a long time to load (~90 seconds).

But good news is, the ball got rolling again. I hope to make some progress in coming days. If you have any suggestions, feel free to chime in comments.

Saturday, October 24, 2009

ReaderScope 1.2.7 - Podcasts, Expanded View

Podcast support was a feature request in one of the market comments. I hadn't thought about it before that. When I looked into how to do it, it turned out to be pretty simple - thanks to Android's easy MediaPlayer API.

If you have subscribed to any podcast in your Google Reader, open it in ReaderScope just like any other feed. When you open a news item that has podcast attached, you will see a speaker icon near the bottom right corner, as shown in the following picture.

Just tap on it. You will see a notification indicating that the Podcast has started playing in the background.


The notification shows the progress through the podcast. If you want to pause or stop it, just tap on the Notification and you will be presented with the options. You can read your other news or even exit ReaderScope, and the podcast will keep playing in the background.

Second feature that made into this version is "Expanded view".

Just press the new menu option - "Expanded Mode" and you will see expanded list of news items with a portion of the news content below each of them. Useful to skim through the news items in a hurry.



Last but not the least, "Periodic Cleanup" feature. In v1.2.5 new options were introduced to cleanup old news items. With the "Periodic Cleanup" feature, you can choose to run the cleanup service at desired intervals. That will keep the storage in check. The periodic cleanup will delete the items that match your specified settings for "Cleanup Age" and "Keep Unread Items" option.

That's it folks. BTW, check out the new Google Reader features the were introduced recently.

Happy Halloween!

Tuesday, October 20, 2009

Sprint HTC Hero problems with ReaderScope

[Update: A fix for this problem is released in v1.2.8a. Please update.]

In past 2-3 weeks new users of Sprint HTC Hero have been trying ReaderScope. Unfortunately, they hit a crash immediately after they open the app. A lot of them have been helpful in sending the crash reports to me. I have now received over 10-12 such crash reports, all confirmed to be on Sprint HTC Hero devices.

The exception that occurs implies that there is something wrong with one of the XML layout files. In reality, the XML layout in question is a very simple one and hardly seems to be the cause. I have posted the layout to android-developers group for others' review too. Most importantly this exception is not seen on any other handset or emulator.

I started a thread on android-developers group to discuss this issue. Although, there has been no authoritative answer whether this problem is in HTC firmware or not; couple more developers have reported similar issues their apps are seeing only on Sprint HTC Hero.

At the moment the only hope I have is next firmware update from HTC for these devices. If anyone has better suggestion, I am all ears.

Keep checking the thread mentioned above, or this blog. Hopefully we will find the solution soon. Thanks for using ReaderScope.

Saturday, October 10, 2009

ReaderScope 1.2.3 - Search

Search box is implicit for any Google product. Google Reader offers one too. Searching through your hundreds of feeds, folders is really handy. With v1.2.3 the same search functionality comes to ReaderScope.




You can search through all items, or individual feeds or tags. Pressing search menu button on first screen will let you search through "All items". Pressing search menu button on second screen will let you search through whatever feed or tag you are browsing on that screen currently. The search box will indicate the scope of your search.

Besides this feature, the functionality of updating the unread counts is implemented completely in this version. In v1.2.2 only the GUI was updated to reflect changes in unread counts. But actual update in database was disabled because of performance issues. In v1.2.3 the syncing with database is deferred till the end. So now you will see a "Syncing database" progress dialog before you close the app.

In v1.2.2, some fixes went in that take care of crashes people were seeing. No obvious crashes have been reported since, so I hope everything is going well. BTW, Did you like the loading screen? :)

Cheers!

Monday, October 05, 2009

ReaderScope 1.2.1 - Auto Login

Today I came across this excellent article "5 Nice Apps I refuse to use" by Chuck Falzone. It talks about how all the Google Reader apps (or android app that needs access to Google services) ask for passwords to the user's Google account. As you know ReaderScope does it too. But I completely empathized with the author's point of view. For many like me, Google is the biggest closet in their wired home. Giving the key to this closet to any random app is scary for sure.

However, of all the login mechanisms I found out, the only supported method for a desktop/mobile app is asking user to enter his/her username and password. Google supports OAuth, but only for web apps. I wish they implement version of OAuth for desktop/mobile apps, just like Twitter. Another way, is to get the login tokens from Android platform. Although, it should be available as platform API, it is not. I guess the reason is, it will be limited only to 'Google experience' phones, hence it cannot be part of the platform.

But, thanks to the article I mentioned above, more info came out on this discussion. From the article I learnt, that a Google Reader widget accesses Google Account info available on phone. I went through the comments, and one of them gave a pointer to how this might be done. After following the email thread, I found a way to do this. The solution is not perfect/ideal. It uses a library put together by a Google engineer (@jbqueru), but it is not an official part of platform. With the help of ubikdroid's example code from the thread, I put together an "AutoLogin" feature for ReaderScope (as of v1.2.1)


If you are using a 'Google Experience' phone (or a ROM with Google Apps), then you don't have to give your password to ReaderScope. Just press AutoLogin and you are in.

If the existing users want to remove their password (which by the way is encoded before storage) from ReaderScope and use this new auth mechanism, then you can do so without loosing your cached items. Go to Settings->Credentials Management. Press "Logout". Choose "No" to the prompt of deleting cache. The app will exit. Start it again, you will be taken to Login screen. Now choose AutoLogin.

If your phone is not 'Google Experience' phone, this will not work. You will get an error message.

I put this together in past few hours. Although it has passed my automated regression tests, there might be some rough edges. Let me know if you see any problems.

Saturday, October 03, 2009

ReaderScope v1.2

After 1 week of reimplementation of news cache, 1 day of testing and 1 last hour of crazy epiphanies and patches... v1.2 is out.

Here is what is new.

With the brand new caching scheme, you can store your bulky news cache on SDCard. Right now my HTC magic is pulling about 1000 images from web (which must be total 10-20MB in size), but the app manager shows ReaderScope only taking 588KB on phone. Because all the content is going to the SDCard.


Also if you do periodic downloads with caching the content, you will notice that the subsequent downloads will take significantly shorter times. This is because, the new caching mechanism accurately downloads only the content that's not in the cache.

In addition, there are few new features that users have been asking for.

Mark All As Read


You can mark all the items in a feed read. Does the same function as its counterpart in Google Reader web interface.

Share with note


You can just "Share", or you can add a note if you wish. Again same job as its counterpart in Google Reader web interface.

Manual override to go offline


One loyal user of ReaderScope has been asking for this feature for a while. It has very practical use for certain. I finally got around to implementing it in v1.2. There are situations when you are online, but you don't want ReaderScope to use the network (because it costs $$). You could already customize the periodic background downloads to not use such specific network. But, you may want to restrict GUI's access to the network too. While you are reading the news, ReaderScope marks the items read, stars/shares them if you choose - this is synced with Google Reader over network.

With the "Go Offline" button, all these front-end actions will be batched instead of doing instant sync over the network (and you will see them in the Pending notification at the top, the one you see when you are actually offline). When you want to go online with the right kind of network, choose "Go Online" and tap on the notification, it will sync your actions.

Moreover, some bug fixes have also gone in.

Hopefully you will enjoy the new version. It's not unlikely that some silly bugs went unnoticed during this big rewrite of the caching mechanism. If you see any problems or hit any crashes, please email me the crash report (using the in built crash reporter).

Enjoy!

Saturday, September 19, 2009

Crash report for Android App

To err is human and to crash is the software created by human (even if it is made for Android ;)

So when your app crashes on your users' phones, it's a good idea to collect the details of the crash and prompt the user to send them to you. I recently wrote one such scheme for ReaderScope and it has been very helpful. Here is how I am doing it.

I found the code snippets after googling around. Mainly from this stackoverflow article. This morning however, I added few more bits to work well with exceptions that take place in background threads.

The way to do this is to implement the Thread.UncaughtExceptionHandler interface and pass it to Thread.setDefaultUncaughtExceptionHandler() at the beginning of your Activity's onCreate(). Here is the implementation class TopExceptionHandler.
package com.altcanvas.readerscope;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Context;
import java.io.*;

public class TopExceptionHandler implements Thread.UncaughtExceptionHandler {

private Thread.UncaughtExceptionHandler defaultUEH;

private Activity app = null;

public TopExceptionHandler(Activity app) {
this.defaultUEH = Thread.getDefaultUncaughtExceptionHandler();
this.app = app;
}

public void uncaughtException(Thread t, Throwable e)
{
StackTraceElement[] arr = e.getStackTrace();
String report = e.toString()+"\n\n";
report += "--------- Stack trace ---------\n\n";
for (int i=0; i<arr.length; i++)
{
report += "    "+arr[i].toString()+"\n";
}
report += "-------------------------------\n\n";

// If the exception was thrown in a background thread inside
// AsyncTask, then the actual exception can be found with getCause
report += "--------- Cause ---------\n\n";
Throwable cause = e.getCause();
if(cause != null) {
report += cause.toString() + "\n\n";
arr = cause.getStackTrace();
for (int i=0; i<arr.length; i++)
{
report += "    "+arr[i].toString()+"\n";
}
}
report += "-------------------------------\n\n";

try {
FileOutputStream trace = app.openFileOutput(
"stack.trace", Context.MODE_PRIVATE);
trace.write(report.getBytes());
trace.close();
} catch(IOException ioe) {
// ...
}

defaultUEH.uncaughtException(t, e);
}
}

Note that we are not consuming the exception. We let the Android framework's defaultUEH to handle it. If you don't do this, bad things may happen.

At the top of your Activity register an instance of above class like this:
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);

Thread.setDefaultUncaughtExceptionHandler(new TopExceptionHandler(this));
...

This exception handler saves the trace in a file. When ReaderScope restarts next time, it detects the file and prompts the user if he/she wants to email it to the developer.

Emailing the stack trace with user's consent is so much easier on Android, than setting up a crash report server. Somewhere near the beginning of your activity just check if stack.trace file is present. If so, execute following code to pack it in an email.
try {
BufferedReader reader = new BufferedReader(
new InputStreamReader(ReaderScopeActivity.this
.openFileInput("stack.trace")));
while((line = reader.readLine()) != null) {
trace += line+"\n";
}
} catch(FileNotFoundException fnfe) {
// ...
} catch(IOException ioe) {
// ...
}

Intent sendIntent = new Intent(Intent.ACTION_SEND);
String subject = "Error report";
String body =
"Mail this to readerscope@altcanvas.com: "+
"\n\n"+
trace+
"\n\n";

sendIntent.putExtra(Intent.EXTRA_EMAIL,
new String[] {"readerscope@altcanvas.com"});
sendIntent.putExtra(Intent.EXTRA_TEXT, body);
sendIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
sendIntent.setType("message/rfc822");

ReaderScopeActivity.this.startActivity(
Intent.createChooser(sendIntent, "Title:"));

ReaderScopeActivity.this.deleteFile("stack.trace");

Hope this helps! If you have suggestions for improvements, please do let me know in comments.

ReaderScope 1.1.3 - SDCard storage

Update: Just released 1.1.3a. Thanks to Fernando, found a hidden bug in a background thread. the 'a' version fixes it.

As the time goes by, ReaderScope accumulates lot of news and metadata. It resides in a SQLite database on the phone. The cached webpages reside in a separate WebView cache - also on the phone. After a few days with the device, everyone comes to know how scarce the phone memory is. So it's a good practice for an app to store its data store on the external SDCard. In v1.1.3 ReaderScope facilitates putting the database on the SDCard. Just head to Settings -> Storage Management. Choose the "Database Storage Location" to be "SDCard".



You will also be prompted if you want to migrate your current database contents to the new location. In future, if you want to move the database back to the phone (maybe to replace the existing SDCard with a new one); that is possible too.

This should help saving some space on the phone. However this is only half the job. The WebView cache has a sizable footprint on the phone storage. The work to get it to SDCard is in progress.

Another addition in this version is the "Feed Filter" option. I hope it will improve the usability for the people who prefer to read only the latest downloaded news.





I have also set up a Google Group to discuss the issues and feature requests for the ReaderScope. Please do join.


Thursday, September 17, 2009

Wifi and Battery

Although I didn't dedicate a blog post to my latest beloved gadget purchase - I have been a happy owner of HTC magic for about three weeks now. I used it for first two weeks with the factory ROM. It worked quite well - except for the Google apps and access to Android market. To my delight I found that it had very good battery life. After around 6-7 hours away from the USB charger, it would loose 10-15% battery. Remembering people's experiences of 24-hr battery life, this seemed quite impressive. However to my annoyance, I found a problem. When the phone was not in use, its wifi would be off. I had ReaderScope on it downloading news every 1 hr. It cancelled the downloads because it didn't find any wifi (and I had set it to use wifi only). Moreover, some apps (which I never found out) would go over the Mobile network if they didn't find wifi. I use a prepaid SIM card. So after every usage of Mobile network, I get a message showing me how much credit was used in last call and what's the balance remaining. So after leaving phone unused for the night, I would find some of these messages in the morning. Using Rs. 0.30 to 9.00 at times. This really bugged me. Loosing money for no reason.

Last week I rooted the phone and installed a different ROM (Amon_RA), the one with all the good stuff (Google apps and access to market). With this ROM, I do not see the wifi getting turned off when the phone is not in use and the screen is off. The ReaderScope periodic downloads go on without any problem over wifi. However now the battery life has much deteriorated. Over night, when the phone is not hooked to a USB charger, it looses about 40-50% or more charge.

My conclusion, always ON wifi is sipping the juice out of battery.

What I find more interesting however, is HTC's default setting to turn off the wifi when device is unused and screen times out. (And I couldn't find any way to change that setting at all.)

I wonder if they had a noble motive of giving the user longer battery life OR was it to boost revenue of Airtel (partner carrier) by favoring Mobile traffic over Wifi, even when the later is available.

Anyhow... Glad to be with the new ROM!!

Wednesday, September 16, 2009

ReaderScope 1.1.2

Two of the requests I have seen so far:
1) How can I download ONLY new/unread items and no read items
2) How can I see only NEW items

In this release I have tried to address them.

1) If you want your periodic downloads to only download the new items, then the way to do it is NOT to choose any favorite tags or any favorite feeds. With no favorite feeds/tags, ReaderScope will query Google reader for a generic list of new items in your reading list. This will get you only the new items. This was possible already; but there was a problem to access these only new items.
The first screen of ReaderScope is designed to give you a summary of all your Google Reader feeds. So it will show you all the feeds and all the unread counts irrespective of they are downloaded in the cache or not. So you couldn't tell which feeds have cached items. In v1.1.2, the feeds that have at least one cached item will be marked brighter than the rest. So now you can see which feeds have items downloaded from the last periodic download.


2) Some users prefer to only read the latest downloaded news. For them the new unread items are now highlighted. As before, the read items will appear in dull colors. The unread items will appear in white color and the unread items that are less than 24hr old are shown in bold white letters.


Let me know if this works for you.

For those who found the filter button introduced in last release useful; here is a good news. If you want to hide read items permanently, then go to Settings->Miscellaneous. Check "Remember filter state for read items". Now if you turn the filter ON to hide the read items, it will remain so until you explicitly turn it OFF.



For those who read news by labels, it becomes confusing to tell which feed the news came from. That problem is addressed in v1.1.2 too.



(Now I won't mistake news from my Onion feed for genuine news ;) )

Thanks for the error reports. I am working on them.
Let me know if this version fits your needs.

Sunday, September 13, 2009

ReaderScope 1.1.1 quick-n-easy features

In the last post I forgot to mention the new feature added - crash reporter. But thanks to the users who used this feature to report the crashes, I found a subtle problem. Also a couple of suggestions from comments in last post were pretty easy to implement on a Sunday afternoon. So I just released v1.1.1 with the fix for the Login crash, two features (zoom functionality, filtering read items) and a little internal optimization.

About the crash. I believe it was seen only by G1 users who have slide-out keyboards (not necessarily though). The G1 users have to slide out the keyboard to type in the credentials. This puts the app into landscape mode. After they press the login button, the query goes over network and a rotating progress dialog is shown. Depending upon the network, this may take some time. Before the login is complete and the progress dialog is gone, if the user slides in the keyboard (which is very natural), they force the app into portrait mode. By default the android platform restarts the activity during these orientation changes. This causes the original progress dialog to not belong to the current window. So when the login response returns and the app tries to cancel the progress dialog, it crashes. The solution I have put in v1.1.1 is to override the default behavior of android and make it not to restart the app. This will not make any visible change in the user interface, except that now the app won't crash even when you slide in the keyboard very quickly.

Now about the features. You will now see a filter icon on top of the list of news items (the 2nd screen). If you tap it, all your read items will get hidden. If you tap it again, they will be visible again.

Without filter:



With filter:



In addition, the news item now has zoom buttons to adjust the zoom.



I am not very satisfied with the icons themselves, will try to make them pretty in future release.

So give it a shot!

Friday, September 11, 2009

ReaderScope 1.1 - Social Beacons

Do you ever read a news story or a blog post in your RSS reader and wonder what other readers think about it? You can visit the web page and see what comments it has got. You can go to news ranking sites like Digg/Reddit and read the comments. You can peek into FriendFeed to see what people have to say about this story. But imagine doing that from a 320x480 screen of your android phone. ReaderScope v1.1 tries to make this social interaction easy for you with Social Beacons.

When you start reading a news item, ReaderScope scans Digg, Reddit, FriendFeed (silently in background) to check if they have this story listed. If it finds them, it will indicate respective beacon icons in the lower right hand corner of the news. If you like, you can tap on them and check the popularity scores and comments from these sites. You don't need to have any account with these services to use this feature (unless you want to rank the story or post a comment).



Of course if you don't bother about the social buzz of a news item, you can turn this feature off. Social Beacons are similar to action controls, you can uncheck them by going to Settings -> Action Controls.



If you like to use beacons only when you have right network or sufficient battery, you can set that too, under Settings -> Smart Settings.


This release also adds the functionality to read news by labels/tags - something requested by many. As a matter of fact, the functionality was always there. If you used any of beta versions, you would know. In 1.0, I moved to a hierarchical structure for feed list, that put a limitation on accessing news by labels. In this release however I found a way to get it working. Check out yourself.


There were also some improvements to avoid some memory leaks (reference leaks to be precise).

So give it a try!