Google I/O 2013 Demo: Android Studio + Cloud Endpoints – Synchronized StopWatch Demo

At Google IO, during the What’s New in Android Developer Tools session, I had the opportunity to demo the Cloud Platform features that are shipping as part of the new Android Studio IDE.

When we first started talking about the demos for IO, I boldly claimed that *any* Android application would be better if it was cloud connected so the details of what app we used didn’t matter to me.  But, I’ll admit, I was a little stumped when they settled on Rich Hyndman’s Ultimate StopWatch app from the Android SDK.  While it is a great app and has some cool client UI, I wasn’t sure how to cloud-enable it.   I thought about storing your last few timings in the cloud, but that seemed a little lame.   Then it occurred to me – a synchronized stopwatch!   We could use the cloud to keep two stop watches in sync.  As you will see below, it didn’t take long to build that out.  We had a great time playing with it internally, so the demo was on!   And I think my record is stands as being able to cloud connect any Android application ;-).

The basic architecture of the application looks like this:

  1. the initiating stopwatch app hits “START”,  “PAUSE” or “RESET” and a web request is made up to the app engine app.
  2. the app engine app then sends a notification to Google Cloud Messaging for Android (GCM) for each devices that has registered.
  3. GCM then sends a trickle down each device notifying it that there is new state (Start or Stop and a time) to get.
  4. Each application then sends a request to the app engine app to get the new state.  When that new state arrives it updates its own UI to match.

Add an App Engine Backend

To get started, the first step is to add an App Engine backend to the StopWatch project.   Luckily Android Studio has built in support for this.  Simply select Tools, then Google Cloud Endpoints and Generate App Engine Backend.

Now we are prompted for three bits of information to enable push notification.    First you head over to http://cloud.google.com/console and create a new project.

Then you grab the project ID

The Project ID ends up

  1. In the App Engine project under WEB-INF/appengine-web.xml
     <application>cloudstopwatchdemo</application>
  2. In each of the Deviceinfoendpoint.java and Stopwatchstateendpoint.java in the -Endpoints project.
     public static final String DEFAULT_ROOT_URL = "https://cloudstopwatchdemo.appspot.com/_ah/api/";

Then to get the project number, click on the text the says “Project ID” and you will get the project number.

The Project Number ends up in the Android project, in GCMIntentService.java

protected static final String PROJECT_NUMBER = "126118236332"

Getting the API Key for GCM  you enable Google Cloud Message for Android in the APIs list.  First select the APIs page.

Then activate Google Cloud Messaging for Android

Finally, Select Register App.  In this case, we are registering the app engine app to be able able to call the GCM API we just enabled.   I called it Mobile Backend.  

From there I can grab the Server API key (scratched out below)

The API key ends up in the App Engine project, in StopWatchStateEndpoint.java

   private static final String API_KEY = "<<fill in>>";

When this completes you end up with a project structure  should include a folder for the App Engine part of the project, with some code generated for you to get started.

You can see we have two new modules here.

  • stopwatch-AppEngine contains a stub implementation of a server backend for our app.  By default it handles the bookkeeping around GCM registration.   But we will see in a bit how to add our own custom logic to it.

  • stopwach-endpoints contains client side proxies for the endpoints defined in stopwatch-AppEngine.   We will use this module directly for our Android app.

Notice the tooling has also modified  stopwatch/build.gradle to add a dependency on stopwatch-endpoints.

dependencies {
 compile project(':stopwatch-endpoints')
}

You will also notice that Android Studio will download from Maven all the required dependencies for this project.   This makes configuration a snap!

Add a Custom Endpoint to the App Engine App

Now we need to add some code to our App Engine app to keep up with the state of the stopwatch, so it can be mirrored to the other instances of the app running on different machines.   One easy way to start off is to think about what state we want to store.

In our case it is pretty simple.  Basically we just need to know if the stopwatch is running or not (we don’t care if it is paused or stopped) and the timestamp — the time currently showing on the stopwatch display, this ensures that all the instances end up with exactly the same time.     We are also storing a primary key, and the source device ID so we can avoid sending updates to the same device that posted the update.

public class StopWatchState {
private Long key;
   private String sourceDevice;
   private double timestamp;
   private long serverTimestamp;
   private boolean running;
}

Now we just need a add a few attributes to make this an entity that App Engine’s datastore knows how to store and give it some getters and setters and we end up with

package com.geekyouup.Android.ustopwatch;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class StopWatchState {
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private Long key;
   private String sourceDevice;
   private double timestamp;
   private long serverTimestamp;
   private boolean running;
   public Long getKey() {
       return key;
   }
   public void setKey(Long key) {
       this.key = key;
   }
   public String getSourceDevice() {
       return sourceDevice;
   }
   public void setSourceDevice(String sourceDevice) {
       this.sourceDevice = sourceDevice;
   }
   public double getTimestamp() {
       return timestamp;
   }
   public void setTimestamp(double timestamp) {
       this.timestamp = timestamp;
   }
   public long getServerTimestamp() {
       return serverTimestamp;
   }
   public void setServerTimestamp(long serverTimestamp) {
       this.serverTimestamp = serverTimestamp;
   }
   public boolean isRunning() {
       return running;
   }
   public void setRunning(boolean running) {
       this.running = running;
   }
}

Great, we have the data we want to store, now we just need to expose accessing that data via web APIs.   This is where Cloud Endpoints comes in.    Simply select the StopWatchState class we just added, then select Tools, Google Cloud Endpoints and Generate Endpoints.

This will generate the server side code that does the basic CRUD operations on our StopWatchState class.   It handles all the logic to store and retrieve instances of StopWatchState from the App Engine DataStore (possibly the biggest public no-sql datastore on the planet doing over 4.5 trillion transactions per month with 99.95% uptime).

This operation adds StopWatchStateEndpoint.java to the App Engine project with implementations such as

   @ApiMethod(name = "getStopWatchState")
 public StopWatchState getStopWatchState(@Named("id") Long id) {
       EntityManager mgr = getEntityManager();
       StopWatchState stopWatchState = null;
       try {
           stopWatchState = mgr.find(StopWatchState.class, id);
       } finally {
           mgr.close();
       }
       return stopWatchState;
   }

Now I can run this server logic locally just to test it out

Then, we can check out our API in the API Explorer at http://localhost:8080/_ah/api/explorer .  This is really great for debugging *just* the server side of your app as well as understanding the REST interface.

Ok — so now we have a basic cloud based backend  working, we can focus on calling it from our Android client.  To do that we need to generate a strongly typed, Android optimized client library.     That is easy enough in Android Studio…  Simple select Tool, Cloud Endpoints and Generate Client Libraries.  

This creates a strongly typed, Android optimized client library for our new service and adds that library to the stopwatch-endpoints module.  This is now ready to call from our Android application.

Now, the first time you use Stopwatchstateendpoint, you will need to import it into your Android app.  But Android Studio makes that very easy..

Then, calling the server methods are very easy..  for example:

mStopwatchStateEndpoint.getStopWatchState(0l).execute();
mStopwatchStateEndpoint
    .insertStopWatchState(new StopWatchState()).execute();

Of course, you need to do this on a background thread so they don’t block the UI thread.

Now, to see it really work, you need to run two instances of the app.   Starting and stopping the stopwatch on one device, stops and stops it on all the other devices as well.

To deploy your generated App Engine project to the Cloud, click on the “Maven Projects” button on the right edge of the IDE, under Plugins->App Engine, right-click and run the appengine:update goal.

Then when we run this app, it reports up to the server start and stop and start state

s

Opening up the Cloud Console, we can see the data stored on the server in the DataStore viewer.

Now if we run two instances, you should see the stopwatches staying in sync.

Special thanks to Appu Goundan who actually wrote this demo, mostly in the wee hours of the morning :-)

Remember, you can:

Good Luck!

2 thoughts on “Google I/O 2013 Demo: Android Studio + Cloud Endpoints – Synchronized StopWatch Demo

  1. Pingback: Dew Drop – June 24, 2013 (#1,571) | Alvin Ashcraft's Morning Dew