I'm building a watch face for Android wear. The watch face will occasionally ask the handheld for information, which will be used to update the watch face. Unfortunately, onDataChanged and onMessageReceived only occur the first time the watch face loads, and never again (I can switch to another watch face and back, and again it'll work once)
Most of the boilerplate is taken from examples I think. So in the watch face I have
@Override
public void onCreate() {
Log.d(TAG, "onCreate - Creating service...");
super.onCreate();
initGoogleApiClient();
}
private void initGoogleApiClient() {
mApiClient = new GoogleApiClient.Builder(mContext)
.addApi(Wearable.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
Log.d(TAG, "Calling connect on mApiClient...");
mApiClient.connect();
}
@Override
public void onMessageReceived(MessageEvent messageEvent) {
Log.d(TAG, "onMessageReceived: " + messageEvent.getPath() + " " + messageEvent.getData());
switch (messageEvent.getPath()) {
case "case 1":
Log.d(TAG,"some data was sent to us...");
if (!mApiClient.isConnected())
mApiClient.connect();
break;
}
}
@Override
public void onConnected(@Nullable Bundle bundle) {
Log.d(TAG,"onConnected - adding Listeners");
Wearable.DataApi.addListener(mApiClient, this);
Wearable.MessageApi.addListener(mApiClient, this);
}
@Override
public void onConnectionSuspended(int i) {
Log.d(TAG,"onConnection suspended");
}
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
Log.e(TAG,"onConnection failed");
}
@Override
public void onDataChanged(DataEventBuffer dataEventBuffer) {
Log.d(TAG, "onDataChanged");
for (DataEvent event : dataEventBuffer) {
if (event.getType() == DataEvent.TYPE_CHANGED && event.getDataItem().getUri().getPath().equals("/image")) {
Log.d(TAG, "Matched TYPE_CHANGED and /image");
DataMapItem dataMapItem = DataMapItem.fromDataItem(event.getDataItem());
Asset photoAsset = dataMapItem.getDataMap().getAsset("photo");
loadBitmapFromAsset(photoAsset);
}
}
}
On the handheld, logging shows that the data is being sent to the wearable the same way on both the first instance after the watch face being loaded (where it works) and on subsequent requests. The difference being on subsequent requests, none of the logging in the above code occurs, showing nothing is getting triggered.
Some handheld code for how the data is sent:
private void SendBitmap(final Bitmap bitmap) {
//snip - stuff to resize the bitmap
Log.d(TAG, "Resized image to " + cropped.getWidth() + "x" + cropped.getHeight());
final Bitmap croppedBitmap = cropped;
new Thread(new Runnable() {
@Override
public void run() {
if (!nodeConnected || !mGoogleApiClient.isConnected())
{
Log.d(TAG,"No node connected. Attempting connection for 5 seconds...");
mGoogleApiClient.blockingConnect(5, TimeUnit.SECONDS);
}
if (!nodeConnected)
{
Log.e(TAG, "Failed to connect to mGoogleApiClient within 5 seconds");
return;
}
if (mGoogleApiClient.isConnected()) {
Log.d(TAG, "Client is connected...");
Asset asset = createAssetFromBitmap(croppedBitmap);
PutDataMapRequest putDataMapRequest = PutDataMapRequest.create("/image").setUrgent();
putDataMapRequest.getDataMap().clear(); //clear down current stuff
putDataMapRequest.getDataMap().putAsset("photo", asset);
putDataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis());
PutDataRequest request = putDataMapRequest.asPutDataRequest().setUrgent();
PendingResult<DataApi.DataItemResult> pendingResult =
Wearable.DataApi.putDataItem(mGoogleApiClient, request);
Log.d(TAG,"DataItem is put");
pendingResult.setResultCallback(new ResultCallback<DataApi.DataItemResult>() {
@Override
public void onResult(DataApi.DataItemResult dataItemResult) {
Log.d(TAG, "Sending task data: " + dataItemResult.getStatus().isSuccess());
}
});
}
else {
Log.w(TAG,"Client is NOT connected...");
}
}
}).start();
//send a message to trigger connection?
SendToDataLayerThread photoTrigger = new SendToDataLayerThread("case 1", "Photo sent...".getBytes());
photoTrigger.run();
Log.d(TAG, "Photo sent to the Google API");
}
private static Asset createAssetFromBitmap(Bitmap bitmap) {
final ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteStream);
return Asset.createFromBytes(byteStream.toByteArray());
}
public class SendToDataLayerThread extends Thread {
String path;
byte[] message;
// Constructor to send a message to the data layer
SendToDataLayerThread(String p, byte[] msg) {
path = p;
message = msg;
}
public void run() {
Wearable.NodeApi.getConnectedNodes(mGoogleApiClient)
.setResultCallback(new ResultCallback<NodeApi.GetConnectedNodesResult>() {
@Override
public void onResult(NodeApi.GetConnectedNodesResult nodes) {
for (final Node node : nodes.getNodes()) {
Wearable.MessageApi.sendMessage(mGoogleApiClient, node.getId(), path, message).setResultCallback(new ResultCallback<MessageApi.SendMessageResult>() {
@Override
public void onResult(MessageApi.SendMessageResult sendMessageResult) {
if (sendMessageResult.getStatus().isSuccess()) {
Log.d(TAG, "Message: {" + message + "} sent to: " + node.getDisplayName());
} else {
// Log an error
Log.d(TAG, "ERROR: failed to send Message");
}
}
});
}
}
});
}
}
Things I've tried, based on other posts:
- using setUrgent()
- in addition to sending my image, sending a timestamp to make the data "unique"
- sending a message to "wake up" the connection on the wearable
I'm pretty stumped at this point. I'm sure I'm missing something obvious but I'm not sure what to try next...