package net.trentgardner.cordova.androidwear;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.wearable.MessageApi;
import com.google.android.gms.wearable.MessageEvent;
import com.google.android.gms.wearable.Node;
import com.google.android.gms.wearable.NodeApi;
import com.google.android.gms.wearable.Wearable;
import java.util.ArrayList;
import java.util.List;
public class WearProviderService extends Service implements
MessageApi.MessageListener,
NodeApi.NodeListener,
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
public static final String MESSAGE_RECEIVED_PATH = "net.trentgardner.cordova.androidwear.NewMessage";
private final List<WearMessageListener> listeners = new ArrayList<WearMessageListener>();
private final List<String> nodes = new ArrayList<String>();
private static final String TAG = WearProviderService.class.getSimpleName();
private GoogleApiClient mGoogleApiClient;
private Thread mBackgroundThread;
private Handler mBackgroundHandler;
private final WearMessageApi.Stub apiEndpoint = new WearMessageApi.Stub() {
@Override
public void sendData(String nodeId, String data) throws RemoteException {
LOGD(TAG, "WearMessageApi.sendData");
WearProviderService.this.sendData(nodeId, data);
}
@Override
public void addListener(WearMessageListener listener) throws RemoteException {
LOGD(TAG, "WearMessageApi.addListener");
synchronized (listeners) {
listeners.add(listener);
}
LOGD(TAG, String.format("Notifying listener of %d connected nodes", nodes.size()));
for (String node : nodes) {
listener.onConnect(node);
}
}
@Override
public void removeListener(WearMessageListener listener) throws RemoteException {
LOGD(TAG, "WearMessageApi.removeListener");
synchronized (listeners) {
listeners.remove(listener);
}
}
};
private final class BackgroundThread extends Thread {
@Override
public void run() {
Looper.prepare();
mBackgroundHandler = new Handler();
Looper.loop();
}
}
@Override
public void onCreate() {
LOGD(TAG, "onCreate");
super.onCreate();
mBackgroundThread = new BackgroundThread();
mBackgroundThread.start();
// Build a new GoogleApiClient
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Wearable.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
mGoogleApiClient.connect();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
mGoogleApiClient.connect();
return START_NOT_STICKY;
}
@Override
public void onDestroy() {
LOGD(TAG, "onDestroy");
if (null != mGoogleApiClient && mGoogleApiClient.isConnected()) {
Wearable.MessageApi.removeListener(mGoogleApiClient, this);
Wearable.NodeApi.removeListener(mGoogleApiClient, this);
mGoogleApiClient.disconnect();
}
super.onDestroy();
}
@Override
public IBinder onBind(Intent intent) {
LOGD(TAG, "onBind");
return apiEndpoint;
}
private void addNode(final String nodeId) {
LOGD(TAG, "addNode");
if (nodes.contains(nodeId)) return;
nodes.add(nodeId);
mBackgroundHandler.post(new Runnable() {
@Override
public void run() {
synchronized (listeners) {
for(int i = 0; i < listeners.size(); ++i) {
try {
listeners.get(i).onConnect(nodeId);
} catch (RemoteException e) {
Log.w(TAG, "Failed to notify listener ", e);
listeners.remove(i--);
}
}
}
}
});
}
private void removeNode(final String nodeId) {
LOGD(TAG, "removeNode");
if (!nodes.contains(nodeId)) return;
nodes.remove(nodes.indexOf(nodeId));
mBackgroundHandler.post(new Runnable() {
@Override
public void run() {
synchronized (listeners) {
for(int i = 0; i < listeners.size(); ++i) {
try {
listeners.get(i).onError(nodeId, "Connection dead");
} catch (RemoteException e) {
Log.w(TAG, "Failed to notify listener ", e);
listeners.remove(i--);
}
}
}
}
});
}
private void dataReceived(final String nodeId, final String message) {
LOGD(TAG, String.format("dataReceived - nodeId: %s", nodeId));
mBackgroundHandler.post(new Runnable() {
@Override
public void run() {
synchronized (listeners) {
LOGD(TAG, String.format("Notifying %d listeners", listeners.size()));
for(int i = 0; i < listeners.size(); ++i) {
try {
listeners.get(i).onDataReceived(nodeId, message);
} catch (RemoteException e) {
Log.w(TAG, "Failed to notify listener ", e);
listeners.remove(i--);
}
}
}
}
});
}
private void sendData(final String nodeId, final String data) {
LOGD(TAG, "sendMessage");
final byte[] message = data.getBytes();
mBackgroundHandler.post(new Runnable() {
@Override
public void run() {
MessageApi.SendMessageResult result =
Wearable.MessageApi.sendMessage(mGoogleApiClient, nodeId,
MESSAGE_RECEIVED_PATH, message).await();
if (result.getStatus().isSuccess()) {
Log.v(TAG, "Message sent to : " + nodeId);
} else {
// Log an error
Log.v(TAG, "MESSAGE ERROR: failed to send Message");
}
}
});
}
@Override
public void onConnected(Bundle bundle) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
LOGD(TAG, "Connected to Google Api Service");
}
Wearable.MessageApi.addListener(mGoogleApiClient, this);
Wearable.NodeApi.addListener(mGoogleApiClient, this);
mBackgroundHandler.post(new Runnable() {
@Override
public void run() {
NodeApi.GetConnectedNodesResult nodes =
Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).await();
for (Node node : nodes.getNodes()) {
addNode(node.getId());
}
}
});
}
@Override
public void onConnectionSuspended(int i) {
LOGD(TAG, "Connection to Google API client was suspended");
}
@Override
public void onMessageReceived(MessageEvent messageEvent) {
if(messageEvent.getPath().equals(MESSAGE_RECEIVED_PATH)) {
final String message = new String(messageEvent.getData());
dataReceived(messageEvent.getSourceNodeId(), message);
}
}
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
LOGD(TAG, "Connection to Google API client was suspended");
Wearable.MessageApi.removeListener(mGoogleApiClient, this);
}
@Override
public void onPeerConnected(final Node node) {
LOGD(TAG, "onPeerConnected");
addNode(node.getId());
}
@Override
public void onPeerDisconnected(final Node node) {
LOGD(TAG, "onPeerDisconnected");
removeNode(node.getId());
}
/**
* As simple wrapper around Log.d
*/
private static void LOGD(final String tag, String message) {
//if (Log.isLoggable(tag, Log.DEBUG)) {
Log.d(tag, message);
//}
}
}