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!

Monday, September 07, 2009

ReaderScope 1.0.2 - making deep cache simple

As was rightly complained, the deep caching feature didn't seem to work in ReaderScope. It appeared so because a) If feed is not marked for periodic download, deep caching won't have any effect b) deep caching wasn't done for news items that were already downloaded. In 1.0.2 this is fixed.

If you choose a feed for deep caching by going to Settings, it will automatically be marked as favorite (i.e. marked for periodic download). If the feed has news items already downloaded (before setting deep caching), then those news items will also be deep cached at the time of next download. I hope this will make the deep caching feature more intuitive.

Note that you will be able to view the deep cached page by clicking on the 'Open Link' action button from panel. For deep-cached news items the web page will open inside the ReaderScope itself (instead of an external browser) and this will work even when you are offline.

Also note the performance implications of choosing feeds for deep cache. It will make your background downloads run longer and consume more memory and network. Imagine opening 20 webpages in the web browser one after the other (this is what happens in the background). So use deep caching with discretion.

Apart from this, there are few more improvements. There is a slight change/improvement in notifications. There are now three distinct icons to tell different notifications from each other.

- This icon in the status bar will indicate that a download is in progress.
- This icon will not linger after download is complete. You will see a "Done!" ticker when download finishes and the notification will vanish. I thought having it after its job is done was unnecessary. However, I liked a suggestion that there should be a way to know when the periodic download took place. That information now will be available in the logs. Just go to Settings->Logs and at the top you will find results of latest 10 downloads.

You will see this icon in the status bar when the periodic/manual download has failed for some reason - due to unsuitable/no network OR due to low battery, etc.

You will see this icon, for the actions (star/share/unread) that you perform offline and are pending. It signifies the actions that are pending upload.

In addition, the action buttons menu has been made even more non-intrusive. When you press "Menu" the actions menu shows up. Now it will get hidden when you touch the news item (you don't have to press "Menu" again).

I hope you like 1.0.2. Let me know.

Thursday, September 03, 2009

ReaderScope 1.0.1

Update 2: Just released 1.0.1a. Upgrading to 1.0.1a from any previous version should cause crash. If you have already done a clean install of 1.0.1, then you don't have to upgrade to 1.0.1a, it has no new features, just the fix.

Update 1: Regarding the "Force-close" problem on upgrade from 1.0 to 1.0.1 - I see what the problem is, but don't understand why it is a problem, because I thought Android's package upgrade will handle it. It happens because of the changed layout of Action Panel. Apparently the upgrade process mixes the old and new layout files. Therefore a clean install of 1.0.1 solves the problem.

I am a long time Linux user. So when it comes to naming anything - files/images/tags - I don't use any white spaces in the name. It is a good practice IMO, but it made me forget that people can name their Google Reader tags with a space in the name. Moreover, Google Reader uses it as-is to form the UID. So none of my accounts exercised this test case. ReaderScope - as in 1.0 - neglects such labels and the others coming after it. That's probably the reason for two problems reported to me so far. Thanks to the gentleman who reported a bug this morning and more importantly generously provided his OPML file for testing. Now this bug should be fixed in 1.0.1.

If you were having any problems with missing feeds/labels, then you should upgrade to 1.0.1.

For troubleshooting this kind of problem, I am also introducing a logging facility in 1.0.1. I managed to put together a logging scheme that is accessible from the UI (Just go to Settings->Log). This screen shows a report of latest download and below that it will show key error messages that might have been incurred in background threads. If you see any problems in the behavior of ReaderScope, take a look at this screen. If you want me to look at it, just hit the "Menu" button and press email. The logs will be put in an email using the email client you have on phone, just send it to readerscope AT altcanvas DOT com. Also describe the problem you are facing.



Also 1.0.1 has some tiny but useful UI improvements. You can now favorite any feed from a short cut menu, while reading the feed.


The last improvement is the overlayed position of Action panel. It makes it less intrusive and more pretty.


So get your updates from android market OR slideme.org OR andappstore.com.