/**
   * Sends byte[] data to the specified node.
   *
   * @param messagePath The path of the message
   * @param message The message to send
   * @param callback The callback to receive the response
   * @param isAsynchronous send data asynchronously
   */
  public void sendMessage(
      final String messagePath,
      final String message,
      final ResultCallback<MessageApi.SendMessageResult> callback,
      final boolean isAsynchronous) {
    if (!isConnected()) {
      if (mConnectionCallBacks != null) {
        mConnectionCallBacks.onConnectionFailed(WearConnectionCallBacks.NETWORK_ERROR);
      }
      return;
    } else if (messagePath == null) {
      if (mConnectionCallBacks != null) {
        mConnectionCallBacks.onConnectionFailed(WearConnectionCallBacks.PATH_NULL_ERROR);
      }
      return;
    }

    if (isAsynchronous) {
      Wearable.NodeApi.getConnectedNodes(mGoogleApiClient)
          .setResultCallback(
              new ResultCallback<NodeApi.GetConnectedNodesResult>() {
                @Override
                public void onResult(NodeApi.GetConnectedNodesResult connectedNodesResult) {
                  List<Node> connectedNodes = connectedNodesResult.getNodes();

                  for (Node node : connectedNodes) {

                    String nodeId = node.getId();
                    PendingResult<MessageApi.SendMessageResult> messageResult =
                        Wearable.MessageApi.sendMessage(
                            mGoogleApiClient, nodeId, messagePath, message.getBytes());
                    if (callback != null) {
                      messageResult.setResultCallback(callback);
                    }
                  }
                }
              });
    } else {
      if (isRunningOnMainThread()) {
        if (mConnectionCallBacks != null) {
          mConnectionCallBacks.onConnectionFailed(
              WearConnectionCallBacks.METHOD_CALLED_FROM_UI_THREAD);
        }
        return;
      }
      NodeApi.GetConnectedNodesResult connectedNodesResult =
          Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).await();
      List<Node> connectedNodes = connectedNodesResult.getNodes();

      for (Node node : connectedNodes) {

        String nodeId = node.getId();
        Wearable.MessageApi.sendMessage(mGoogleApiClient, nodeId, messagePath, message.getBytes())
            .await();
      }
    }
  }
  /**
   * Adds DataItem to the Android Wear network. The updated item is synchronized across all devices.
   *
   * @param dataPath The path to the data
   * @param data The data to send
   * @param callBack The callback to receive the response
   * @param isAsynchronous send data asynchronously
   * @param sendImmediately source :
   *     http://android-developers.blogspot.in/2015/11/whats-new-in-google-play-services-83.html
   *     With Google Play services 8.3, we’ve updated the DataApi to allow for urgency in how data
   *     items are synced. Now, a priority can be added to the data item to determine when it should
   *     be synced. For example, if you are building an app that requires immediate syncing, such as
   *     a remote control app, it can still be done immediately by calling setUrgent(), but for
   *     something such as updating your contacts, you could tolerate some delay. Non-urgent
   *     DataItems may be delayed for up to 30 minutes, but you can expect that in most cases they
   *     will be delivered within a few minutes. Low priority is now the default, so setUrgent() is
   *     needed to obtain the previous timing.
   */
  public void sendData(
      String dataPath,
      DataMap data,
      ResultCallback<DataApi.DataItemResult> callBack,
      boolean isAsynchronous,
      boolean sendImmediately) {
    if (!isConnected()) {
      if (mConnectionCallBacks != null) {
        mConnectionCallBacks.onConnectionFailed(WearConnectionCallBacks.NETWORK_ERROR);
      }
      return;
    } else if (dataPath == null) {
      if (mConnectionCallBacks != null) {
        mConnectionCallBacks.onConnectionFailed(WearConnectionCallBacks.PATH_NULL_ERROR);
      }
      return;
    } else if (data == null) {
      Log.d("Send DataMap", "Data cannot be null");
      return;
    }

    PutDataMapRequest putDataMapRequest = PutDataMapRequest.create(dataPath);
    putDataMapRequest.getDataMap().putAll(data);

    /** Current time is also sent with data, just to make it a new data */
    putDataMapRequest
        .getDataMap()
        .putString(
            WearConnectionConstants.KEY.CURRENT_TIME, String.valueOf(System.currentTimeMillis()));

    PutDataRequest request = putDataMapRequest.asPutDataRequest();

    // update from google play service 8.3. refer comments above
    if (sendImmediately) {
      request.setUrgent();
    }

    if (isAsynchronous) {
      /** You will get callback after data is sent use the below code */
      PendingResult<DataApi.DataItemResult> dataResult =
          Wearable.DataApi.putDataItem(mGoogleApiClient, request);
      if (callBack != null) {
        dataResult.setResultCallback(callBack);
      }
    } else {
      if (isRunningOnMainThread()) {
        if (mConnectionCallBacks != null) {
          mConnectionCallBacks.onConnectionFailed(
              WearConnectionCallBacks.METHOD_CALLED_FROM_UI_THREAD);
        }
        return;
      }
      Wearable.DataApi.putDataItem(mGoogleApiClient, request).await();
    }
  }
  /**
   * Returns the message received from the callback
   *
   * @param messageEvent The message event that contains the message
   * @param messagePath The path of the message intended to
   * @return The string message or null
   */
  public String getMessage(MessageEvent messageEvent, String messagePath) {
    if (messagePath == null) {
      if (mConnectionCallBacks != null) {
        mConnectionCallBacks.onConnectionFailed(WearConnectionCallBacks.PATH_NULL_ERROR);
      }
      return null;
    }
    String path = messageEvent.getPath();

    if (messagePath.equalsIgnoreCase(path)) {
      return new String(messageEvent.getData());
    } else {
      return null;
    }
  }
  @Override
  public void onConnected(Bundle connectionHint) {
    if (mDataListener != null) {
      Wearable.DataApi.addListener(mGoogleApiClient, mDataListener);
    }
    if (mMessageListener != null) {
      Wearable.MessageApi.addListener(mGoogleApiClient, mMessageListener);
    }
    if (mNodeListener != null) {
      Wearable.NodeApi.addListener(mGoogleApiClient, mNodeListener);
    }

    // TODO  Vishnu : check this callback is needed
    if (mConnectionCallBacks != null) {
      mConnectionCallBacks.onConnectionSuccess();
    }
  }
  /**
   * Returns the data item modified in this event. An event of TYPE_DELETED will only have its
   * {DataItem#getUri} populated.
   *
   * @param dataEvents The data buffer
   * @param dataPath The data path
   * @return The data item corresponding to the data path or null.
   */
  public DataItem getData(DataEventBuffer dataEvents, String dataPath) {
    if (dataPath == null) {
      if (mConnectionCallBacks != null) {
        mConnectionCallBacks.onConnectionFailed(WearConnectionCallBacks.PATH_NULL_ERROR);
      }
      return null;
    }

    List<DataEvent> events = FreezableUtils.freezeIterable(dataEvents);
    dataEvents.release();
    for (DataEvent event : events) {
      String path = event.getDataItem().getUri().getPath();
      if (dataPath.equalsIgnoreCase(path)) {
        return event.getDataItem();
      }
    }
    return null;
  }
 @Override
 public void onConnectionFailed(ConnectionResult connectionResult) {
   if (mConnectionCallBacks != null) {
     mConnectionCallBacks.onGooglePlayServiceError(connectionResult);
   }
 }
 @Override
 public void onConnectionSuspended(int cause) {
   if (mConnectionCallBacks != null) {
     mConnectionCallBacks.onConnectionFailed(WearConnectionCallBacks.CONNECTION_SUSPENDED);
   }
 }