Tag Archives: android

Android: Getting Started with Databases and ContentProviders Part 1

Clone the code from GitHub

Android provides powerful database functionality built on SQLite. If you’re familiar with SQL from web development, you’ll be very comfortable with SQLite. However, what you might find is that it’s very easy to deal directly with the database (especially if you’re coming from a framework like Rails or Symfony).

Whatever language or platform you’re using, it’s always good practice to build an abstraction layer between your app’s code and its database. In this tutorial miniseries, we’ll walk through building a simple app that uses a database abstraction class, MyDatabaseHelper to manage querying, inserting, updating and deleting records from a simple database. In future posts, we’ll look at dealing with multiple tables and joins, and finally explore Android’s own ContentProvider API to abstract our database in a standard way.

Getting started

Start with a new Android application project (I recommend trying out the new Android Studio preview, especially if you’re just starting out with Android), and add the following files:

// src/com/example/MyActivity.java
public class MyActivity extends Activity {
  private MyDatabaseHelper mDatabaseHelper;
 
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
 
    mDatabaseHelper = new MyDatabaseHelper(this);
  }
}
// src/com/example/MyDatabaseHelper.java
public class MyDatabaseHelper extends SQLiteOpenHelper {
  public static final String TABLE_USERS = "users";
  public static final String COL_ID = BaseColumns._ID;
  public static final String COL_NAME = "name";
  public static final String COL_EMAIL = "email";
  public static final String COL_DOB = "date_of_birth";
  private static final String DATABASE_NAME = "my_app.db";
  private static final int DATABASE_VERSION = 1;
 
  public MyDatabaseHelper(Context context) {
    super(context, DATABASE_NAME, null, DATABASE_VERSION);
  }
 
  @Override
  public void onCreate(SQLiteDatabase db) {
    db.execSQL("CREATE TABLE " + TABLE_USERS + " ("
        + COL_ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
        + COL_NAME + " TEXT NOT NULL,"
        + COL_EMAIL + " TEXT,"
        + COL_DOB + " INTEGER"
        + ");");
  }
 
  @Override
  public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    db.execSQL("DROP TABLE IF EXISTS " + TABLE_USERS + ";");
    onCreate(db);
  }
 
  public long insert(String tableName, ContentValues values) {
    return getWritableDatabase().insert(tableName, null, values);
  }
 
  public int update(String tableName, long id, ContentValues values) {
    String selection = COL_ID + " = ?";
    String[] selectionArgs = {String.valueOf(id)};
 
    return getWritableDatabase().update(tableName, values, selection, selectionArgs);
  }
 
  public int delete(String tableName, long id) {
    String selection = COL_ID + " = ?";
    String[] selectionArgs = {String.valueOf(id)};
 
    return getWritableDatabase().delete(tableName, selection, selectionArgs);
  }
}

The advantage of this approach is that MyDatabaseHelper becomes a single point of access to the raw database (following good DRY principles). It also provides the opportunity to validate data before it is written to the database. For example, we have added a NOT NULL constraint on the name column. If we tried to save a record to the database without setting a value for name, our app would crash with a SQLiteConstraintException. Let’s demonstrate this by adding a few users in our Activity, but leaving one of the names blank:

// src/com/example/MyActivity.java
  public void onCreate(Bundle savedInstanceState) {
    // ...    
    addUser(null, null, 0);
    addUser("Joe User", "joe@example.com", 0);
    addUser("Mary Jones", "mary@example.com", 0);
    addUser("Sue Bloggs", "sue@example.com", 0);
  }
 
 private void addUser(String name, String email, long dateOfBirthMillis) {
    ContentValues values = new ContentValues();
    values.put(MyDatabaseHelper.COL_NAME, name);
 
    if (email != null) {
      values.put(MyDatabaseHelper.COL_EMAIL, email);
    }
 
    if (dateOfBirthMillis != 0) {
      values.put(MyDatabaseHelper.COL_DOB, dateOfBirthMillis);
    }
 
    mDatabaseHelper.insert(MyDatabaseHelper.TABLE_USERS, values);
  }

Running this code, your app will crash with the SQLiteConstraintException we expected:

  ERROR/SQLiteDatabase(19185): Error inserting name=null
      android.database.sqlite.SQLiteConstraintException: users.name may not be NULL (code 19)
      at android.database.sqlite.SQLiteConnection.nativeExecuteForLastInsertedRowId(Native Method)
      at android.database.sqlite.SQLiteConnection.executeForLastInsertedRowId(SQLiteConnection.java:775)
      at android.database.sqlite.SQLiteSession.executeForLastInsertedRowId(SQLiteSession.java:788)
      ....

Adding Validations

Using our abstract Database helper class though, we can protect against this and handle the error much more elegantly. To do this, we’ll add some simple validations at the appropriate points in MyDatabaseHelper:

// src/com/example/MyDatabaseHelper.java
  public long insert(String tableName, ContentValues values) throws NotValidException {
    validate(values);
 
    return getWritableDatabase().insert(tableName, null, values);
  }
 
  public int update(String tableName, long id, ContentValues values) throws NotValidException {
    validate(values);
 
    String selection = COL_ID + " = ?";
    String[] selectionArgs = {String.valueOf(id)};
 
    return getWritableDatabase().update(tableName, values, selection, selectionArgs);
  }
 
  // ...
 
  protected void validate(ContentValues values) throws NotValidException {
    if (!values.containsKey(COL_NAME) || values.getAsString(COL_NAME) == null || values.getAsString(COL_NAME).isEmpty()) {
      throw new NotValidException("User name must be set");
    }
  }
 
  public static class NotValidException extends Throwable {
    public NotValidException(String msg) {
      super(msg);
    }
  }

Back in MyActivity, wrap the call to insert() in a try...catch block to capture the NotValidException and show a message to the user:

// src/com/example/MyActivity.java 
  private void addUser(String name, String email, long dateOfBirthMillis) {
    // ...  
    try {
      mDatabaseHelper.insert(MyDatabaseHelper.TABLE_USERS, values);
    } catch (MyDatabaseHelper.NotValidException e) {
      Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
    }
  }

Now if you run your app, it will gracefully handle the error, and continue adding the other user records to the local database.

Querying and Displaying Records

At the moment, we can’t see our data though. We’ll add a quick ListView to MyActivity to show the list of user’s. If one doesn’t already exist, create a new layout resource file in your project: res/layouts/my_activity.xml and add a ListView to fill the screen:

// res/layout/my_activity.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
 
    <ListView
        android:layout_width="fill_parent"
        android:layout_height="match_parent"
        android:id="@+id/listView"/>
</LinearLayout>

In MyActivity, make sure that you set the content view to be the new my_activity.xml layout:

// src/com/example/MyActivity.java
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.my_activity);
 
    // ...
  }

Next, create a SimpleCursorAdapter to retrieve data from the database and display it in our new ListView:

// src/com/example/MyActivity.java
  public void onCreate(Bundle savedInstanceState) {
    // ...
 
    Cursor c = mDatabaseHelper.query(MyDatabaseHelper.TABLE_USERS, MyDatabaseHelper.COL_NAME);
    String[] from = new String[]{MyDatabaseHelper.COL_NAME, MyDatabaseHelper.COL_EMAIL};
    int[] to = { android.R.id.text1, android.R.id.text2 };
 
    SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_2, c, from, to, 0);
 
    ListView listView = (ListView) findViewById(R.id.listView);
    listView.setAdapter(adapter);
 
    // ...
  }

Finally, you’ll see that we’ve called a new method, query(), on our database helper. The parameters specify the table to query, and how to sort results. This will provide a simple way to retrieve the records from our database, wrapping the SQLiteOpenHelper‘s own query() method. Add the new method to MyDatabaseHelper:

 // src/com/example/MyDatabaseHelper.java
  public Cursor query(String tableName, String orderedBy) {
    String[] projection = {COL_ID, COL_NAME, COL_EMAIL, COL_DOB};
    return getReadableDatabase().query(tableName, projection, null, null, null, null, orderedBy);
  }

Of course, we could add other parameters such as search terms to our query(), making it very powerful – and again providing a single point of contact for querying our database.

Running the code, you’ll now see the list of names and email addresses as people are added to the database. Note that every time you re-run the app, another set of records will be added as the calls to addUser() are in the activity’s onCreate() method.

Screenshot of App Running

As you can see, it’s relatively easy to build a powerful abstraction layer between your database and application code, and there are huge advantages in doing so. As your app grows, you’ll start to build a library of common methods that will save you time and make your code more robust.

Coming Next

In the next tutorial, we’ll add another table to our database and adapt our database helper class to handle multiple tables and joins. In the final part, we’ll look at Android’s ContentProvider API, which provide a standard way to abstracting and manage access to your your app’s raw database.

Thanks for reading! If you’ve liked this tutorial, please let me know by leaving your comments and feedback on Twitter or Google+.

Join the free Plymouth Software newsletter today to get news, tutorials, hints and tips delivered straight to your inbox.

Testing Rendered Views with RSpec2, Capybara and Rails 3.

While view tests are brittle, easily breaking when the design of a page changes, they are undoubtedly handy for checking the important parts of a page are rendered. I usually check for page titles, model attributes/tables or forms, and footer element, and so on.

Capybara is a great DSL for testing a page’s rendered output, but unfortunately the Capybara matchers are not available in view or helper specs according to the rspec-rails documentation.

Thanks to this answer on Stack Overflow, it’s possible to load a page’s rendered content into a controller spec, and test it using the Capybara matchers.

I’m posting some example code here based on the answer for future reference, and to help anyone struggling to use Capybara with Rspec 2:

# Gemfile
group :test do
  gem 'capybara'
  gem 'rspec-rails'
end
# spec/controllers/users_controller_spec.rb
require 'spec_helper'
 
describe UsersController do
  # Render views and load the rendered content into Capybara for matching
  # See: http://stackoverflow.com/questions/4706370/rspec-view-testing-with-capybara-and-rails3/4773050#4773050#
  render_views
  let(:page) { Capybara::Node::Simple.new(@response.body) }
 
  describe "GET 'index'" do
    before { get :index }
 
    it { should respond_with(:success) }
    it { should assign_to(:users).with_kind_of(Array) }
    it { should render_template(:index) }
 
    # Test the rendered view using Capybara matchers
    it { page.should have_selector("h2") }
    it { page.should have_content("All Users") }
    it { page.should have_selector("table#users") }
    # ... add more tests ...
    it { page.should have_selector("div#footer" }
  end
end

Helpful post? Let me know by commenting on Twitter.

Android: How to test Local Web Apps on an Emulated Device (AVD)

Android AVD running local Web App

Editing your /etc/hosts file is great for running and testing web apps as they are under development. For a project I’m working on, I needed to be able to access one of my local Rails apps through an Android device, which meant editing the device’s /etc/hosts file. As I haven’t rooted my Nexus-S, so opted to use an emulated Android Virtual Device (AVD), as they are configured by default to allow root access.

I started by creating a simple hosts file on my desktop. It’s this file that I would push to the AVD

cd Desktop
vim hosts # or your favourite text editor....

Use your text editor to create a simple hosts file pointing to your local web app. Remember to also include a localhost entry:

  # hosts
  127.0.0.1       localhost 
  192.168.1.10    rails-app

Next, copy your new hosts file to the AVD:

adb push hosts /etc/hosts

Access Denied

Despite having root access, though, adb wouldn’t let me push the new hosts file because the system partition is mounted as read-only. You can remount the system partition in read-write mode using adb remount, and then try pushing the hosts file up to the device:

adb remount
abd push hosts /etc/hosts

I kept getting an Out of Memory error, which seems to be a common problem. The solution (gleaned from scattered forum posts) is to use the emulator’s -partition-size option.

Using this option means you won’t be able to launch the AVD directly from Eclipse, but instead need to use the command line. Close down any running AVDs, and then run, and then re-launch the AVD passing in the -partition-size option with a reasonable value:

emulator -avd Samsung_Galaxy-Tab -partition-size 128
 
# ...wait the avd to boot up...
 
adb remount
abd push hosts /etc/hosts

The hosts file should now successfully be copied to your AVD. If you launch the AVD’s browser and enter the local URL from your hosts file (e.g. http://rails-app:3000/), the AVD will connect to the local IP address you specified.

Note: You’ll need to keep hold of your hosts file as your settings will be wiped when you shut down the AVD. You can reinstall the file next time by performing the adb remount; adb push hosts /etc/hosts script each time you boot the AVD.

References

  1. Google Groups Thread
  2. Cute Android Tips: Failed to Copy File to System

Android: Spinner-Like UI for Selecting Multiple Options

Download the source code files AndroidMultipleChoice.tar.gz (tar.gz, eclipse project)

Android includes a Spinner control that works very much like a desktop drop-down (or combo-box) control. Tapping a Spinner presents a list of options, and allows the user to select one from the list.

However, in building a small research app, I needed to allow users to select multiple items from the Spinner’s list. From what I could see, though, the Spinner class is not capable of doing this, so I built a small throwaway app that implements this oft-requested functionality.

Getting Started

Spin up a new Android application in Eclipse, creating an activity named MultipleChoiceActivity. For reference, I’m targeting the Google Android 2.1 APIs (Level 7).

Once the project is created, open up and edit the main.xml file to display a simple label and button. I’ve used a Button instead of a Spinner as we can change the button’s label to indicate which options have been selected:

<!-- res/main.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:orientation="vertical"
  android:padding="5dip">
 
  <TextView 
    android:text="Select Colours"
    android:id="@+id/textView1"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:padding="5dip" />
 
  <Button 
    android:layout_height="wrap_content"
    android:layout_width="fill_parent"
    android:text="- None Selected -"
    android:id="@+id/select_colours"
    android:padding="5dip" />
</LinearLayout>

Building the Activity

For this example, I’m using a hard-coded list of colours here in this example (rather than, for example, hooking up to a database). Start by hooking up the interface to properties in MultipleChoiceActivity.java:

// src/com/example/multiple_choice/MultipleChoiceActivity.java
 
// ... package, imports ...
 
public class MultipleChoiceActivity extends Activity implements OnClickListener {
  protected Button selectColoursButton;
 
  protected CharSequence[] colours = { "Red", "Green", "Blue", "Yellow", "Orange", "Purple" };
  protected ArrayList<CharSequence> selectedColours = new ArrayList<CharSequence>();
 
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
 
    selectColoursButton = (Button) findViewById(R.id.select_colours);
    selectColoursButton.setOnClickListener(this);
  }
 
  @Override
  public void onClick(View view) {
    switch(view.getId()) {
      case R.id.select_colours:
        // TODO: Show the colours dialog
        break;
 
      default:
        break;
    }
  }
}

With that code, your button is now hooked up to listen for clicks. The colours array is a simple list of colours that will be displayed when we click the button. The selectedColours ArrayList is a dynamic array of the colours that have been selected. Using ArrayList lets us easily add and remove colours when the user changes the selection.

Try running the app in the Android emulator (or on a device) to check that everything works so far.

Displaying the Options

Next, we’ll use Android’s AlertDialog to let us display the list of colours, and allow the user to make selections. AlertDialog comes with built-in functionality to allow multiple selections, and automatically adds checkboxes to our list interface.

Add the following method to MultipleChoiceActivity.java:

// src/com/example/multiple_choice/MultipleChoiceActivity.java
protected void showSelectColoursDialog() {
  boolean[] checkedColours = new boolean[colours.length];
  int count = colours.length;
 
  for(int i = 0; i < count; i++)
    checkedColours[i] = selectedColours.contains(colours[i]);
 
  DialogInterface.OnMultiChoiceClickListener coloursDialogListener = new DialogInterface.OnMultiChoiceClickListener() {
   @Override
    public void onClick(DialogInterface dialog, int which, boolean isChecked) {
      if(isChecked)
        selectedColours.add(colours[which]);
      else
        selectedColours.remove(colours[which]);
 
      onChangeSelectedColours();
    }
   };
 
  AlertDialog.Builder builder = new AlertDialog.Builder(this);
  builder.setTitle("Select Colours");
  builder.setMultiChoiceItems(colours, checkedColours, coloursDialogListener);
 
  AlertDialog dialog = builder.create();
  dialog.show();
}

showSelectColoursDialog is the method that will be called when tapping the button. It starts by walking through the colours array and checking which colours are already in the selectedColours ArrayList. The results get stored in an array of booleans (checkedColours) that will be used by the AlertDialog to determine which items in the list are to be checked.

Next, we build a DialogInterface.OnMultiChoiceClickListener that will be invoked when the user checks or unchecks a colour in the list. Depending on the state of the check box (supplied via the isChecked parameter), we add or remove the colour from the selectedColours ArrayList.

Notice that when the listener is finished, we call an as-yet undefined method, onChangeSelectedColours. This will be a callback method that we’ll use to update the button’s label:

// src/com/example/multiple_choice/MultipleChoiceActivity.java
protected void onChangeSelectedColours() {
  StringBuilder stringBuilder = new StringBuilder();
 
  for(CharSequence colour : selectedColours)
    stringBuilder.append(colour + ",");
 
  selectColoursButton.setText(stringBuilder.toString());
}

This simple callback method just walks through the ArrayList of selected colours, and builds a string containing the name of each colour.

Finally, all that’s left to do is hook up our button to call showSelectColoursDialog in the onClick method we overrode earlier:

// src/com/example/multiple_choice/MultipleChoiceActivity.java
@Override
public void onClick(View view) {
  switch(view.getId()) {
    case R.id.select_colours:
      showSelectColoursDialog();
      break;
 
    default:
      break;
  }
}

With everything hooked up, it’s time to test out the app. Run the emulator (or install to a device), tap the button and you’ll be able to select multiple colours from the list. If you tap the back button, the list will close, and your button’s label will have updated to show the colours you tapped. Try tapping the button again, and your previously selected colours will still be checked.

Summary

Hopefully this simple example will be useful to anyone looking to implement multiple-choice options. It would be nice to use the Spinner gadget rather than a Button, perhaps by subclassing Spinner and overriding it’s click handler and label drawing methods. I haven’t tried this – let me know in the comments if you decide to give it a go.

Introducing Outlime: Rapid Sketching for Android

Update: Outlime has now been discontinued.

Update 26 August 2010: The Outlime product page is now live at plymouthsoftware.com/outlime.

I’m pleased to announce the publication of my first app for Android: Outlime.

Outlime is a very minimalist sketching app for Android that lets you quickly commit ideas and concepts from your head to your phone. Outlime costs just £1.29 and is available from the Android Market. You can find out more about Outlime at on the product page.

Example sketches in Outlime

Outlime is inspired by 37Signals’ Draft for iPad. A couple of weeks ago, lots of ideas were spinning round my head for new Amberleaf screens. I was on the verge of buying (another) whiteboard, but resisted cluttering and instead kept scribbling on endless piles of sticky notes.

Just at that time, I stumbled across Draft and hoped that such a minimalist app existed on Android. I couldn’t find one, so instead set about creating the app for myself.

The result is Outlime. Following the extremely minimal concepts that inspired it, Outlime offers two pens (white and green) and an eraser. Sketches are automatically saved when you load another sketch, or exit the app.

Built for Android

Outlime takes advantages of Android technologies, like Intents for sharing Outlime sketches with any registered image app on your phone. This makes it simple to send sketches via any appropriate app installed on your phone, such as Gmail, Facebook, Picasa, and so on. Outlime also leverages the Android MediaStore, so your sketches show up in your phone’s Gallery app.

Outlime benefits from larger screens, although I’ve found it pretty handy for sketching quick layouts on my phone. Although it was developed on a 3″ Pulse, some of the newer Android devices are sporting 4″, 5″ and larger screens.

Writing Outlime has been a great learning experience for me in dealing with the intricacies of the Android SDK (although I have suffered serious ruby-withdrawal!), and in preparing an app for the Market. I’ve got some ideas how I’d like to develop Outlime. If you try out, let me know what you think by tweeting @cblunt or @plymsoftware, or commenting below.

Get Outlime from Android Market

Outlime costs just £1.29 and is available now from Android Market. You can scan the QR code below with your Android Device to view Outlime in the Market (use Barcode Scanner or similar).