Sunday, December 30, 2012

2012 summary

The end of the year is the usual time for looking back and making a new years' resolutions, so I'm going to gloat a little bit and do some wishful thinking about the next year. Without further ado, let me list what I've managed to accomplish:

  1. I have published three apps on two mobile platforms.
  2. I changed my safe corporate job for a position in the startup.
  3. I gave a talk at the conference.
  4. There were over 4k unique visits of my blog and got a couple of +1s.

And what are my plans for the next year? I like to keep things simple, so let's just double the values from the list above, except maybe for the second point - I hope I won't have to change the job, I'm very happy with my current employer.

Anyways, thanks for reading my blog and stay tuned for more content next year!

Friday, December 28, 2012

UnknownHostException gotcha

Several days ago I was preparing a simple app for the programming contest organized by Future Simple during KrakDroid conference. It consisted of a public ContentProvider, SyncAdapter and an Activity. The contest participants had to write an app which retrieves the input data from ContentProvider, solve a very simple algorithmic problem and save the output back in the ContentProvider. This would trigger the sync and the SyncAdapter would upload the data to our backend. The Activity showed the contest rules and the list of submissions.

As you can see it's nothing fancy and I hacked everything together rather quickly, but when I tried to send the first submission I got the UnknownHostException from AbstractHttpClient.execute. Had I googled the issue first, I'd solve this issue in one minute. The problem is, my internet connection was flaky and the backend I tried to connect to was set up couple hours earlier, so the UnknownHostException seemed like a quite probable error.

If you haven't click the Google link above, here's the solution: this exception is a way in which Android tells you that your app is missing the INTERNET permission.

Tuesday, December 18, 2012

KrakDroid aftermath

I finally got some sleep after a really busy weekend. For the first time I had an opportunity to give a talk at the programming related conference. I was also responsible for a programming contest organized by the company I work at, I tried to push the new release of our product and, on top of that, I had to do some Christmas related stuff, so I didn't get much sleep.

Let's get back to the programming. I gave the talk about standard synchronization pattern on Android (SyncAdapter + Authenticator + ContentProvider) during this year's edition of KrakDroid conference. Here are the slides:



The link to recorded video can be found in the slideshare content description. I won't bother posting it here, because a) I gave the talk in Polish, and I try to keep the content of this blog in English b) I was really tired, at one point of the talk I had to stop and ask myself "What the hell am I trying to say".

Although my talk was average and there are many things I might have done better, I'm very happy I had an opportunity to give this talk. Public speaking is definitely outside of my comfort zone and I learned a lot. Here's the "conference speaker checklist" for my future self:
  1. Get some sleep
  2. Do the test run of the talk in front of the mirror

    I finished the slides for my presentation half an hour before giving it, so I didn't have a chance to rehearse it. There were few points during my talk when adding a slide with a summary would make things much clearer. Which brings me to the next point:
  3. Recap

    I'm not sure how much the listeners who didn't knew anything about sync will remember from my talk. The recaps could help them remember at least some keywords they would be able to google later.
  4. Do the test run of the talk in front of other people

    But not any other people - you want your audience to be critical and at least a bit familiar with the topic. You want someone who will tell you that this part of the talk is boring or that part is not clear.
  5. Get more sleep
And finally, do not freak out - the hardest part is saying "Hello, my name is...", the rest will pour out of you.

Tuesday, November 13, 2012

Android gets nested fragments

Most of the items in the Android 4.2 APIs list are kind of "meh" to me, but there is one item that's make me very happy: nested fragments. Fragments were supposed to be reusable UI components, but for some unfathomable reason the initial release of the fragments API didn't allow composing a fragment from other fragments. Even during the 2012 Google I/O one of the Google developers said that they have discussed this idea, but they're not sure if it's a good one, which is an absolute surprise to me, because I don't think anyone sane would consider making the ViewGroup which isn't a subclass of a View.

Fragment nesting was a topic I kept on my topic list for a day when I feel I need to bitch about something, but now I'm happy to remove it and write this ecstatic post. So now launch your SDK managers and download the r11 of support package! (or wait for half a year and hope it will be added to Maven Central)

Wednesday, October 17, 2012

Android protip: remove debug logs from release builds with Proguard

I use the android.util.Log extensively - often it's faster than starting the debugger and (unlike debugging) it's always on, which is invaluable when you're trying to track the root cause of some hard to reproduce bugs. Logging is also nice for release candidate builds you give to the QA team - if they find some bugs you'll have more information than just a stack trace.

On the other hand you don't want to keep all those logs for release builds, mostly for performance and privacy reasons. If you google around  for a solution to this issue you'll probably find a dozen ideas like using a wrapper for logging class, using a dedicated logging framework or even some sed/awk scripts to ant build process. Among those ideas is the one I'd like to recommend: customizing Proguard configuration.

Proguard is automatically run in release builds by ant and Export wizards from Eclipse and there is an optimization option, which can be used to disable logs:

-assumenosideeffects
Specifies methods that don't have any side effects (other than maybe returning a value). In the optimization step, ProGuard will then remove calls to such methods, if it can determine that the return values aren't used.
I wouldn't use this tool for the purpose stated in the manual, because even if my code doesn't have any side effects, the methods called from it might have ones; I'd rather use some static code analysis tool to find the unnecessary calls and manually remove them. It looks perfect for suppressing the logs though and it's very simple to set up - just add the following lines to the proguard-project.txt file generated by android command line tool:

-assumenosideeffects class android.util.Log {
    public static *** d(...);
    public static *** v(...);
    public static *** i(...);
    public static *** w(...);
    public static *** e(...);
}

You can of course keep some of the priority levels by removing lines from this config.

The nice thing about this solution is that you have to set it only once and it will just work for all further release builds.

UPDATE (August 2013): do not assume that Proguard will remove any code other than the methods listed in assumenosideeffects setting. See "Proguard gotcha" post for more details.

Tuesday, October 9, 2012

Android font metrics for dummies

Recently I was working on a custom View with overridden onDraw method and at some point I needed to center some text vertically. Unfortunately Paint.setTextAlign() supports only horizontal orientation, so I tried calculating the y-coordinate of the origin myself, but I couldn't get it exactly right. After two failed attempts I decided that I need a program which visualizes the available font metrics, because it seems that I do not understand the FontMetrics documentation, or the aforementioned class and it's documentation sucks.

You can find the source code on my GitHub, and here's the screenshot for other typographically challenged programmers:



(BTW: the word "Żyłę" used there is not a complete gibberish, it's an accusative case of word "vein" in Polish; it's nice, because it's short, but it covers all cases of the metrics class).

Let's get back to vertical alignment. In general you should center text vertically either on x-height or on half the cap height above the baseline (at least that's the info I found). Neither metric is directly available in FontMetrics class, but you can approximate the cap height as a (textSize - descent) or calculate x-height yourself using Rect height returned by Paint.getTextBounds for string "x".

Wednesday, October 3, 2012

Screensaver blocking on Symbian

In my latest game the players spend significant amount of time just watching the screen and trying to figure the puzzle out. The first few levels are obvious and most players will sole them in a few seconds,m but as the difficulty of the puzzles increases the players stare at the screen longer and longer. At some point the screensaver would kick in and piss the player off (fortunately my ragtag QA/betatester team found this issue before launch).

Google search results are (as usual) helpful, but the most promising lead, the QSystemScreenSaver class is not a solution. There are three problems with it:

  1. The API of the class itself is terrible.
  2. The API of the related QML element is even worse.
  3. Last, but not least, it doesn't work (at least not in the Qt Mobility version shipped with the Qt SDK).

(BTW: these three points sums up pretty much every experience with Qt Mobility package I had. Qt devs should either kill this festering boil with fire or fix it and rename it, because I learned to dread everything remotely related to Qt Mobility, and I suspect I'm not the only one).

Anyways, let's get back to the core of the problem, i.e. "how to block the screensaver". Qt Mobility failed, but the task doesn't seems like a rocket science to me. Slightly different Google search suggested using native Symbian's User::ResetInactivityTime() method. Few minutes and one QTimer later, everything worked:
#ifndef SCREENSAVERBLOCKER_H
#define SCREENSAVERBLOCKER_H

#include <QObject>
#include <QApplication>
#include <QTimer>

class ScreenSaverBlocker : public QObject
{
    Q_OBJECT

public:
    explicit ScreenSaverBlocker(QObject *parent = 0) : QObject(parent) {
        mTimer.setInterval(1000);
        connect(&mTimer, SIGNAL(timeout()), this, SLOT(blockScreenSaver()));
        changeScreenSaverState(QApplication::activeWindow() != 0);
        if (qApp) {
            qApp->installEventFilter(this);
        }
    }

    void changeScreenSaverState(bool blockScreenSaver) {
        if (blockScreenSaver && !mTimer.isActive()) {
            mTimer.start();
        } else {
            mTimer.stop();
        }
    }

protected:
    bool eventFilter(QObject *obj, QEvent *event) {
        Q_UNUSED(obj)
        if (event->type() == QEvent::ApplicationActivate
         || event->type() == QEvent::ApplicationDeactivate) {
            changeScreenSaverState(event->type() == QEvent::ApplicationActivate);
        }
        return false;
    }

private slots:
    void blockScreenSaver() {
#ifdef Q_OS_SYMBIAN
        User::ResetInactivityTime();
#endif
    }

private:
    QTimer mTimer;
};

#endif // SCREENSAVERBLOCKER_H
The important thing in the code above is watching the ApplicationActivate and ApplicationDeactivate events - after all, when your app is in background, you shouldn't affect the phone behavior. I'm not sure if the app would fail the Nokia's QA process without this feature, but it seemed prudent to write the code this way.

If you want to use this object in your QML UI just register it with qmlRegisterType and add the registered import and QML element to your root element.

Wednesday, September 26, 2012

Android sticky broadcast perils


I might have called this blog post "Android sticky broadcasts considered harmful", but "Considered harmful" texts are considered harmful, so I opted for a less linkbaity title.

I was working on a bug in a legacy code which used sticky broadcasts for publishing the sync service state (BTW: if you thought "Hello, ContentResolver.isSyncActive?" you should subscribe to my blog feed - I'm planning to write about the problems with sync state methods in ContentResolver). The bug manifested as a minor UI issue - sometimes the UI indicated that sync is in progress, even in situations when the sync could not be in progress, for example because the network connection was down. The QA team found an easy reproduction steps for it: start sync and reinstall the app before the sync finishes.

I found much more troubling reproduction steps: start sync, uninstall app while sync is in progress and then install it again. The difference between those steps and the ones found by QA is the fact that I'm performing uninstall, which means that all information about my app should be wiped from the system.

I dug deeper and found out two scary facts:

  • Sticky broadcasts are not connected in any way to app package, which means they are not removed on uninstallation. They are removed on phone restart, but that's not a scenario you should rely upon.
  • Any application with BROADCAST_STICKY permission may alter your sticky broadcasts.


As long as you don't use sync status for anything more complicated than showing a spinner in Action Bar, you might get away with only minor UI issues, but if you want to use it for business logic, you're entering the world of pain.

The sticky broadcasts might be garbage left over by the old version of your app or some other app if you've chosen the action string poorly (protip: if you really have to use sticky broadcasts, include your app's package name in action string). Even if you ignore the latter case (and the very unlikely scenario of other app maliciously tinkering with your app's intents), I consider the former case to be a deal breaker - each sticky broadcast is an additional state which has to be migrated between app versions and app installations which further increases cognitive load of programming, which is high enough already.

Let's summarize this blog post: remove BROADCAST_STICKY permission from your app's AndroidManifest.xml, test the app, fix all crashes from SecurityExceptions and never look back.

Wednesday, September 19, 2012

Android heterogeneous Adapters gotcha

Unless you were writing your Android apps under some kind of digital rock, you heard about Mark Murphy a.k.a. commonsguy. Today I'd like to write about a gotcha related to heterogeneous Adapters in general, which recently bit me in the rear when I used (misused?) one of Mark's Android components - MergeAdapter.

As you can read on the project's site, "MergeAdapter accepts a mix of Adapters and Views and presents them as one contiguous whole to whatever ListView it is poured into". This means of course that this is a heterogeneous adapter, i.e. the one which returns integer > 1 from getViewTypeCount(). The implementation of this method is pretty straightforward - it just iterates through the list of adapters it consists of and returns the sum of getViewTypeCount()s :
@Override
public int getViewTypeCount() {
  int total=0;

  for (PieceState piece : pieces.getRawPieces()) {
    total+=piece.adapter.getViewTypeCount();
  }

  return(Math.max(total, 1)); // needed for
                              // setListAdapter() before
                              // content add'
}
Everything is fine and dandy if you use the code like this:
@Override
public void onCreate(Bundle icicle) {
  super.onCreate(icicle);
  setContentView(R.layout.main);

  MergeAdapter adapter = new MergeAdapter();
  adapter.addView(someView);
  adapter.addAdapter(someAdapter);

  setListAdapter(adapter);
}
But sometimes you might want to attach the MergeAdapter to ListView and add fill it later (the real scenario for this case is adding stuff in onLoadFinished callback, I'm using contrived example for sake of simplicity):
@Override
public void onCreate(Bundle icicle) {
  super.onCreate(icicle);
  setContentView(R.layout.main);

  MergeAdapter adapter = new MergeAdapter();
  adapter.addAdapter(someAdapter);

  setListAdapter(adapter);

  adapter.addAdapter(someOtherAdapter);
}
This code will work as long as the adapter's contents fit on one screen, but if you start scrolling the list and the item recycling kicks in your app will crash with ClassCastException from your adapters' getView(). If by some chance you use the same IDs for the Views of the same type the app won't crash, but your items probably won't look exactly as they should. Either way, you won't be happy.

The root cause is the undocumented fact that the getViewTypeCount() is called only once after attaching it with ListView.setAdapter(). In the example above, the MergeAdapter contains only one item type when the setAdapter() is called, getViewTypeCount() will return 1, and adding the second adapter with another item type won't change this.

Why doesn't this crash right away? The ListView will call getView() in correct adapters, but then it will try to reuse items created by one adapter for items in the second adapter, because it assumes there is only one view type (getViewTypeCount() returned 1).

So what's the lesson here? Do not change the MergeAdapter in loader callbacks, either construct it before setAdapter() call (for example add empty CursorAdapters and call CursorAdapter.changeCursor() later) or postpone the setAdapter() call until you load all the parts. The more general rule is that you may not calculate the number of item types from the actual data, for example the following code won't work:
public class MyCursorAdapter extends CursorAdapter {
  // Implementation of bindView, newView, etc. skipped

  private int mCalculatedItemTypeCount;

  @Override
  public int getViewTypeCount() {
    return Math.max(mCalculatedItemTypeCount, 1);
  }

  @Override
  public void changeCursor(Cursor cursor) {
    mCalculatedItemTypeCount = /* some calculations */;
    super.changeCursor(cursor);
  }
}

Wednesday, September 12, 2012

Screen orientation and QT applications on Symbian Belle

Let's take a break from Android gotchas and do some mobile necrophilia, i.e. let's talk about Symbian.

Recently I received an email from Nokians saying that they are testing Nokia Store content with new firmware update and my app won't work after update. After few quick email exchanges we narrowed down the problem to screen orientation locking code I wrote about some time ago. It turns out that things can be done much simpler:

Window {
    Rectangle {
        id: root
        anchors.fill: parent

        Component.onCompleted: {
            screen.allowedOrientations = Screen.Landscape
        }

        // more stuff
    }
}
int main(int argc, char *argv[])
{
    QScopedPointer<QApplication> app(createApplication(argc, argv));

    QmlApplicationViewer viewer;
    viewer.setMainQmlFile(QLatin1String("qml/nupagadi/GameArea.qml"));
    viewer.setOrientation(QmlApplicationViewer::ScreenOrientationLockLandscape);
    viewer.setResizeMode(QDeclarativeView::SizeRootObjectToView);
    viewer.showExpanded();

    return app->exec();
}
Less code, no need to comment it as a gotcha/workaround, and it's supposedly futureproof.

I'm very positively surprised with Nokians' approach, responsiveness and this whole experience. Of course I wouldn't be me if I didn't bitch a little bit, namely: why did I have this problem in the first place? I mean, locking the screen orientation is not a rocket science and should be well documented. It should, but unfortunately it's not, like so many things about QML.

Wednesday, September 5, 2012

Android SharedPreferences gotcha

I have another gotcha for you. Can you tell what's wrong with the following code?

public class MyFragment extends Fragment implements OnSharedPreferenceChangeListener {
  private TextView mInfo;
  private SharedPreferences mPreferences;

  public static final String INFO_SP_KEY = "info";
  
  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
      Bundle savedInstanceState) {
    return inflater.inflate(R.layout.my_fragment, container, false);
  }

  @Override
  public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    mInfo = (TextView) view.findViewById(R.id.info);
  }
  
  @Override
  public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
    if (key.equals(INFO_SP_KEY)) {
      updateInfo();
    }
  }

  @Override
  public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    mPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
  }

  @Override
  public void onPause() {
    super.onPause();
    mPreferences.unregisterOnSharedPreferenceChangeListener(this);
  }

  @Override
  public void onResume() {
    super.onResume();
    mPreferences.registerOnSharedPreferenceChangeListener(this);
    updateInfo();
  }

  protected void updateInfo() {
    mInfo.setText(getString(R.string.info_text, mPreferences.getInt(INFO_SP_KEY, 0)));
  }
}
At first glance everything looks fine and in most cases it will work fine as well. However, if you a) set android:minSdkVersion to 8 or lower and b) change the shared preference from another thread (IntentService, SyncAdapter, etc.), you'll get the following crash:

09-05 07:16:58.993: E/AndroidRuntime(403): android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
09-05 07:16:58.993: E/AndroidRuntime(403): at android.view.ViewRoot.checkThread(ViewRoot.java:2802)
09-05 07:16:58.993: E/AndroidRuntime(403): at android.view.ViewRoot.requestLayout(ViewRoot.java:594)
09-05 07:16:58.993: E/AndroidRuntime(403): at android.view.View.requestLayout(View.java:8125)
09-05 07:16:58.993: E/AndroidRuntime(403): at android.view.View.requestLayout(View.java:8125)
09-05 07:16:58.993: E/AndroidRuntime(403): at android.view.View.requestLayout(View.java:8125)
09-05 07:16:58.993: E/AndroidRuntime(403): at android.view.View.requestLayout(View.java:8125)
09-05 07:16:58.993: E/AndroidRuntime(403): at android.view.View.requestLayout(View.java:8125)
09-05 07:16:58.993: E/AndroidRuntime(403): at android.view.View.requestLayout(View.java:8125)
09-05 07:16:58.993: E/AndroidRuntime(403): at android.view.View.requestLayout(View.java:8125)
09-05 07:16:58.993: E/AndroidRuntime(403): at android.widget.TextView.checkForRelayout(TextView.java:5378)
09-05 07:16:58.993: E/AndroidRuntime(403): at android.widget.TextView.setText(TextView.java:2688)
09-05 07:16:58.993: E/AndroidRuntime(403): at android.widget.TextView.setText(TextView.java:2556)
09-05 07:16:58.993: E/AndroidRuntime(403): at android.widget.TextView.setText(TextView.java:2531)
09-05 07:16:58.993: E/AndroidRuntime(403): at com.porcupineprogrammer.sharedpreferencesgotcha.BaseFragment.updateButtonText(BaseFragment.java:65)
09-05 07:16:58.993: E/AndroidRuntime(403): at com.porcupineprogrammer.sharedpreferencesgotcha.WrongFragment.onSharedPreferenceChanged(WrongFragment.java:12)
09-05 07:16:58.993: E/AndroidRuntime(403): at android.app.ContextImpl$SharedPreferencesImpl$EditorImpl.commit(ContextImpl.java:2830)
09-05 07:16:58.993: E/AndroidRuntime(403): at com.porcupineprogrammer.sharedpreferencesgotcha.BaseFragment$1$1.run(BaseFragment.java:36)
09-05 07:16:58.993: E/AndroidRuntime(403): at java.lang.Thread.run(Thread.java:1096)

Fortunately the obvious crashlog is obvious and you can solve this issue in about 5 seconds, by wrapping the UI action in Activity.runOnUiThread() method. Morbidly curious may track the root cause of this issue in GrepCode. Tl;dr: before Gingerbread the listeners are notified in the same thread as the SharedPreferences.commit() caller, in later releases commit() ensures the notifications are performed in UI thread.

Code of the sample application that demonstrates this issue is available on my github.

Thursday, August 30, 2012

Custom scrollbar graphics in Android

One of the UI elements you might want to customize in your app are scrollbars for ListViews, ScrollViews and other scrollable objects. It's a minor visual change, but it might give your app more consistent and polished look and feel, especially in case of the heavily branded UI.

Anyways, changing the default scrollbar style is dead simple - you just need to specify new drawables for the track and thumb in your style:

<style name="badList">
    <item name="android:scrollbarThumbVertical">@drawable/scrollbar_handle</item>
    <item name="android:scrollbarTrackVertical">@drawable/scrollbar_track</item>
</style>


Of course, there is a small gotcha (why else would I bother to write this blog post?). Let's say that you don't need to customize horizontal scrollbar, so you prepare only vertical 9-patches:


On Ice Cream Sandwich everything looks fine, but on Gingerbread the graphics are not exactly what you want:



Quick Google search returned a StackOverflow thread with a description and link to Android bug tracker, but no full workaround. If you're too lazy to click on those links, on Gingerbread and earlier releases the View asks ScrollbarDrawable for the height of horizontal scrollbar and uses it as a horizontal scrollbar height and vertical scrollbar width. Let's modify our scrollbar graphics a bit:



And apply it as both horizontal and vertical scrollbar.

<style name="goodList">
    <item name="android:scrollbarThumbVertical">@drawable/scrollbar_handle</item>
    <item name="android:scrollbarTrackVertical">@drawable/scrollbar_track</item>
    <item name="android:scrollbarThumbHorizontal">@drawable/scrollbar_handle</item>
    <item name="android:scrollbarTrackHorizontal">@drawable/scrollbar_track</item>
</style>

Lo and behold, it works!





Note: in general case you probably want to create another graphics for horizontal scrollbar by rotating and flipping vertical scrollbar graphics. Our scrollbar graphics doesn't have any non-symmetric elements and I'm lazy, so I used the same 9-patch for both scrollbars.

The code of the sample application can be found here.

Friday, August 24, 2012

More content coming soon

It's been a while since my last blog post. The main reason of this unfortunate state is the fact that I've changed my day job. I've dumped my cushy corporate post in favor of more demanding position at startup. I have much less time for blogging and my personal projects, but in return I've learned a lot about Android. I already have about two dozens entries on my topic list and I keep adding new ones every other day.

The "write when you have some time" plan obviously doesn't work, so I have to try something else. My current plan is "publish one post every Wednesday". We'll see soon how will it work out for me.

Friday, June 22, 2012

SQLite unions gotcha

Recently I've been tracking the problem with SQLite database used in the Android application I'm working on. The starting point of the whole story is that I've noticed that the cursor created with the SQLiteDatabase.query() method returned smaller data set than the same query executed through sqlite3 command line interface. The query in question looked like this:
SELECT * FROM some_view WHERE (column_a=1 OR column_b=1);
Inside the Android app I was getting rows for the second part of OR clause (i.e. column_b=1), but no rows for the first part.

Quick search through Android sources yielded the clue - I wasn't executing exactly the same query on the command line. Selection arguments are always bound as a strings, so the question marks in query string should be surrounded with quotes. So the Android app was executing the following query:
SELECT * FROM some_view WHERE (column_a="1" OR column_b="1");
So now we have another puzzle: why column_b=1 and column_b="1" give the same results, but the behavior is different for column_a?
Let's try to reproduce the problem:
sqlite> .mode column
sqlite> .headers on
sqlite> CREATE TABLE t (x INTEGER);
sqlite> INSERT INTO t VALUES(1);
sqlite> SELECT COUNT(*) FROM t WHERE x=1;
1
sqlite> SELECT COUNT(*) FROM t WHERE x="1";
1
So far so good, no surprises. Let's create a view similar to the one which causes problems.
sqlite> CREATE VIEW v AS SELECT NULL AS a, x AS b FROM t UNION SELECT x, NULL FROM t;
sqlite> SELECT * FROM v;
a           b
----------  ----------
            1
1
Now let's take a look at counts:
sqlite> SELECT COUNT(*) FROM v WHERE b=1;
COUNT(*)
----------
1
sqlite> SELECT COUNT(*) FROM v WHERE b="1";
COUNT(*)
----------
1
sqlite> SELECT COUNT(*) FROM v WHERE a=1;
COUNT(*)
----------
1
sqlite> SELECT COUNT(*) FROM v WHERE a="1";
COUNT(*)
----------
0
Yay, we reproduced our bug. But why is this happening?
sqlite> PRAGMA TABLE_INFO(v);
cid         name        type        notnull     dflt_value  pk
----------  ----------  ----------  ----------  ----------  ----------
0           a                       0                       0
1           b           integer     0                       0
It seems that the lack of explicitly defined type of the first column prevents type conversion (please note that this is only my assumption based on the observations above; unfortunately the sqlite documentation doesn't cover such cases in detail). How can we work around this issue?
sqlite> CREATE VIEW vfix AS SELECT x AS a, x AS b FROM t WHERE 1=0 UNION SELECT * FROM v;
sqlite> PRAGMA TABLE_INFO(vfix);
cid         name        type        notnull     dflt_value  pk
----------  ----------  ----------  ----------  ----------  ----------
0           a           integer     0                       0
1           b           integer     0                       0
As you can see the column types are correctly copied from the underlying table. Let's check the counts:
sqlite> SELECT COUNT(*) FROM vfix WHERE b=1;
COUNT(*)
----------
1
sqlite> SELECT COUNT(*) FROM vfix WHERE b="1";
COUNT(*)
----------
1
sqlite> SELECT COUNT(*) FROM vfix WHERE a=1;
COUNT(*)
----------
1
sqlite> SELECT COUNT(*) FROM vfix WHERE a="1";
COUNT(*)
----------
1
Looks OK. Pretty? No, but it does the job and that's what matters at the end of the day.

Wednesday, May 30, 2012

QML applications on Nokia Belle

After the latest update of "Nu, Pogodi!", I received few negative reviews saying that the game doesn't work. I've tested the game thoroughly on all devices I was able to get my hands on, but I wasn't able to reproduce the error, so I decided to wait until I get more info. Few days ago with the help of one customer I was able to pin down the problem - the game failed to display any UI on new Belle firmware with Qt 4.8.0. I don't have such device myself, but fortunately the great Remote Device Access service allows testing on Nokia 808 PureView with latest Belle firmware. I've reproduced the error, wrote the Nokia Developers Support, and they sent me a very helpful link: Changes in Nokia Belle FP1. One issue listed there caught my eye:

If application does not specify a size for the root QML object and doesn’t use the Qt components Window as root element (Window component should not be used as a child item), it might cause the root window not to be shown.

Solution / Workaround:
Always declare a size for your custom QML root element.

I've checked my main QML file and indeed, I did not set the root element size, instead I've set the resize mode to SizeRootObjectToView and maximized the QDeclarativeView. I think it's the better solution than setting the root element size explicitly, because the display resolution is not the same on all Nokia phones (I'm looking at you, E6). Instead of doing that, I wrapped my entire UI into Window element from Qt Components and lo and behold, my game displayed something, although it wasn't exactly what I expected:


My code locked the screen orientation after loading main QML file, and it looked like the only thing that might cause this problem, so I changed the calls order. On Belle FP1 devices everything worked fine, but this change broke the display on devices with Anna and older Belle firmware:


Wat? The only solution I came up with was creating the utility method for detecting version of Qt during runtime and locking screen orientation after and before loading main QML file, depending on the Qt version. Relevant piece of code:

bool Utils::runtimeQtAtLeast(int major, int minor, int bugfix)
{
    const QStringList v = QString::fromAscii(qVersion()).split(QLatin1Char('.'));
    if (v.count() == 3) {
        int runtimeVersion = v.at(0).toInt() << 16 | v.at(1).toInt() << 8 | v.at(2).toInt();
        int version = major << 16 | minor << 8 | bugfix;
        return version <= runtimeVersion;
    }
    return false;
}

// ...

const bool qt48 = Utils::runtimeQtAtLeast(4,8,0);
QmlApplicationViewer viewer;
if (qt48) {
    viewer.setOrientation(QmlApplicationViewer::ScreenOrientationLockLandscape);
    viewer.setResizeMode(QDeclarativeView::SizeRootObjectToView);
}

viewer.setMainQmlFile(QLatin1String("qml/nupagadi/GameArea.qml"));

if (!qt48) {
    viewer.setOrientation(QmlApplicationViewer::ScreenOrientationLockLandscape);
    viewer.setResizeMode(QDeclarativeView::SizeRootObjectToView);
}
This kind of incompatibility between minor versions of the Qt framework is mind boggling. It makes me think what else did Nokia screw up in Qt 4.8.0 for Symbian and what will they screw up in the next firmware updates. One thing is sure: I'll have a lot of blogging material.

Tuesday, April 17, 2012

Git killer feature: content tracking

At my current day job I was the first adopter of git and one of the most frequently question I'm asked as "the git guy" is the variation of "why do you use git instead of mercurial?". I usually responded that git was the first distributed VCS I used, and I played for a while with hg and bazaar, but I haven't discovered any significant feature that would make me change my initial choice. Today I have found the case that allows me to give a better answer.

About a month ago I released the Word Judge application - an offline word validator for Scrabble and other word games. Initially I was working only on one version of Word Judge with polish dictionary, so I had one line of code in my git repository:


When polish version was ready, I decided that I might as well release the english version, because it would require only minor changes: changing app icon and dictionary file. It turned out that I also have to change the application package, so both versions of Word Judge could be installed at the same time. The changes were in fact minor, but included a lot of renaming (package name is mirrored by the directory structure) :


Later on I decided to add ads to my application. First, I've added the necessary code to master (i.e. polish) branch:


I switched to english branch, typed 'git merge master --no-commit', and was very surprised to see just a few conflicts in binary files (icons). All the changes made in java code, in the renamed files, were automatically merged. If you still don't get how awesome this is, consider what would I had to do without it:

  1. Start the merge
  2. Resolve the conflict by selecting "keep deleted files" for every java file changed in the master branch
  3. Manually merge every java file from "pl" package and "en" package
  4. Delete "pl" package
  5. Commit changes

And I would have to do this every single time I port the changes between language branches. It's a tedious and error-prone job, and git automatically (automagically?) does it for me. How cool is that? Mightily cool IMHO. Of course if I add some java files in one branch, I have to do move them to correct package in the other branch (hence the '--no-commit' switch in git command above), but most of the conflicts are caused by binary files.

It's also more common case than you might think, i.e. you don't have to write a dictionary supporting multiple languages to run into it. For example if you have a lite and full version of an app, you have exactly the same situation.

Right now my repository looks like this (or it would look like this if the git gui tools didn't suck, but that's another story):


I can't imagine supporting such structure without automatic merges provided by git, because it tracks content instead of files. And that's the real killer feature that distinguishes git from other DVCS.

Tuesday, April 10, 2012

The dark side of LINQ: LINQ to SQL on Windows Phone

In case you don't know what's LINQ and you use C#, I suggest you drop everything you do and enlighten yourself. Be warned: when you learn LINQ, you won't be able to work with Java collections (Guava makes them bearable, but barely) or Qt/STL containers without throwing in your mouth every now and then.

Here's tl;dr for the non-enlightened: LINQ is a sane way to query and alter the data. Instead of this:

private void PrintSortedEvenNumbers(IList<int> unfiltered)
{
    List<int> filtered = new List<int>();
    foreach (int i in unfiltered)
        if (i % 2 == 0)
            filtered.Add(i);
    filtered.Sort();
    foreach (int i in filtered)
        Console.Write(i + " ");
}
You can just write this:
private void PrintSortedEvenNumbers(IList<int> unfiltered)
{
    foreach (int i in unfiltered.Where(num => num % 2 == 0).OrderBy(n => n))

        Console.Write(i + " ");
}
This is a trivial example, but the more complicated code, the more benefit you get from using LINQ.

I started using it for operations on collections and XML files and I immediately fell in love with it. Imagine my joy when I learned that Windows Phone 7.1 finally supports local relational database which can be queried through LINQ to SQL!

I've read the tutorial, thought a bit about the application I was writing at the time and decided that I need many-to-many relationship. Oops, that's not supported. Well, it's "kinda" supported, meaning you can create a data structure and insert some data, but when you remove the data the foreign key constraints won't be verified and cascade triggers won't work. I think I can simplify the above statement and just call it "not supported feature".

Fortunately I didn't absolutely had to use many-to-many relationship. It would be nicer and would allow us to relax some constraints, but the current data could be as well represented using a nested one-to-many relationships. I've wrote the code based on aforementioned LINQ to SQL tutorial, wrote the tests, run them and watched in amazement as they fail. After googling a lot and experimenting I was able to make my code work, but it was quite different than the crap they posted on MSDN as tutorial. If you want to use LINQ to SQL, take a look at this code on github.

I spent about two days reading about LINQ to SQL and experimenting with the code and in the end I didn't even had the data structure I wanted to. And we're not talking here about rocket science, the SQLite scheme I needed was something like this:
create table x (id INTEGER PRIMARY KEY AUTOINCREMENT, text STRING);
create table y (id INTEGER PRIMARY KEY AUTOINCREMENT, text STRING);
create table z (id INTEGER PRIMARY KEY AUTOINCREMENT,
                x_id INTEGER NOT NULL,
                y_id INTEGER NOT NULL,
                FOREIGN KEY(x_id) REFERENCES x(id) ON DELETE CASCADE,
                FOREIGN KEY(y_id) REFERENCES y(id) ON DELETE CASCADE);
Which leads me to conclusion: LINQ to SQL for Windows Phone just doesn't work. Consider also the amount of boilerplate code I had to write for simple foreign key relation: in case you didn't looked at the github link that's whooping 80 lines of code for every one-to-many relationship. I don't know, maybe there are some tools that generate this stuff for you, but in this case why does the official tutorial even mention writing the table classes by hand? And where are those tools?

Recently I was also playing with Django which also features an ORM for the model definition.You need the foreign key? You use something called ForeignKey. You need the many-to-many relationship? You use the ManyToManyField. Dirt simple. I'm sure there are some dark corners you have to be aware of, but the basic stuff just works.

Wednesday, April 4, 2012

Background operations on Windows Phone 7

Few weeks ago I was complaining to another developer that Windows Phone applications cannot perform tasks in background when they are not running. That was true few months ago when I learned about Windows Phone 7.0, but he pointed me to MSDN documentation of new WP 7.1 feature: Background Agents.

I clicked the link with my hopes up, but I was immediately shot down with the highlight on the first page: "Background agents are not supported on 256-MB devices". I proceeded to the overview page and it turned out the highlight from the first page was just the tip of the iceberg. The constraints listed there are just staggering.

First there are the registration issues: you can register background task for the next two weeks and after that period your application have to reschedule the task. I'm not sure why do I have to do this, and at the first glance it looks only like a minor nuisance, until you take into account two other constraints: tasks cannot reschedule themselves and there is a hard limit of scheduled periodic tasks, which can be ridiculously low. Relevant quote from MSDN:
To help maximize the battery life of the device, there is a hard limit on the number of periodic agents that can be scheduled on the phone. It varies per device configuration and can be as low as 6.
Not a minor nuisance anymore, huh?

This limit is only imposed on periodic agents, which are intended for short, periodic tasks like polling some service or uploading a data. There are also Resource Intensive Agents which can be used for longer tasks like data synchronization, but they have their own set of constraints: the device have to be charging, the battery have to be almost fully charged and there should be a Wi-Fi or PC connection (no cellular data). I think the MSDN note summarizes it quite well:
Due to the constraints on the device that must be met for resource-intensive agents to run, it is possible that the agent will never be run on a particular device. (...)Also, resource-intensive agents are run one at a time, so as more applications that use resource-intensive agents are installed on a device, the likelihood of an agent running becomes even less. You should consider this when designing your application.
I'm going to add to the comment above my own observation: every application can register only one background agent, which can be both periodic agent and resource intensive agent. It means that if you need both types of agents, your resource intensive agent is also affected by the periodic agent hard limit.

It all boils down to this: you can't rely on the background agents. You don't have the guarantee that you'll be able to register the agent, which means that you can't use them for critical functionality. So we're exactly where we were after 7.0 release.

Wednesday, March 28, 2012

QML error handling revisited

After releasing Nu, Pogodi! I learned the hard way that checking the QML runtime errors might be a good idea. For that particular application, simply checking the errors from QDeclarativeView after setting the main qml file was enough, because everything in qml file was statically declared. But what if you use QML Loader element, either explicitly or through some other qml element like PageStack from Qt Components, and something goes wrong?

Well, if you don't improve the error handling code, your application will silently fail in some places, which probably won't make the users happy. I didn't wanted to repeat the Nu, Pogodi! screw up when releasing Word Judge, so I've created a better error handling solution. First part is an error handler class:
// ----------------------------------------
//  qmlerrorhandler.h
// ----------------------------------------

class QmlErrorHandler : public QObject
{
    Q_OBJECT
public:
    explicit QmlErrorHandler(QDeclarativeView &view, QObject *parent = 0);
    bool errorOccured() const;

private slots:
    void handleQmlStatusChange(QDeclarativeView::Status status);
    void handleQmlErrors(const QList<QDeclarativeError>& qmlErrors);

private:
    QDeclarativeView &mView;
    bool mErrorOccured;

};

// ----------------------------------------
//  qmlerrorhandler.cpp
// ----------------------------------------

QmlErrorHandler::QmlErrorHandler(QDeclarativeView &view, QObject *parent) :
    QObject(parent),
    mView(view),
    mErrorOccured(false)
{
    connect(&view, SIGNAL(statusChanged(QDeclarativeView::Status)), SLOT(handleQmlStatusChange(QDeclarativeView::Status)));
    connect(view.engine(), SIGNAL(warnings(QList<QDeclarativeError>)), SLOT(handleQmlErrors(QList<QDeclarativeError>)));
}

void QmlErrorHandler::handleQmlStatusChange(QDeclarativeView::Status status)
{
    if (status == QDeclarativeView::Error) {
        handleQmlErrors(mView.errors());
    }
}

void QmlErrorHandler::handleQmlErrors(const QList<QDeclarativeError>& qmlErrors)
{
    QStringList errors;
    foreach (const QDeclarativeError& error, qmlErrors) {
        // Special case for bug in QtComponents 1.1
        // https://bugreports.qt-project.org/browse/QTCOMPONENTS-1217
        if (error.url().toString().endsWith("PageStackWindow.qml") && error.line() == 70)
            continue;

        errors.append(error.toString());
    }

    if (errors.isEmpty())
        return;

    mErrorOccured = true;

    QMessageBox msgBox;
    msgBox.setText("Uh oh, something went terribly wrong!");
    msgBox.setInformativeText("We're sorry, but it seems there are some problems "
                              "with running our application on your phone. Please "
                              "send us the following information to help us resolve "
                              "this issue:\n\n") +
                              errors.join("\n"));
    msgBox.exec();
    qApp->exit(-1);
}

bool QmlErrorHandler::errorOccured() const
{
    return mErrorOccured;
}
And here's how I use it in my applications:
int main(int argc, char *argv[])
{
    QScopedPointer<QApplication> app(createApplication(argc, argv));

    QScopedPointer<QmlApplicationViewer> viewer(QmlApplicationViewer::create());
    QmlErrorHandler errorHandler(*viewer);
    viewer->setMainQmlFile(QLatin1String("main.qml"));
    viewer->showExpanded();

    if (!errorHandler.errorOccured()) {
        return app->exec();
    } else {
        return -1;
    }
}
Basically we need to catch the runtime errors, which are emitted from QDeclarativeEngine in signal named for some unfathomable reason "warnings". Checking the errorOccured() in main() is ugly, but the qApp->exit() doesn't work until the event loop in main is started and that's the first thing which came to my mind. Please leave a comment if you know a simpler solution.

Note the lines 46-49 in QmlErrorHandler: we're catching all warnings and the qt components are not completely free of them. I had to add a special case to prevent triggering the handler on every orientation change. If you stumble upon some other errors that should be ignored, please let me know.

Friday, March 23, 2012

Word Judge for Symbian available in Nokia Store

Today I have finally pushed both versions of Word Judge through Nokia Store QA. The version with an english dictionary was available for over the week now, but I had to submit the polish version few times until it passed the Nokia tests.

Anyways, the applications are available for free in Nokia Store. Here's the link for english version:


And for polish version (might not be available in all countries - I had to add some restrictions to pass QA process):


Stay tuned for posts about development of Symbian version and enjoy your Scrabble games.

Monday, March 19, 2012

DAWG data structure in Word Judge

I'm the first to admit that the Word Judge is booooring application. Checking if the word can be used in a word game? Meh. From a programmer perspective however, there is one very interesting problem to solve - how to compress a large dictionary to reduce the size of the application package and at the same time be able to query this dictionary without using excessive amount of memory and CPU power?

First, let's settle on what is a "large dictionary". One of the languages supported by Word Judge is Polish, for which the valid word list has over 2 million entries and takes about 36MB after unpacking. Do we need to compress this data at all? Probably not. If you consider the average hardware spec and modern network speed, the 36MB is not much, but we can do so much better. Besides, it's fun!

One the other end of the spectrum is the "zip all the things" approach. It's not a good idea though - it puts a very heavy load on the CPU and doesn't compress the data that well. The zipped Polish dictionary takes 4.5MB.

The most important observation is that we're not compressing some random white noise, but words from real language. We can leverage this information to get a much better compression than some generic algorithm. Lot of words share the same prefix and suffix, which means they can be efficiently represented as a directed acyclic graph with shared nodes for common prefixes and suffixes. Let's see how the graph would look like for couple of words:


White letters on black background mark the nodes representing the ends of words. So the graph above represents the following words:
abject
abjection
abjections
abjectly
abjectness
ablate
ablated
ablation
ablations
The nodes for 'ab' prefix are of course shared, the 'ions', 's' suffixes nodes as well. For obvious reasons we cannot share the 't' node: in one group it marks the end of the word, in other it does not; the child nodes for each 't' nodes are also different. Nodes are equal, and thus can be shared, if and only if they represent the same letter, the End Of Word flag is the same for both of them, and the list of children is exactly the same.

This type of graph contains minimal number of nodes, but each node takes quite a lot of space. We need 8 bits for the letter, 1 bit for EOW flag, 8 bits for the children list length and some kind of reference, for example node index, to each child, which is log2(n) rounded up bits, where n is a number of nodes in the graph. As you can see the major contributor to a single node size is the children list: for TWL06 dictionary the number of nodes is in the 10k-100k order of magnitude, which means we need several bits per node index.

A guy called John Paul Adamovsky found a neat solution to that. Instead of keeping the full list of children, we can keep children in something similar to singly-linked list: let's store only the index of the first child, number the nodes in such way that the children always have consecutive indices and add a End Of Children List flag to each node. This way we need exactly 1 + log2(n) bits for child list. This way we can keep the entire node in one byte. What's the catch? We need to introduce few more nodes.

For example on the graph above we can no longer share the 'i' node in the 'ions' suffix: if it was shared it would have to have a number one greater than the number of both 'e' node in 'ablate' and 'n' node in 'abjectness' (this is, of course, assuming the 'i' node is the last on the child list of both 't' nodes; but it's impossible for any node ordering). Our graph would look like this:


The rule for node equality has to be extended: the nodes are equal if they represent the same letter, the flags are the same, the children are equal and the siblings further on the children list are equal. The last condition is a bit confusing, so I'll provide an example.

Let's add a 'absolution' and 'absolutions' to our dictionary. We can certainly share the 'ab' prefix and 'ons' suffix, but do we need a separate 'i' node as well? The 'i' node in 'absolution' must have a End-Of-Child-List flag set. If we arrange the child nodes in either 'ablat-' or 'abject-' part of graph in such way that the 'i' node is the last node, we can share the 'i' node between that part of graph and the newly added branch. Our graph would look like this:


This is the way we can squeeze the 36MB dictionary to 1.5MB. The structure also doesn't need any processing, it can be loaded to int array and used directly. If you're curious how to convert the dictionary into this kind of graph you can read the description of my dawggenerator project on github (C++) or the original description by John Paul Adamovsky (pure C).

Wednesday, March 14, 2012

Large raw assets in Android 2.2

My most recent application, Word Judge, contains full dictionary of word valid in word games. I went to great lengths to minimize the size of this data, but for some languages it's still quite large. For example polish dictionary is compressed from 35MB to 1.4MB. In Android 2.2 and earlier, if you add such large file as an asset and then try to open it, you'll get IOException. The exception itself doesn't contain any useful information, but the following text appears in logcat:
03-07 14:40:42.345: D/asset(301): Data exceeds UNCOMPRESS_DATA_MAX (1442144 vs 1048576)
With that information googling the workaround is easy. Some folks recommend splitting the file into multiple parts, or repackaging the apk, but the simplest solution is to change the file extension of your asset to one of the extensions listed in aapt tool sources as files that are not compressed by default:
/* these formats are already compressed, or don't compress well */
static const char* kNoCompressExt[] = {
    ".jpg", ".jpeg", ".png", ".gif",
    ".wav", ".mp2", ".mp3", ".ogg", ".aac",
    ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet",
    ".rtttl", ".imy", ".xmf", ".mp4", ".m4a",
    ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2",
    ".amr", ".awb", ".wma", ".wmv"
};
Of course if you target API level 9 or newer, you don't have to worry about this gotcha.

There is one more thing worth mentioning: the speed of IO operations in debug mode is atrocious. Loading the 1.4MB dictionary to memory takes over 10 seconds. Fortunately when the program is running without debugger attached, the loading time decreases to less than 1 second.

Saturday, March 10, 2012

Introducing: Word Judge

I enjoy playing Scrabble and other word games like WordSquared of Word with Friends and I think I'm quite good at them for an amateur - my average score in 2-player Scrabble game is around 330 points. I do not have a tournament aspirations, because memorizing word lists or best stems doesn't fit my definition of fun, but I'm always in for a casual game.

The only thing that bothers me during these casual games are occasional, but unpleasant arguments - how do you spell given word or what is some 3 letter combination an obscure word or gibberish. Polish language have both declension and conjugation with a lot of irregularities, so there are plenty of things to argue about.

Most of the times we sort things out simply by checking the word spelling on the Internet, but there are situations when you can't do that, for example when you're abroad and don't have wi-fi access and roaming data access price is extraorbitant, or you're in the middle of the outback with no connectivity whatsoever. Without that possibility you have to find a compromise, and boy, that's not easy when you challenge someones 7-letter word on two triple-word bonus tiles.

That's why I created Word Judge - an ultimate way to settle all word-related disputes. The application contains full dictionary of valid words and short word list with definitions. Dictionary is shipped with the application, so you don't need internet connectivity to check if the word can be used in a word game. Currently the application is available only for Android devices, but I'm going to release Symbian (and maybe Harmattan?) version soon.



Unlike many other word game helpers, Word Judge doesn't contain anagrammer tool. It's not an oversight, it didn't add it for two reasons. First of all I don't think that such functionality should be bundled with the application, which is basically a dictionary. You use a dictionary to check the spelling, not to cheat. The second reason is more prosaic - I don't like the UI of any anagrammer tools I found. It's usually a jumble of checkboxes and radio buttons and two fields for board and rack tiles. Even if you figure out what all those controls do, these tools don't really solve the problem, which is finding the best move. It's not an easy problem, even if you find a convenient way to import current game state to the application (AR maybe?). It's certainly a good idea for a separate application, but not for a part of a dictionary app.

I released one version of my application for every supported language (currently only English and Polish). I considered setting up a service with downloadable dictionaries, but I decided against it, because it would needlessly complicate the application and I think that using multiple dictionaries is quite rare use case. Let me know if I'm wrong, I might change my mind. Also, if you like the idea of my application and want me to create a Word Judge for your language, send me an email. Unfortunately, since the app is free I cannot offer you anything more than a mention in application description and "About" window.

I was sure from the start that I won't charge money for this app, but I pondered for quite a long time if I should use ads. On one hand they are visually displeasing and take scarce screen real estate. On the other hand, I want to see for myself how much revenue they generate - the testimonies of other developers vary from ecstatic to disappointed. In the end I decided to add a small banner as an experiment.

This post is already getting too long, so let me just mention that in the next week or so I'm going to write about stuff I learned while working on this application. I hope you'll enjoy the reading. Meanwhile, dust of your Scrabble board, download my app and enjoy an argument-free game!

Thursday, March 8, 2012

Customizing UI controls on Android

By customizing I mean "Oh, and can we have buttons with pink background and neon green text" requests you get from your customers. It turns out that such simple requests are in fact not simple. Take a look at the following screenshots taken on HTC Desire phone:


The "Nexus" button uses custom background selector copied from Android sources, so it looks exactly like a buttons on Nexus phones. The "HTC" button is using default background selector for HTC phones. As you can see there are few differences: highlight, corner rounding and slightly different padding. Other vendors also customize default look of UI controls: Motorola uses red highlight, and Samsung tablets use light blue highlight and there are probably some minor differences in padding and rounding as well.

Let's get back to the original problem, i.e. using pink background and neon green text. Obviously we cannot change just the text color, because it would be unreadable on HTC devices. Changing the background is also tricky, because you cannot reuse highlight graphics built into the platform resources: the different padding and corner rounding force you to use custom graphics for every state. Using layer list drawable to add some decoration is also out of the question, because of the differences in padding - most likely your decoration would be off by few pixels on some devices.

But now you have one completely custom UI control, which stands apart of built in controls. So for me it's rather "all or nothing" approach - either you customize all your UI controls (which might be very time consuming) or you use default controls.

I found one exception to this unfortunate situation: it's possible to create a ListView items with custom "normal" background and default highlight. Just use the following selector for list item background:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_selected="true" android:drawable="@android:color/transparent" />
    <item android:state_focused="true" android:drawable="@android:color/transparent" />
    <item android:state_pressed="true" android:drawable="@android:color/transparent" />
    <item android:drawable="@drawable/list_item" />
</selector>

Friday, March 2, 2012

DIP on BlackBerry

Android has this nice notion of density-independent pixel - a measurement unit which ensures the widgets have the same physical size on the devices with different screen size and different resolution. Basically 1dp is 1px on a device with 160dpi and is scaled appropriately on devices with different dpi.

Why is it important? Because if you design your UI in px on high resolution device and then run it on low resolution device, you'll end up with gigantic buttons which take 50% of the screen. If you do the opposite, you'll end up with button, which are too small to click on high res device.

Source: Android documentation

Unfortunately the BlackBerry doesn't support this concept out of the box. By default you specify the size and position of UI controls in regular pixels. The BlackBerry documentation suggests using this nugget of a code:

boolean lowRes = Display.getWidth() <= 320;

if (lowRes)
{
    // The device has a low resolution screen size
}
else
{
    // The device has a high resolution screen size
}

But I think there is a better way.

The net.rim.device.api.ui.Ui contains the static method convertSize that can be used, as you might have guessed, for size conversion between different measurement units. We'll of course convert our measurements to pixels, because that's the unit expected by most drawing methods. But what unit will we convert from?

Points are nice, because you probably use them for your font size already, but they are to coarse to be used for all components. Fortunately there is no need to write a wrapper for your own unit, because you can use built-in unit called centipoints. 100 centipoints = 1 point, so this unit should provide enough precision to layout elements just the way you want.

Tuesday, February 28, 2012

WP7 WebRequest oddity

During testing of a Windows Phone 7 application I write during my day job I noticed strange thing: when I disabled network connection, I received 404 NotFound error. More precisely, the WebRequest threw WebException with Status = UnknownError, Response.Uri = {} and Response.StatusCode = NotFound.

So you get 404 in two cases: when either connection endpoint is down or when server actually responds with 404 NotFound. It would be nice to separate those two cases though, for example to display to the user the message that actually helps them fix the problem.


Fortunately you can tell those two situations apart by checking WebException.Response.ResponseUri - in case of connection failure it contains empty Uri object (not null, just empty). Here's the extension method I use to convert the exception to the one that makes more sense to me :
public static WebResponse SaneEndGetResponse(this WebRequest request, IAsyncResult asyncResult)
{
    try
    {
        return request.EndGetResponse(asyncResult);
    }
    catch (WebException wex)
    {
        if (wex.Response != null &&
            ((HttpWebResponse)wex.Response).StatusCode == HttpStatusCode.NotFound &&
            wex.Response.ResponseUri != null &&
            String.IsNullOrEmpty(wex.Response.ResponseUri.ToString()))
        {
            throw new WebException("Network error", WebExceptionStatus.ConnectFailure);
        }
        throw;
    }
}

Friday, February 24, 2012

"Nu, Pogodi!" bought by over 2000 users

This week my "Nu, Pogodi!" game reached the 2000 downloads from Nokia Store. It might not be a lot if you compare it to the number of Angry Birds downloads, but considering that a) the game is paid, i.e. those 2000 Nokia phone users actually spent their money on my game, and b) it took about two weeks total to program it, I think it's a very good result.

Despite the fact that the game is very simple, I've learned a lot while writing and publishing it, which makes me very excited about the next, more complex programs I'm going to release and gives me enormous motivation boost. Now I just need to get to keep working before it wears off.

Thursday, February 23, 2012

Nokia Belle update and content published in Nokia Store

I've received an email from "Nu, Pogodi!" customer, saying that he cannot download my game from Nokia Store after updating his N8 phone firmware to Symbian Belle. After quick investigation in turned out that the N8 Belle device was listed as "Not compatible" in content distribution details.

When I published my game in November, the only devices running with Belle firmware were Nokia 603, 700 and 701. When the Belle update for various phones was released, the new firmwares were added to the list as "Not compatible", because that's the only reasonable default. It's prudent to prevent users from downloading application that might not work, because it's very likely that in case of any errors they'll post a negative review, and it's very hard to bump your rating from 1 or 2 stars average. I verified that everything works fine and updated the distribution metadata.

So here's the piece of advice for anyone who published some content in Nokia Store: periodically check if there are new firmwares available and update your content distribution.

Wednesday, February 22, 2012

Android LayoutInflater gotcha

Many custom Adapter tutorials contain subtle error which can be hard to find and fix. Even efficient list adapter sample from Android SDK contains this bug. If you compile and run the sample without any changes you should see something like this:


There's nothing wrong with the list on this screenshot, as long as that's the look you want. But what if you want the list items to be wider? Let's change the list item layout:

<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2007 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
  
          http://www.apache.org/licenses/LICENSE-2.0
  
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="150dip">

    <ImageView android:id="@+id/icon"
        android:layout_width="48dip"
        android:layout_height="48dip" />

    <TextView android:id="@+id/text"
        android:layout_gravity="center_vertical"
        android:layout_width="0dip"
        android:layout_weight="1.0"
        android:layout_height="wrap_content" />

</LinearLayout>

What's changed? Nothing, nada, zilch, zip. No changes whatsoever:


You can use hierarchy viewer tool to verify that the list item height was not set correctly.


There's obviously nothing wrong with the layout xml, so let's take a look at the code:

/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.example.android.apis.view;

import android.app.ListActivity;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import android.widget.ImageView;
import android.graphics.BitmapFactory;
import android.graphics.Bitmap;
import com.example.android.apis.R;

/**
 * Demonstrates how to write an efficient list adapter. The adapter used in this example binds
 * to an ImageView and to a TextView for each row in the list.
 *
 * To work efficiently the adapter implemented here uses two techniques:
 * - It reuses the convertView passed to getView() to avoid inflating View when it is not necessary
 * - It uses the ViewHolder pattern to avoid calling findViewById() when it is not necessary
 *
 * The ViewHolder pattern consists in storing a data structure in the tag of the view returned by
 * getView(). This data structures contains references to the views we want to bind data to, thus
 * avoiding calls to findViewById() every time getView() is invoked.
 */
public class List14 extends ListActivity {

    private static class EfficientAdapter extends BaseAdapter {
        private LayoutInflater mInflater;
        private Bitmap mIcon1;
        private Bitmap mIcon2;

        public EfficientAdapter(Context context) {
            // Cache the LayoutInflate to avoid asking for a new one each time.
            mInflater = LayoutInflater.from(context);

            // Icons bound to the rows.
            mIcon1 = BitmapFactory.decodeResource(context.getResources(), R.drawable.icon48x48_1);
            mIcon2 = BitmapFactory.decodeResource(context.getResources(), R.drawable.icon48x48_2);
        }

        /**
         * The number of items in the list is determined by the number of speeches
         * in our array.
         *
         * @see android.widget.ListAdapter#getCount()
         */
        public int getCount() {
            return DATA.length;
        }

        /**
         * Since the data comes from an array, just returning the index is
         * sufficent to get at the data. If we were using a more complex data
         * structure, we would return whatever object represents one row in the
         * list.
         *
         * @see android.widget.ListAdapter#getItem(int)
         */
        public Object getItem(int position) {
            return position;
        }

        /**
         * Use the array index as a unique id.
         *
         * @see android.widget.ListAdapter#getItemId(int)
         */
        public long getItemId(int position) {
            return position;
        }

        /**
         * Make a view to hold each row.
         *
         * @see android.widget.ListAdapter#getView(int, android.view.View,
         *      android.view.ViewGroup)
         */
        public View getView(int position, View convertView, ViewGroup parent) {
            // A ViewHolder keeps references to children views to avoid unneccessary calls
            // to findViewById() on each row.
            ViewHolder holder;

            // When convertView is not null, we can reuse it directly, there is no need
            // to reinflate it. We only inflate a new View when the convertView supplied
            // by ListView is null.
            if (convertView == null) {
                convertView = mInflater.inflate(R.layout.list_item_icon_text, null);

                // Creates a ViewHolder and store references to the two children views
                // we want to bind data to.
                holder = new ViewHolder();
                holder.text = (TextView) convertView.findViewById(R.id.text);
                holder.icon = (ImageView) convertView.findViewById(R.id.icon);

                convertView.setTag(holder);
            } else {
                // Get the ViewHolder back to get fast access to the TextView
                // and the ImageView.
                holder = (ViewHolder) convertView.getTag();
            }

            // Bind the data efficiently with the holder.
            holder.text.setText(DATA[position]);
            holder.icon.setImageBitmap((position & 1) == 1 ? mIcon1 : mIcon2);

            return convertView;
        }

        static class ViewHolder {
            TextView text;
            ImageView icon;
        }
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setListAdapter(new EfficientAdapter(this));
    }

    private static final String[] DATA = Cheeses.sCheeseStrings;
}

I've highlighted the problematic line. It turns out that you have to use another overload of LayoutInflater.inflate method:

convertView = mInflater.inflate(R.layout.list_item_icon_text, parent, false);

We set the attachToRoot to false, because we just want to properly intialize LayoutParams for the LinearLayout of the item and let the ListView to add the inflated views wherever it needs. In fact, setting it to true causes exception to be thrown from AdapterView:

java.lang.UnsupportedOperationException: addView(View, LayoutParams) is not supported in AdapterView
    at android.widget.AdapterView.addView(AdapterView.java:461)
    at android.view.LayoutInflater.inflate(LayoutInflater.java:416)
    at android.view.LayoutInflater.inflate(LayoutInflater.java:320)
    at android.view.LayoutInflater.inflate(LayoutInflater.java:276)
    at com.fu.InflaterBugActivity$EfficientAdapter.getView(InflaterBugActivity.java:77)
    at android.widget.AbsListView.obtainView(AbsListView.java:1430)
    at android.widget.ListView.makeAndAddView(ListView.java:1745)
    at android.widget.ListView.fillDown(ListView.java:670)
    at android.widget.ListView.fillFromTop(ListView.java:727)
    at android.widget.ListView.layoutChildren(ListView.java:1598)
    at android.widget.AbsListView.onLayout(AbsListView.java:1260)
    at android.view.View.layout(View.java:7175)
    at android.widget.FrameLayout.onLayout(FrameLayout.java:338)
    at android.view.View.layout(View.java:7175)
    at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1254)
    at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1130)
    at android.widget.LinearLayout.onLayout(LinearLayout.java:1047)
    at android.view.View.layout(View.java:7175)
    at android.widget.FrameLayout.onLayout(FrameLayout.java:338)
    at android.view.View.layout(View.java:7175)
    at android.view.ViewRoot.performTraversals(ViewRoot.java:1140)
    at android.view.ViewRoot.handleMessage(ViewRoot.java:1859)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:123)
    at android.app.ActivityThread.main(ActivityThread.java:3683)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:507)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
    at dalvik.system.NativeStart.main(Native Method)
Here's the application screenshot after the change:

Ugly, but that's exactly what we should get after setting layout height to 150dip.