Android Things makes developing connected embedded devices easy by providing the same Android development tools, best-in-class Android framework, and Google APIs that make developers successful on mobile. Android Things extends the core Android framework with additional APIs provided by the Things Support Library. These APIs allow apps to integrate with new types of hardware not found on mobile devices.

SurveyBox is one of the first production projects built using Android Things and Firebase. With it, attendees of an event can give instantaneous sentiment feedback. Event organizers are able to view this data from Firebase in real time. You may see many of these stations around I/O. Feel free to give it a whack!

What you will build

In this codelab, you're going to build your own version of the SurveyBox using Android Things and Firebase.

As the Rainbow HAT has three buttons, you will rate users being Happy, Neutral, or Sad.

What you'll learn

What you'll need

Update Android SDK

Before you begin building apps for Android Things, you must:

Assemble the hardware

Connect the Rainbow HAT to your development board. Align the 40-pin female connector on the HAT with the 40-pin male connector on the development board. Gently press the connector on the back of the Rainbow HAT onto the connector on the development board (see the kit assembly guide for more information).

If you have not already installed Android Things on your development board, follow the image flashing instructions and make sure you flash the latest system image.

Connect to the device

Verify that your development computer is properly connected to your device using the adb tool:

$ adb devices
List of devices attached
1b2f21d4e1fe0129        device

Click the following link to download the starter project for this codelab on your development machine:

Download source code

...or you can clone the GitHub repository from the command line:

$ git clone https://github.com/googlecodelabs/androidthings-surveybox.git

Unpack the downloaded zip file.

Understanding the starter project

This project contains all of the code you need.

There are several classes in the Android app:

The SurveyBox connects to buttons and LEDs in order to identify signals and provide the user with visual feedback. In this codelab, you will use the RainbowHat driver in order to open buttons and LEDs.

Import and run the starter project

Open the step1-start-here project in Android Studio and run it:

  1. Open Android Studio, and close any existing projects you may have opened with File → Close Project.
  2. Choose Import project from the welcome screen.
  3. Navigate to the project directory you downloaded in the previous step.
  4. Select the step1-start-here/android subdirectory.
  5. Click Open. The project will take a few moments to import and build.

In order to interact with peripherals such as the buttons on the Rainbow HAT, you will need to add support for GPIO (General Purpose Input / Output). The Android Things Support Library makes it easy to connect and interact with a variety of peripheral types, including buttons and LEDs.

Open ButtonActivity.kt. Update this file to add support for each button that can be pressed. To indicate the button was pressed, you will also turn on and off an LED (Light-emitting diode).

You will see that the peripherals have been defined, but not instantiated.

private lateinit var buttons: ArrayList<Button>
private lateinit var statusLed: Gpio

Find the initializeGPIO() method and add the following code snippet to connect to an LED for status updates and the buttons that you will press.

fun initializeGPIO() {
  Log.i(TAG, "Configuring GPIO pins")
  // Configure the GPIO Pins for the leds
  Log.i(TAG, "Configuring LED pins")
  statusLed = RainbowHat.openLedRed()
  // Configure the buttons to emit key events on GPIO state changes
  buttons = mutableListOf(RainbowHat.openButtonA(),
    RainbowHat.openButtonB(),
    RainbowHat.openButtonC())
}

The status LED should blink to indicate an operation in progress after a button is pressed. Let's implement a method that we can use to blink the LED. We will use this method to give the user feedback when they press a button.

private fun blinkStatusLed() {  
  statusLed.value = true
  Handler().postDelayed({
    statusLed.value = false
  }, 400)
}

Return to initializeGPIO(). Add code to listen to button presses for buttons A, B, and C on the Rainbow HAT. In addition to an event listener, also include a debounce delay.

private fun initializeGPIO() {
  ...
  for ((i, button) in buttons.withIndex()) {
    Log.i(TAG, "Registering button ${Integer.toString(i)}")
    // Create GPIO connection.
    button.setDebounceDelay(10)
    button.setOnButtonEventListener { _, pressed ->
      run {
        if (!pressed) {
          Log.d(TAG, "Button $i was pressed")
          blinkStatusLed()
        }
      }
    }
  }
}

Run the app by selecting Run → Run 'app' from the menu, or click the Run icon in the toolbar.

Press one of the buttons. You should see the red LED turn on then off. Verify that you can see the following output in logcat as the buttons are pressed:

D/ButtonActivity: Button 1 was pressed
D/ButtonActivity: Button 2 was pressed
D/ButtonActivity: Button 0 was pressed

No data will be added yet. In the next step, you will add the logic for connecting your device to Firebase.

Set up database

Button press events are stored in Firebase. To get started, create a new project in the Firebase console. Select Database and create a Realtime database.

Select the option Start in test mode to disable the security rules.

From the main page of the Firebase console, select the Add Firebase to your Android App option.

Follow the instructions and download a google-services.json file. You will need this later. Use "com.example.surveybox" as the Android package name.

Add Firebase Auth

Return to the Firebase console and select the Authentication page. Press the SET UP SIGN-IN METHOD button.

Enable Email/Password in the Sign-in providers list.

Set up Firebase

Copy the google-services.json file you downloaded earlier to your project in the app module.

Find the empty initializeFirebase method in ButtonActivity.kt. When the app starts, the device will connect to Firebase.initialize the FirebaseApp and instantiate a new DatabaseManager.

private fun initializeFirebase() {
  FirebaseApp.initializeApp(this)
  databaseManager = DatabaseManager()
}

Update your button event listeners to send data to Firebase:

private fun initializeGPIO() {
  ...
  for ((i, button) in buttons.withIndex()) {
    Log.i(TAG, "Registering button ${Integer.toString(i)}")
    // Create GPIO connection.
    button.setDebounceDelay(10)
    button.setOnButtonEventListener { _, pressed ->
      run {
        if (!pressed) {
          Log.d(TAG, "Button $button was pressed")
          databaseManager.addButtonPress(i)
          blinkStatusLed()
        }
      }
    }
  }
}

Add the following to the very bottom of app/build.gradle:

apply plugin: 'com.google.gms.google-services'

Deploy the app again to the device. Press the buttons a few times, then open up the database page in the Firebase console. Verify that several events have been added.

Now that you have been able to send data to Firebase, you will add support to a web frontend to display this data.

Now that your device is configured and reporting data to Firebase, you can display this data in a web browser in real time. Each time a button is pressed, your website can immediately update the graphs to show the changed code.

Setup Firebase on the web

Go to the Firebase console and select the Add Firebase to your web app option. Copy the block of code and insert it into a new JavaScript file called public/firebase.js.

var config = {
  apiKey: "XXX",
  authDomain: "<project-id>.firebaseapp.com",
  databaseURL: "https://<project-id>.firebaseio.com",
  projectId: "<project-id>",
  storageBucket: "<project-id>.appspot.com",
  messagingSenderId: "YYY"
};
firebase.initializeApp(config);

Add user authentication

Next, open index.html and add an auth listener to your page. If Firebase Auth has not logged in the user, the page will redirect to a login page. If the user has logged in, we will display the <my-app> tag which will render the application surface.

Open index.html and add the following to the body of index.html:

<body>
<script>
    // Track the UID of the current user.
    var currentUid = null;
    firebase.auth().onAuthStateChanged(function(user) {
        // onAuthStateChanged listener triggers every time the user ID token changes.
        // This could happen when a new user signs in or signs out.
        // It could also happen when the current user ID token expires and is refreshed.
        if (user && user.uid != currentUid) {
            // Update the UI when a new user signs in.
            // Otherwise ignore if this is a token refresh.
            // Update the current user UID.
            currentUid = user.uid;
        } else {
            // Sign out operation. Reset the current user UID.
            currentUid = null;
            console.log("no user signed in");
            document.location.href = 'login.html';
        }
    });
</script>
<my-app></my-app>
</body>

Display event data

In the web dashboard there are two types of graphs: a bar graph showing the total number of button presses for the event, and a line graph showing the number of button presses in 1-minute increments over time.

In order to display data to them, you will connect to your Firebase database using an event listener. Each time a button is pressed, your website will run a callback.

In my-app.html, find the onEventChanged() function. Add the following code to clear the data stored in the graphs and read data from Firebase that was recorded for the current event. As new data is retrieved from Firebase, the graphs will receive new data and update automatically.

onEventChanged(newValue, oldValue) {
  this.updateEvent(newValue);
  // Denote that the page is loading content
  this.$.progress.indeterminate = true;
  // Read data from Firebase
  let pressRef = firebase.database().ref(`data/${newValue}`);
  this.set('graphData.count', 0);
  this.set('graphData.data', []);
  pressRef.on('child_added', (snapshot) => {
    this.$.progress.indeterminate = true;
    buttonPress = snapshot.val();
    this.set('graphData.count', this.graphData.count + 1);
    this.push('graphData.data', buttonPress);
    this.$.progress.indeterminate = false;
  });
}

As new data is received, the data is added to graphData.data. In order for the graphs to update you will need to register observers. Find the empty observers() function and register handlers for graph data changes:

static get observers(){
  return [
    // Observer method name, followed by a list of dependencies, in parenthesis
    'onGraphChanged(graphData.data)', // Check direct changes
    'onGraphChanged(graphData.data.splices)', // Check array mutations
  ]
}

When the data is completely changed, or one element of the array has been changed, onGraphChanged will be called. We can implement that method as shown below. It will find the graphs in the page and change their value. The graphs are already defined. You don't need to change them.

onGraphChanged(newValue, oldValue) {
  window.requestAnimationFrame(() => {
    this.qc('bar-graph').set('data', newValue);
    this.qc('line-graph').set('data', newValue);
  });
}

See the results

We need to connect the Polymer app with the Firebase project you created earlier. Use the Firebase CLI tools to log in to your account, select the project you created in the previous step, and set the alias to default.

npm install
./node_modules/.bin/firebase login
./node_modules/.bin/firebase use --add

Start a local web server to host the Firebase project directly on your machine by running the following commands:

./node_modules/.bin/bower install
./node_modules/.bin/polymer serve

This will generate a localhost URL for you to open. You will be presented with a login page. Create a new account with an email and password. Then you will be navigated to the main page. You will then be able to start viewing data. Start pushing your buttons and you will see the graphs update immediately!

Congratulations! You've successfully integrated Firebase into your own device using Android Things, allowing you to measure events in real time.

Here are some ideas you can implement to go deeper.

Extend your device

Check out the Weather Station codelab to learn how to use sensors on the Rainbow Hat.

Add support for the Google Assistant by connecting a microphone and speaker.

What we've covered