Connecting with Android Wear APIs
I have developed several Android apps with Android Wear support already, along with a new game that is still raising money on Kickstarter. Along with this has come experience with the Android Wear connectivity APIs, namely the Data Layer and Messaging APIs. As per a request, I will create a simple example app for mobile and wear that will allow messages and data to be sent between them using practices I’ve developed.
First, we need to create a project and include modules for both mobile and wear. Additionally, we will have to add certain dependencies to our gradle files to get the necessary libraries.
MOBILE
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
wearApp project(':wear')
compile 'com.android.support:appcompat-v7:22.2.0'
compile 'com.google.android.gms:play-services-wearable:7.5.0'
}
WEAR
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.google.android.support:wearable:1.1.0'
compile 'com.google.android.gms:play-services-wearable:7.5.0'
}
After this gets compiled, we can get started by creating activities. Let’s start with the mobile activity:
public class Home extends AppCompatActivity implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
mIsInResolution = savedInstanceState.getBoolean(KEY_IN_RESOLUTION, false);
}
setContentView(R.layout.activity_home);
}
We create a new activity (and make sure it’s added to the manifest) and program what happens when the activity is first created. It displays the layout `activity_home`.
Okay, this all seems very basic to you because you already have some Android experience. Let’s skip ahead. We’ll connect to Google Play Services and handle what happens after we connect.
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApiIfAvailable(Wearable.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();@Override
public void onConnected(Bundle connectionHint) {
Log.i(TAG, "GoogleApiClient connected");
}
Now that we’ve connected, we can send messages or sync data. These are the two main types of communication between a phone and a watch (or between two watches). Messages are node to node, useful for one time things or to initiate actions. If I send a message from my phone to one of my watches, the information is sent only between those two devices one time.
Messages are a bit complicated to set up at first because you need to find the correct node. That’s why I created a helper class that hides away a lot of the boilerplate code. ConnectionUtils makes it easy to send messages.
First you have to add file, `values/wear.xml` that contains the capabilities of that specific device.
MOBILE
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="CAPABILITY_MOBILE">MOBILE</string>
<string name="CAPABILITY_ANDROID_WEAR">WEARABLE</string>
<string-array name="android_wear_capabilities">
<item>@string/CAPABILITY_MOBILE</item>
</string-array>
</resources>
On the phone, the CAPABILITY_MOBILE item is active. This makes it easy to target when using this class.
You can create a `NodeManager` and include the capability you wish to target. In the example construction below, in an example watch app, it is looking for any node that is a phone.
nodeManager = new ConnectionUtils.NodeManager(gapi, getString(R.string.CAPABILITY_MOBILE), MessageListener.CONFIGURATION_PATH);
You can register a listener to run an action when a correct node is found:
nodeManager.setNodeListener(new ConnectionUtils.NodeListener() {
@Override
public void onNodeFound() {
}
When you have a node, you will be able to send a message to it.
nodeManager.sendMessage("Open");
This can be received in a companion app using an activity (if that activity is currently open) or through a background service.
To receive a message through an activity, implement
MessageApi.MessageListener
and override this method:
@Override
public void onMessageReceived(MessageEvent messageEvent) {
}
From here, you can retrieve the message and/or its path to respond as you want.
If you instead want to use a background service to receive messages, you first need to make sure the manifest has the correct filters.
<service android:name=".Services.MessageListener" >
<intent-filter>
<action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
</intent-filter>
</service>
Now you are able to send and receive messages!
Data items are more like a SharedPreference for multiple devices. When you change one data item, that change is mirrored on all nodes. If another device is connected it will receive the newest synced data. This is useful for storing settings or other things that should persist across devices.
In my development, I have come up with two classes that simplify data syncing. First, using ConnectionUtils:
ConnectionUtils.sendData(GoogleApiClient, String name, String type, Object value)
This is a very rudimentary method that lets you send a data item of a specific name, a class type (boolean or int), and with a value.
You can add a listener for data events in your activity by implementing this interface:
DataApi.DataListener
and then override this method:
@Override
public void onDataChanged(DataEventBuffer dataEvents) {
}
If you instead want to use a background service, which may be a better choice for syncing user data across all devices automatically, you can just add the method `onDataChanged` to your previously created MessageListener.
With that method and service in both modules, you will be able to keep data automatically stored across all devices.
There is a second way that incorporates more sophisticated code for syncing data using SharedPreferences. The SettingsManager class for both mobile and wearables acts not only as an interface for keeping data stored locally, but also makes it easy to keep your data synced.
The class itself is not too complicated to understand. You can construct it by passing in a `Context` variable. You use methods like `setString` and `getString` to set and get SharedPreference values respectively.
To start sending synced data, you first need to connect to Google Play Services. Then, you pass that GoogleApiClient to the SettingsManager object.
sm.setSyncableSettingsManager(gapi);
Once this happens, you now have access to a new method: `.pushData()`. This will update all SharedPreference data on all other devices, making it available for syncing. To retrieve this data and make it accessible on the recipient device, another single line of code can be inserted.
@Override
public void onDataChanged(DataEventBuffer dataEvents) {
new SettingsManager(getApplicationContext()).pullData(dataEvents);
}
A new SettingsManager object is created and it takes all of the dataEvents data, assigning it to the corresponding spot in SharedPreferences again.
The Message and Data APIs are very handy tools for communicating between devices and will continue to be integral tools as both Android Wear and Google’s IoT project Brillo develop.