Sunday, April 24, 2011

Test - first development for Android

My app (Reverse CheckList) is now officially big enough to get over it and start having some unit tests. Seriously.

How can I tell?

For the first couple of hundreds of SLOC it was easy - a few features, easy to test, easy to keep the entire thing in my head. Features were not interdependent, and the whole app was just a test field for code samples that I found online. Oh, and it was not released. My wife and I were the only users. So if something did not work, I could simply code a fix and re-deploy it to our phones. Piece of cake.

This all changed when the design started crystallizing, when I refactored some of the code, that was previously scattered across multiple code files to be more coherent and reusable, and finally, when I released the app to the Android Market.

Reusable components... I think everyone agrees with me that this is a great idea to write code once and then reuse it. No code duplication, better design and... more dependencies! Yes! If a method is called from one place, you only need to retest this one place. If it is called from multiple places, then you have a bigger problem on your hands.

 Monkey helps a lot, of course. Just because 1. I'm too lazy to re-test the entire app when I change something 2. IT IS AWESOME and I can spend hours watching it just randomly click on stuff (Sometimes on two emulators simultaneously), but now I feel that even that is not enough.

So unit tests it is. We'll see how it goes.

Thursday, April 21, 2011

Reverse CheckList released

I made some updates yesterday and released Reverse CheckList 1.05. to Android Market.

In this release, I added sorting of the main screen of the app - the list of lists - by various criteria. I also fixed a few defects, and made it look just a little bit better - re-worded some messages and texts on buttons etc.

But there's still a lot to do before I can say that I am totally done with this little project and I can move on to something bigger and more interesting.

Wednesday, April 20, 2011

Observer that would not observe....

This is what I wasted half the night trying to solve and I failed:

http://stackoverflow.com/questions/5726468/contentobserver-getting-notifications-about-Linkdeletes-but-not-inserts-or-updates

Basically, I have a ListActivity that I wanted to sort. To enable sorting, I added a spinner control at the top of the screen, with sort options. When a sort option is selected, it requeries the underlying DataProvider, and the list is sorted according to user's preferences.

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent">

<LinearLayout android:orientation="horizontal"
android:layout_width="fill_parent" android:layout_height="wrap_content">

<Spinner android:layout_width="fill_parent"
android:layout_height="40sp" android:id="@+id/spinner_sort_activities">
</Spinner>

</LinearLayout>

<FrameLayout android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ListView android:id="@android:id/list" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:drawSelectorOnTop="true" />

<!-- Here is the view to show if the list is empty -->
<TextView android:text="@string/empty_list_default_value"
android:id="@+id/txt_empty_list" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:textSize="16sp"
android:textStyle="bold" android:gravity="center">
</TextView>
</FrameLayout>
</LinearLayout>

However, it does not make much sense for the Spinner to still be there when the list is empty, and the empty view is presented to the user. So I did this:

// Get a cursor to access the note
Cursor mCursor = managedQuery(ActivityColumns.CONTENT_URI, PROJECTION, null, null,
getSortOrderStringFromSpinner());

mCursor.registerContentObserver(new ContentObserver(new Handler())
{
@Override
public void onChange(boolean selfChange)
{
// hide the list sort drop down if no items in the list
if (activityListAdapter.isEmpty())
sortOptionsSpinner.setVisibility(View.GONE);
else
sortOptionsSpinner.setVisibility(View.VISIBLE);
}

@Override
public boolean deliverSelfNotifications()
{
return true;
}
});


For some reason it just does not work. I know there is a problem somewhere in my code. But I just don't know where. The observer is observing - it receives all deletes, but not inserts or updates. Frustrating.

I tried debugging, reading, googling, stack-overflowing, cursing and, finally, throwing some random code at the problem, but it still did not work. I hope to find an answer quickly, because I have some updates I wanted to publish ASAP, but I wanted to get the sorting done first.

Well, I'll spend some more time on it tonight.

Frustrated with Android

For the last few months I've been trying to write my first Android application. For the most part that was an awesome experience - the framework is very well designed, it looks nice, the tools are pretty good (emulator, monkey, etc), but sometimes things are just soooo frustrating.

And because misery needs company, I decided to share my experiences with Android with you.