Purr Programming Shirt Part 4 — Adding Android Wear Support

In the first three parts of this series we discussed how to create a wearable garment (in our case, a t-shirt) with LEDs, how to connect it to an Android device using MetaWear, and how to make it change colors and flash when someone tweets at you. Now, let’s add another fun feature: Triggering color changes and making the LEDs flash by using an AndroidWear smartwatch. In this example, the app is going to listen for a speech command, check what is said, and prompt the colors to change and flash if the command is valid.

How Does It Work?

Screen Shot 2016-04-14 at 2.38.47 AM

When you open the watch application, it launches a voice prompt. The user can state which pattern to use, such as “rubyfuza”. If the app recognizes the command, a Wearable.MessageApi message is sent. That message will then be read by an instance of WearableListenerService on the phone. The service creates a Broadcast Intent and sends it to the MetaWear Service, which, in turn, sends commands to the shirt to change colors or flash the LEDs in the specified pattern.

Setting Up the Wear App and Adding Voice Recognition

screen (3)
To get started, you need to add an AndroidWear module to your application.

When we open the application, we’re immediately going to launch a prompt for the user’s voice command. To do that, we create a ACTION_RECOGNIZE_SPEECH intent and pass it into startActivityForResult.

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        .
        .
        .
        val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
        intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
                RecognizerIntent.LANGUAGE_MODEL_FREE_FORM)
        // Start the activity, the intent will be populated with the speech text
        startActivityForResult(intent, SPEECH_REQUEST_CODE)

    }

When voice recognition is complete, it calls onActivityResult with the results. We’re able to read the results by overriding this method, and if it is a SPEECH_REQUEST, we can call getStringArrayListExtra on the Intent object returned in the method. The first item in the array is a string representing the user’s speech.

   override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
        if (requestCode == SPEECH_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
            val results = data.getStringArrayListExtra(
                    RecognizerIntent.EXTRA_RESULTS)
            val spokenText = results[0]
            Log.i("Speech ", spokenText)
        }

Sending the Command to the Phone

In order to send commands to a phone, we need to include the play-services dependency in the dependencies section of our wear build.gradle file.

compile 'com.google.android.gms:play-services-wearable:8.4.0'

In the main activity, we implement the GoogleApiClient.ConnectionCallbacks and GoogleApiClient.OnConnectionFailedListener interfaces.

class MainActivity : WearableActivity(), GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
    .
    .
    .
    override fun onConnected(bundle: Bundle?) {
        Log.i("on connected", "here")
    }

    override fun onConnectionSuspended(i: Int) {

    }

    override fun onConnectionFailed(connectionResult: ConnectionResult) {
        Log.i("On conneced", "failed")
    }
}

Since AndroidWear supports multiple Wear devices paired to the same phone, you need to identify which device you want to send a message to. This can be achieved by performing an AsyncTask, called at startup, that gets a list of all connected nodes. (To keep things simple in this example, we only support the user having one Android Wear device paired to the phone.) We then store the ID in a class variable to tell the messageApi which device to send a message to.

   override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        .
        .
        .
        StartWearableActivityTask().execute()
    }

   private inner class StartWearableActivityTask : AsyncTask<Void, Void, Void>() {

        override fun doInBackground(vararg args: Void): Void? {
            getNode()
            return null
        }
    }

    private fun getNode() {
        val nodes = Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).await()

        if (nodes.nodes.size > 0) {
            messageNodeId = nodes.nodes[0].id
        }
    }

Finally, we use WearableApi.sendMessage to send commands to the phone.

Wearable.MessageApi.sendMessage(mGoogleApiClient, messageNodeId,
                        command, null).setResultCallback { sendMessageResult -> Log.i("Message Sent", sendMessageResult.status.isSuccess.toString()) }

Receiving the Message

In the application on our phone, we now need to register a service that implements the WearableListenerService abstract class.

        <service
           android:name=".WatchDataListenerService"
           android:enabled="true">
                <intent-filter>
                    <action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
                </intent-filter>
        </service>

When messages are received, we parse the message path and send it as the command via a broadcast intent to the PurrProgrammingNotificationListenerService, which prompts the LEDs on the t-shirt to blink and change colors.

class WatchDataListenerService : WearableListenerService() {
    val SERVICE_NOTIFICATION = "com.polyglotprogramminginc.purrprogramming.SERVICE_NOTIFICATION"

    override fun onMessageReceived(messageEvent: MessageEvent?) {
        Log.i("message received", messageEvent!!.path)
        super.onMessageReceived(messageEvent)
        val i: Intent = Intent(SERVICE_NOTIFICATION)
        i.putExtra("command", messageEvent!!.path)
        sendBroadcast(i)
    }

}

User Feedback

screen (2)

Once a command is recognized, the user gets feedback similar to the message at the right. Instead of creating a mechanism in our AndroidWear app to prompt the user for another command, we opted for a design that requires the user to close the app via a swipe motion, and reopen it to issue another command. Please note, however, that this is not something that we would recommend for a production app.

You can find the code for our app here.

About Me: I am a Atlanta based, mobile/Android/IOS/AngularJS/Ruby developer, polyglot programmer, founder of Polyglot Programming Inc., wearable technology enthusiast and am interested in the internet of things. You will often find me purr programming and I regularly speak at conferences around the world. I am available for hire! More Posts

Follow Me:
TwitterLinkedInGoogle Plus

I am a Atlanta based, mobile/Android/IOS/AngularJS/Ruby developer, polyglot programmer, founder of Polyglot Programming Inc., wearable technology enthusiast and am interested in the internet of things. You will often find me purr programming and I regularly speak at conferences around the world. I am available for hire!

Posted in Android, Android Wear, Development, Kotlin, MetaWear, Mobile, Wearables Tagged with: , , , ,