teaching machines

CS 491 Lecture 15 – Wevents Part IV

October 27, 2011 by . Filed under cs491 mobile, fall 2011, lectures.

Agenda

Problem

We mentioned last time that the advantage of an IntentService + ResultReceiver over an AsyncTask is only seen when the background task does more than update the UI, like maintain a local cache. Such a cache is useful for when the device is not connected to a network. But how do we do it? Spend some time thinking out loud with your neighbor on how best to incorporate a cache into Wevents.

Updates

Since last lecture, I’d added a database, an Event class, an EditActivity, and an Event adapter.

Parcelable

When you need to send complex data down the wire or to a file, you must define some sort of protocol so that the ultimately receiver can unpack it and recreate the structure on the other end. In our case, even passing an event between Activitys requires us to marshall or serialize it. The good news is the packing and unpacking process is semi-automated, provided we have our compound data structure implement the Parcelable interface. We must also:

There’s something really awkward about this whole design, but alas, what can we do?

Now we can pass our event for editing to the EditActivity and return our modified event back.

Inserting and editing

On receipt of the edited event, we can go ask the service to push out the changes:

@Override
protected void onActivityResult(int requestCode,
                                int resultCode,
                                Intent data) {
  if (requestCode == REQUEST_EDIT) {
    if (resultCode == RESULT_OK) {
      Intent intent = new Intent(WeventsApplication.ACTION_EDIT, null, WeventsActivity.this, WeventsService.class);
      intent.putExtra("username", username);
      intent.putExtra("password", password);
      intent.putExtra("receiver", receiver);
      intent.putExtra("event", data.getParcelableExtra("event"));
      startService(intent);
    }
  } else {
    super.onActivityResult(requestCode, resultCode, data);
  }
}

The PHP side of things looks like:

<?php
$db = pg_connect("dbname=DB_NAME user=${_POST['username']} password=${_POST['password']}");
if ($db) {
  if ($_POST['id'] == -1) {
    $result = pg_prepare($db, "my_query", "INSERT INTO event(title, location) VALUES ($1, $2)");
    $result = pg_execute($db, "my_query", array($_POST['title'], $_POST['location']));
  } else {
    $result = pg_prepare($db, "my_query", "UPDATE event SET title = $1, location = $2 WHERE id = $3");
    $result = pg_execute($db, "my_query", array($_POST['title'], $_POST['location'], $_POST['id']));
  }
  echo "ok";
} else {
  echo "Couldn't connect";
}
?>

On the Service side, we need to handle the new action. Instead of return the raw JSON string, let’s compose an Event array and marshall it back.

When the service cues us back, let’s reload the list adapter.

Detecting a network connection

Android offers a Service for checking the network access state. Through it we can see if we are connected:

public static boolean isNetworkAvailable(Context context) {
  ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
  NetworkInfo info = manager.getActiveNetworkInfo();
  return info != null && info.isConnected();
}

Using the local cache

Our Service is responsible for fetching the data right now. In the event that a network connection is not available, we want the Service to use cached results. That means we need to have cached the results from when we did have a connection. We’ll need to perform the following tasks to make our cache work:

How do we handle the authentication screen when there’s no network?

Checking for connection

We could poll every so often and check for a new network connection, but polling is for micromanagers. Instead, we’ll use a BroadcastReceiver and await notice from the system. Here’s our receiver:

public class NetworkReceiver extends BroadcastReceiver {
  @Override
  public void onReceive(Context context,
                        Intent intent) {
    if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
      boolean hasConnection = !intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
      Log.d("WEVENTS", "connection: " + hasConnection);
    }
  }
}

And in the manifest:

<receiver android:name=".NetworkReceiver">
  <intent-filter>
    <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
  </intent-filter>
</receiver>