teaching machines

CS 491 Lecture 12 – Wevents Part I

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

Agenda

Wevents

One of you stated in your list of desired app-knowledge that you wanted a shared calendar. Another of you shared in lecture that you wanted to talk about hooking up to a remote database. The synthesis I call Wevents, an event calendar for us. Since the events are shared, we need a remote database. I have hosting service through Bluehost, so we are going to create a database there. But first, what’s our data? Let’s take a few minutes to design a calendar view and compose a schema for the database.

Once decided, let’s:

  1. Create the database. Bluehost’s cpanel admin facilitates this.
  2. Create a user. Ditto on cpanel.
  3. Upload the schema. We need to write this and use the PHP-based administrator to run it. We’ll want to grant the user privileges on the table:
    GRANT ALL PRIVILEGES ON event TO joe_user;

    Let’s also throw in a couple of dummy insertions so that we can test some queries.

  4. Write a short PHP test to verify access:
    <?php
    $db = pg_connect("dbname=db user=joe_user password=password");
    if ($db) {
      echo "ok";
      pg_close($db);
    } else {
      echo "boo";
    }
    ?>
  5. Write a short PHP test to perform a query and export the results in a JSON structure:
    if ($db) {
      $query = "SELECT * FROM event;";
      $result = pg_exec($db, $query);
      if ($result) {
        echo json_encode(array_values(pg_fetch_all($result)));
        pg_freeresult($result);
      } else {
        echo pg_errormessage($db);
      }
      pg_close($db);
    } else {
      echo "Couldn't connect.";
    }

Authenticate

Okay, our database backend is in order. Our PHP scripts will serve as the bridge between the database and clients, who will perform all transactions through HTTP. Let’s make our Android client, starting with an activity for authentication. It uses a simple TableLayout. On button-click, we’d like to pass the username and password off the authenticate PHP script. If the PHP script reports success, we can move to our main activity. Otherwise, we sit tight.

We’ve got to ship stuff off to our PHP script. That means a trip over the network. That means don’t do it in the main thread. We could do it in a thread in our Activity, but Activitys can get destroyed if they’re not focused. Let’s farm our network tasks out to a Service. And not just any service. An IntentService, which is ideal for handling asynchronous tasks:

public class WeventsService extends IntentService {
  public WeventsService() {
    super("WebGetService");
  }

  @Override
  protected void onHandleIntent(final Intent intent) {
    // perform some task
  }
}

That onHandleIntent method will get called whenever startService is issued. To issue the HTTP request, we can use Apache’s HTTP client API:

ArrayList<NameValuePair> postParameters = new ArrayList<NameValuePair>();
postParameters.add(new BasicNameValuePair("username", intent.getStringExtra("username")));
postParameters.add(new BasicNameValuePair("password", intent.getStringExtra("password")));

int resultCode = 0;
String content = null;

HttpClient client = new DefaultHttpClient();
        
HttpPost request = new HttpPost(url);

try {
  UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(postParameters);
  request.setEntity(formEntity);
  ResponseHandler<String> handler = new BasicResponseHandler();
  content = client.execute(request, handler);
} catch (UnsupportedEncodingException e1) {
  resultCode = 1;
  content = "bad encoding";
} catch (ClientProtocolException e) {
  resultCode = 1;
  content = "client protocol error";
} catch (IOException e) {
  resultCode = 1;
  content = "I/O error";
}
client.getConnectionManager().shutdown();

There are two unresolve issues. First, Service code runs in the main thread. We need to spawn a new thread to avoid ANRs. Second, once we get the content, what do we do with it? Well, Android offers another IPC mechanism called a ResultReceiver. It imposes a callback onReceiveResult that gets called when someone sends something to it. Unfortunately, it’s a class, not an interface. Since Java doesn’t support multiple inheritance, let’s create another class which advertises its own interface and passes the results along:

public class WeventsReceiver extends ResultReceiver {
  private Receiver receiver;
  
  public WeventsReceiver(Handler handler) {
    super(handler);
  }
  
  public void setReceiver(Receiver receiver) {
    this.receiver = receiver;
  }
  
  @Override
  protected void onReceiveResult(int resultCode, Bundle resultData) {
    if (receiver != null) {
      receiver.onReceiveResult(resultCode, resultData);
    }
  }

  public interface Receiver {
    public void onReceiveResult(int resultCode, Bundle resultData);
  }
}

Now our activity can implement WeventsReceiver.Receiver. It can then handle the content however it chooses.

Animation

In the event that the user is not authenticated. Let’s animate the dialog. First, we’ll add these resources: cycle_7.xml and shake.xml. Then, on failed authentication, let’s shake:

Animation animation = AnimationUtils.loadAnimation(this, R.anim.shake);
findViewById(R.id.table).startAnimation(animation);

If we do authenticate, we can move on the next Activity.