Random thoughts, musings, and tech stuff

18 Aug 2013, 17:39

Share

Talking to Glass from Android via the Mirror API

This week, I started prototyping a stripped down Java implementation of a Mirror service for Talkray, the awesome mobile communications app that I work on at work. The typical flow for installing Mirror Glassware is to visit a website, click an ‘install’ button, pick your Google account and grant access, and Google returns you back to the website, giving the website your token. This works fine for brand new services, or web services, that already have your account, and are adept at doing OAuth. However, for a mobile app, where the account only lives on the phone and servers that don’t serve web pages, this presents a bit of a challenge. I could implement the full OAuth dance server-side, but, if I did that on the web, I’d need a way to make sure that we’re tying this to the correct account. Then, I had a crazy idea, what if I could use Android’s built-in APIs that talk to Google services for talking with Glass? Could this work? Spoiler: the short answer is no, and the long answer is a qualified yes.

What do you mean, yes and no? Does it work?

While I have not spent a ton of time with this, there seem to be a few major roadblocks in the way of making this a reasonable solution. First off, I am able to authenticate and push items to Glass from Android, and I will get into the details shortly. However, the roadblocks are not insignificant. For one thing, as far as I’m aware, I can only initiate requests from Android, I can’t handle POST requests to my app. This means that there would be no way to respond to an item on Glass back to my app.

Another issue, from what I’ve read, is that the tokens that you get from Google are only good for an hour. Google expects you to re-request tokens as you need them, and provides a way to do those re-requests without bugging the user. Unfortunately, I didn’t need to provide my OAuth key/secret to the Android app, which means that the platform is handling that. Initially, I was hoping that I could get the token on the phone and push it up to the server. That way, I could solve the difficult OAuth problem, and do just the Mirror API stuff on the server with the token that I already have. But, if the token expires every hour, I would need to be able to re-request for that token somehow, and I can’t do that from the server because the platform is handling that, instead of my app, which means that I would not be able to re-request a token from the server. (I think.) There are probably ways to work around this, but I would assume that they all involve lots of talking between your phone and the server - even if you could use that token on the server.

That said, if all you want to do is push data from the phone to Glass, and don’t need to get data back from Glass, this might work OK for you. Keep reading to see how.

How to

I’ll break this down into two main sections, the Authentication piece, and the Mirror piece. Pretty self explanatory.

Authentication

First off, I grabbed a lot of this code from the following blog post: Google OAuth on Android using AccountManager, which was incredibly helpful in getting started with this. So, go read that first, I’ll be here when you get back.

Alright, now that you’ve got an idea of how to use Android’s AccountManager, there are a few tweaks and notes that need to be made to that post. Most importantly, is an error in that post that I mentioned in the comments. When grabbing the user’s account, you need to make sure to filter on “com.google”, before I added this, I kept getting Dropbox: accountManager.getAccountsByType("com.google")

The SCOPE also needs to be changed to request permissions for the Mirror API:

Those are really the only changes to Thomas’s code that need to be made to get the authentication piece working.

Mirror

Now that you’re authenticating with the AccountManager, we need to plug in some Mirror calls. I think that I’ve found a bug here as well, but I’ll need to ping the team to make sure. Basically, when you’re building the Mirror object, you don’t pass it any credentials, instead, the token gets added to the timeline insert request.

See how that third parameter in the Builder is null?

Now, you can take a look at what we can do once we have our token:

See the call to .setOauthToken(authPreferences.getToken())?

If you have Glass and you’re running this, at this point you should see “Hello from android” inserted into your timeline. It did work for me when I was testing it last night.

Conclusion

While I’m very interested in seeing if I can push this to make a work-able solution, I’m concerned that this will be a dead-end, at least for now, given the issues that I outlined above. However, I am certainly open to hearing ideas about how this might be expanded into something that is actually useful, or a way that I could actually use the token server-side. (Obviously, nothing that Google would frown upon.)

EDIT

1

Forgot a couple of things, first, there are a few jars that you’ll need to pull in, check out the libs dir of the project. I’ll probably need to get this formatted to have maven suck those in auto-magically, but I didn’t have much time to get the code working and write this post, so hopefully soon. Second, I had an issue with getting AndroidHttp.newCompatibleTransport working, so I stuck with NetHttpTransport.

2

Looks like someone else had done this a couple months back and actually spent enough time to give it some polish. For those wondering, I don’t really bother with polishing experiments much. They might grow eventually, or they might just stay as they are. The idea is to just test something out quickly, and maybe write about it. I don’t have time for much more than that.

3

I just ran into the error: “Daily limit for unauthenticated use exceeded. Continued use requires signup.” Luckily, I figured out what the issue was, at least for me. I switched to Intellij, and one thing that I didn’t realize was that gradle was not grabbing my regular debug.keystore from ~/.android/debug.keystore. So, I tried specifying a particular keystore with gradle: