Database versioning, Android style

Question: how can we track database schema upgrades? How can we make sure our database structure matches the deployed code?

One answer: in Android’s SQLite database, they solve this problem by storing a version number in   a database property. When an application opens a connection, the version number in the code is checked against the version number in the database. If they don’t match, Android calls a hook to let the application update the schema.

For our web app purposes, we used the idea of storing the version number in the database. We threw it in a table. Our upgrade scripts are separate from the code, but they still do the job of converting data, applying DDL changes, and finally increasing the version number in the database.

An application should fail early if the database is out of date. For this, we created a ConnectionProvider that validates the version number when a database connection is established. Thus, there is a version stored in the database and a version hard-coded into the application. If we forget to upgrade the dev or test database before deploying the corresponding code, we find out on startup.

Programming experience in a different environment made our lives easier this day. Design was simple because we were aware of a pattern that worked in one architecture. It adapted well to ours.

Android: Confirmation Dialog

Warning! This technique is suited to Android API Level 8-10 (Gingerbread). For Honeycomb or newer, use DialogFragment.

Problem: my activity knows how to do something (delete a picture), but it needs to ask the user for confirmation first.

Solution: pop a confirmation dialog, whose “yes” button responds by deleting the picture.

Code:
For the very simplest case, where

  • the activity uses no other dialogs
  • the message in the dialog never changes

then two steps are required:

  1. Instead of taking the action, call showDialog.

    private void deletePicture() {
        showDialog(0);
    }
(where 0 is any integer – we don’t care because this is the only dialog used by this activity)

  1. override onCreateDialog(int) to return the dialog object. Building the confirmation dialog is a good-sized chunk of code. The following sample create pulls the creation of a Yes/No dialog into a method, so that the defining of the action we want to take on “Yes” is separated from the cruft of creating this simple-case dialog.

    @Override
    protected Dialog onCreateDialog(int id) {
        return createConfirmationDialog(“Delete this picture?”, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                reallyDeleteThePicture();
            }
        });
    }


    private AlertDialog createConfirmationDialog(String message, DialogInterface.OnClickListener yesAction) {
        return new AlertDialog.Builder(this).setMessage(message)
                .setPositiveButton(“Yes”, yesAction)
                .setNegativeButton(“No”, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        dialogInterface.cancel();
                    }
                }).create();
    }

Unfortunately the code that executes on “yes” is obfuscated by Java’s painful syntax, and we all bang our heads on our desks and wish for Java 8. Good thing blog posts have color. If you’re here to cut and paste, then replace the red message with your text and the green method call with your own code, and you’re done.

What is happening here? showDialog triggers a call to onCreateDialog. Here we’re ignoring the int argument because we only ever use one dialog in this activity. AlertDialog.Builder is used to create a dialog with two buttons: Yes, which performs the desired action wrapped in an OnClickListener; and No, which closes the dialog and takes no other action. The user can also close the dialog by pressing back.

The onCreateDialog method will be called only once the first time the user activates the deletePicture() functionality. After that, the same dialog object will be reused.

Let’s look at what we need to do in case our assumptions are still not valid. This is still just a confirmation dialog, but what if:

  • the activity uses another dialog.

Suddenly the int passed to showDialog and onCreateDialog matters. They match up, so that the one override onCreateDialog can handle construction of two different dialogs.

Code: Perhaps we also need to confirm overwriting some changes. The below example makes use of the createConfirmationDialog method defined above.

    private static final int DIALOG_DELETE_PICTURE = 1;
    private static final int DIALOG_OVERWRITE_PICTURE = 2;

    private void deletePicture() {
        showDialog(DIALOG_DELETE_PICTURE);
    }


    private void savePicture() {
        showDialog(DIALOG_OVERWRITE_PICTURE);
    }


    @Override
    protected Dialog onCreateDialog(int id) {
        switch (id) {
            case DIALOG_DELETE_PICTURE:
                return createConfirmationDialog(“Delete this picture?”, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        reallyDeletePicture();
                    }
                });
            case DIALOG_OVERWRITE_PICTURE:
                return createConfirmationDialog(“Save changes? This will overwrite the original picture.”, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        reallySavePicture();
                    }
                });
        }
        throw new IllegalArgumentException(“unknown dialog ID: ” + id);
    }

This onCreateDialog method will be called up to twice in the activity’s lifetime, once per id.

That wasn’t too bad. Now what if

  • the message is different each time the dialog pops

Since onCreateDialog is called only once for each dialog ID, there’s another hook for customizing the dialog per use. Override onPrepareDialog(int, Dialog).

    @Override
    protected void onPrepareDialog(int id, Dialog dialog) {
        switch (id) {
            case DIALOG_DELETE_PICTURE:
                ((AlertDialog) dialog).setMessage(“delete picture ” + pictureDescription + ” ?”);
                break;
            default:
                super.onPrepareDialog(id, dialog);
        }
    }

These are simple cases for a simple dialog. For anything more complicated, dig around in the user guide.
This isn’t pretty code, with the same concern (this simple dialog) scattered around the activity’s code. For Honeycomb and higher, this is abstracted into a DialogFragment, which looks like more fun. Too bad my phone is on Gingerbread.

Cross-platform problem solving: how an Android Pro Tip helps our web app design

Separation of concerns is a challenge in a web application. The quick, straightforward way to control a page winds up being messy, with business logic and application logic and view logic all mushed together.

Here at my Stealth-Mode Startup, we’re using the Sitebricks web framework on top of Guice. One pain point in our webapps’ code is that the Sitebricks Page objects wind up serving too many purposes. They control what’s displayed to the user, what actions are taken on an HTTP request, and what data is available to the view. We grimace when we think too hard about them.

Last month at CodeMash, I saw a presentation by Michael Pardo on Android Pro Tips. Android’s Activity objects have something in common with our Sitebricks Page objects: they control and populate the view as well as performing application tasks. One pattern Michael presented in the Android talk was the ViewHolder: an inner class inside the Activity takes responsibility for interacting with the view. The ViewHolder knows about the IDs of the particular components in the layout, isolating this from the rest of the activity.

We’re going to try implementing this ViewHolder pattern in our web application. I love that a solution designed for one platform solves the same problem on another platform. Also, this shows how attending development conferences and learning about various technologies is relevant to my job in unexpected ways.

IntelliJ IDEA 11

The new version of IntelliJ IDEA is out, and I’m liking it.

Gradle integration: creating a new project from a build.gradle was smooth and easy. Then when dependencies were added to the gradle file, the gradle plugin came in handy. One line added to build.gradle

apply plugin: 'idea'

and then one execution of

gradle idea

updated the dependencies without losing my other settings.

Git integration: it’s built-in, but I haven’t tried it yet. I’m still making friends with git at the command line. in the meantime, I added .idea, *.ipr and *.iws to the .gitignore list.

Android integration: this is smooth. It was a little rough in the last version. In 11, it’s slick so far. Clicking on an R.id.viewName takes me directly to the definition of that layout element in the XML. The IDE also has a GUI editor for creating layouts — priceless. (I do have an issue with the resources not being regenerated and packaged correctly on changes to layouts. I haven’t yet narrowed it down closely enough to report a bug. Rebuilding the project fixes it.)

Final variables: Type an expression, then press ctrl-alt-v for “create a local variable to store the results of this expression.” A little thought bubble appears containing a checkbox for “Declare final.” Having checked that once, my created variables are now final by default. Hurray!

JetBrains supports the local user group community by giving free licenses to attendees (one random giveaway at each meeting of the St. Louis JUG when at least thirty people show up, which is most of the time). JetBrains also supplies licenses to leaders of the JUG. This is a great reason to be active in the local user group community!

Setting up an Android project in IntelliJ Idea

And there was pain…

I imported an existing Android project into IntelliJ Idea Community Edition 10.5.1. Some things started complaining “Please select Android SDK.”

The help pages are out of date; they say to do this in the Android facet settings. It isn’t there.

Here is the secret do-things-right button: Module settings (ctrl-alt-shift-S), then pick Project, and then you need to change the Project SDK (1). To teach Idea where your Android SDK is, click New, and lo! There it is! The Android SDK option!

Choose Android SDK, and then select the home directory of your Android SDK installation. It will then ask you to choose a target API level. Now, the Android SDK is set.

The code still didn’t compile and the Run task still complained “Please select Android SDK.” Strangely, it seemed to have lost the SDK selection. I restarted Idea, opened the project structure options, and chose the Android SDK again. This time in “Edit Run Configurations”, I was able to select the virtual device to run on.

If you know a better way to accomplish this setup, please comment.

(1) You could follow these steps at the module level, too.