The Friends List

Sometimes you need to manage a list of friends inside your Android application. In my case the friends list is a list of contacts entries. Using a contacts entry as a friend is a good idea for your application because:
  • the user has to use a well known activity (the Edit Contact activity) to edit the friend's contact details
  • you don't have to write the code for that part :)
I use a table called friends that is kept in-sync with content://contacts/people so that it always contains an entry for each contact.
public final class FriendsColumns implements BaseColumns {
  public static final String PERSON_ID = "person";
  public static final String IS_FRIEND = "is_friend";

  // cached details from contacts
  public static final String DISPLAY_NAME = "display_name";

  // additional application-related columns
}
The column is_friend is a boolean column that marks the contact as a friend. The column display_name is filled with the contact's display name while synchronizing the table. It's purpose is only to remove the need for additional queries while rendering the list activity. For synchronization I created service that runs in a background thread and makes use of the CursorJoiner class.
  Cursor peopleCursor = contentResolver.query(Contacts.People.CONTENT_URI,
      new String[] {Contacts.People._ID, Contacts.People.DISPLAY_NAME},
      null,
      null,
      Contacts.People._ID);
  Cursor friendsCursor = contentResolver.query(Friends.CONTENT_URI,
      new String[] {Friends._ID, Friends.PERSON_ID, Friends.DISPLAY_NAME},
      null,
      null,
      Friends.PERSON_ID);

  CursorJoiner joiner = new CursorJoiner(peopleCursor,
      new String[] {Contacts.People._ID},
      friendsCursor,
      new String[] {Friends.PERSON_ID});
  Uri friendUri;
  for (CursorJoiner.Result result : joiner) {
    switch (result) {
    case LEFT:
      ContentValues insertValues = new ContentValues();
      insertValues.put(Friends.PERSON_ID, peopleCursor.getLong(0));
      insertValues.put(Friends.IS_FRIEND, 0); // false
      insertValues.put(Friends.DISPLAY_NAME, peopleCursor.getString(1));
      contentResolver.insert(Friends.CONTENT_URI, insertValues);
      break;
    case RIGHT:
      friendUri = ContentUris.withAppendedId(Friends.CONTENT_URI,
          friendsCursor.getLong(0));
      contentResolver.delete(friendUri, null, null);
      break;
    case BOTH:
      ContentValues updateValues = new ContentValues();
      updateValues.put(Friends.DISPLAY_NAME, peopleCursor.getString(1));
      friendUri = ContentUris.withAppendedId(Friends.CONTENT_URI,
          friendsCursor.getLong(0));
      contentResolver.update(friendUri, updateValues, null, null);
      break;
    }
  }
            
  friendsCursor.close();
  peopleCursor.close();
The synchronization service is started when the friends list activity is created and when the contents of content://contacts/people changed.
public void onCreate(Bundle savedInstanceState) {
  // ...

  synchronizeFriendsWithContacts();
  ContentObserver contentObserver = new ContentObserver(null) {
    @Override
    public void onChange(boolean selfChange) {
      synchronizeFriendsWithContacts();
    }
  }
  getContentResolver().registerContentObserver(Contacts.People.CONTENT_URI,
      true,
      contentObserver);

  // ...
}

private void synchronizeFriendsWithContacts() {
  startService(new Intent(this, FriendsSynchronizer.class));
}
By using this approach I achieved:
  • great performance in rendering the list activity, because of the local copies from contacts
  • when adding a new friend to the list I already have a list of remaining contacts, by using is_friend as false
  • removing a friend from the list is done by just setting is_friend to false
  • when the user removes a contact the friends list is automaticaly cleaned too
  • I don't need to write a new activity that edits the friend's contact details :)

Black Magic Skin

After some Gimp-ing I managed to make also a black skin. You can download this one too. Mandatory screenshot:

HTC Magic Skin

This will be my next phone... Because I like it so much, I created this android skin to feel much better while developing on the emulator.

This skin is available for download.

What other better way to demonstrate the skin than using some screenshots?

Emulator screenshot:



Phone buttons: