teaching machines

CS 491 Lecture 9 – Rattle Part II

Agenda

  • add an edit Activity to Rattler
  • sharing data across Activitys
  • sharing global data across all Activitys
  • update a database record
  • writing a custom adapter
  • playing a song
  • speaking a song

Editing

Making a user-friendly UI for editing will take some work. Let’s just mock up an Activity that displays a song’s fields in some TextViews and an EditText.

First, we need to communicate the ID of the song to be edited to the EditActivity. How can we communicate information to other Activitys? The Intent can take key-value “extras” that the spawned Activity can receive. On the requester side, we have:

Intent intent = new Intent(this, EditActivity.class);
intent.putExtra("id", song.getID());
startActivity(intent);

And on the fulfiller side, we have:

long id = getIntent().getLongExtra("id", -1);

Singleton

Our one database will be shared across multiple activities in our app. It’d be nice to just share one instance. Google recommends creating a singleton for this kind of thing:

public class Singleton {
  private static Singleton soleInstance = null;

  private Singleton() {
    ...
  }

  public static Singleton getInstance() {
    if (soleInstance == null) {
      soleInstance = new Singleton();
    }
    return soleInstance;
  }
}

We could follow the singleton pattern for our database, but getInstance needs the creation context. Each activity has its own context, which may get destroyed and recreated. So, we probably don’t want to build our singleton off that.

The overall application housing the Activitys has a context too, which outlives the individual Activitys. We could just tell the caller to pass in the application context. We’d prefer to enforce this at the API level (which we could probably do by having them pass the Application in instead). But there’s a cleaner way. We can store global state in our Application subclass:

public class RattlerApplication extends Application {
  private SongsDatabase songsDB;

  @Override
  public void onCreate() {
    super.onCreate();
    songsDB = new SongsDatabase(this);
  }

  public SongsDatabase getSongsDatabase() {
    return songsDB;
  }
}

We then have to go to the manifest and add an

android:name=".RattlerApplication"

attribute to our application. Now our Activitys can just reach into the application and grab the database. Like the EditActivity, which needs to update the database on save.

Custom adapter

An adapter is the bridge between your model (data) and its views. Write now, we’re using the ArrayAdapter with a builtin layout. Let’s scrap this and use our own, which will put a play button on each menu item. We’ll need to perform these steps:

  • Create a custom layout for a menu item. A horizontal linear layout with a TextView and an ImageView will work nicely.
  • Subclass ArrayAdapter and hook up each song to the view described in XML.
  • Add a button-click listener to play the associated song.

Playing a song

We’ve seen MediaPlayer is great for playing resources. Here, our song is stuck inside a database. Unfortunately, the only way at present to get it to play our songs is to first write the song out to a file:

FileOutputStream out;
try {
  out = context.openFileOutput("song.rtttl", Context.MODE_WORLD_READABLE);
  out.write((song.getName() + ":d=4,o=5,b=120:" + song.getMusic()).getBytes());
  out.close();
          
  Uri uri = Uri.fromFile(new File(context.getFilesDir() + "/song.rtttl"));
  MediaPlayer player = MediaPlayer.create(context, uri);
  player.start();
} catch (IOException e) {
  e.printStackTrace();
}

Speaking a song (or getting the result of an Activity)

Speech recognition on Android is pretty slick, though it requires an Internet connection. The basic idea is this:

  1. Spawn an ACTION_RECOGNIZER intent with an EXTRA_LANGUAGE_MODEL=LANGUAGE_MODEL_FREE_FORM extra.
  2. Await and handle the result in onActivityResult.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *