/**
   * Sends the message using the Sender object to the registered device.
   *
   * @param message the message to be sent in the GCM ping to the device.
   * @param sender the Sender object to be used for ping,
   * @param deviceInfo the registration id of the device.
   * @return Result the result of the ping.
   */
  private static Result doSendViaGcm(String message, Sender sender, DeviceInfo deviceInfo)
      throws IOException {
    // Trim message if needed.
    if (message.length() > 1000) {
      message = message.substring(0, 1000) + "[...]";
    }

    // This message object is a Google Cloud Messaging object, it is NOT
    // related to the MessageData class
    Message msg = new Message.Builder().addData("message", message).build();
    Result result = sender.send(msg, deviceInfo.getDeviceRegistrationID(), 5);
    if (result.getMessageId() != null) {
      String canonicalRegId = result.getCanonicalRegistrationId();
      if (canonicalRegId != null) {
        endpoint.removeDeviceInfo(deviceInfo.getDeviceRegistrationID());
        deviceInfo.setDeviceRegistrationID(canonicalRegId);
        endpoint.insertDeviceInfo(deviceInfo);
      }
    } else {
      String error = result.getErrorCodeName();
      if (error.equals(Constants.ERROR_NOT_REGISTERED)) {
        endpoint.removeDeviceInfo(deviceInfo.getDeviceRegistrationID());
      }
    }

    return result;
  }
 /**
  * Ping a message using the {@link GameMessage}.
  */
 public static boolean pingGameMessage(
     String message, GameMessage gameMessage, String gcmPayloadPingReason) throws IOException {
   boolean success = false;
   for (String toHandle : gameMessage.getTo()) {
     Message msg = new Message.Builder()
         .collapseKey(gameMessage.getFrom())
         .addData(GCM_PAYLOAD_PING_REASON, gcmPayloadPingReason)
         .addData(GCM_PAYLOAD_FROM_USER_HANDLE, gameMessage.getFrom())
         .addData(GCM_PAYLOAD_TO_USER_HANDLE, toHandle)
         .addData(GCM_PAYLOAD_GAME_ID, gameMessage.getGameId())
         .addData(GCM_PAYLOAD_MESSAGE, message)
         .build();
     if (verifyFields(msg)) {
       DeviceInfo deviceInfo = endpoint.getDeviceInfo(toHandle);
       if (deviceInfo != null) {
         LOG.info("Building game message to send to user: "******" from user: "******"The device was not found in registry for user handle " + toHandle);
       }
     } else {
       LOG.warning("Empty fields in the GCM Message. No invites sent.");
     }
   }
   return success;
 }
 /**
  * Ping the registered devices of the recipients of the notification.
  *
  * @param gameMessage contains the information needed for sending and processing the invite
  * @param gems the number of gems picked up in this game
  * @param mobsKilled the number of mobiles killed in this game
  * @param deaths the number of deaths of player in this game
  * @return {@code true} if delivery to all users were successful; {@code false} otherwise
  * @throws IOException
  */
 public static boolean pingGamePlayerSendEndScore(
     GameMessage gameMessage, long gems, long mobsKilled, long deaths) throws IOException {
   boolean success = false;
   for (String toHandle : gameMessage.getTo()) {
     Message msg = new Message.Builder()
         .collapseKey(gameMessage.getFrom())
         .addData(GCM_PAYLOAD_PING_REASON, PING_REASON_PLAYER_END_SCORE)
         .addData(GCM_PAYLOAD_FROM_USER_HANDLE, gameMessage.getFrom())
         .addData(GCM_PAYLOAD_TO_USER_HANDLE, toHandle)
         .addData(GCM_PAYLOAD_GAME_ID, gameMessage.getGameId())
         .addData(GCM_PAYLOAD_MESSAGE, "Here are the endgame scores for this player.")
         .addData("gems", Long.toString(gems))
         .addData("mobs_killed", Long.toString(mobsKilled))
         .addData("deaths", Long.toString(deaths))
         .build();
     if (verifyFields(msg)) {
       DeviceInfo deviceInfo = endpoint.getDeviceInfo(toHandle);
       if (deviceInfo != null) {
         LOG.info("Building game message to send to user: "******" from user: "******"The device was not found in registry for user handle " + toHandle);
       }
     } else {
       LOG.warning("Empty fields in the GCM Message. No invites sent.");
     }
   }
   return success;
 }
 /**
  * Ping a message using the {@link FriendMessage}.
  */
 private static boolean pingFriendMessage(
     String message, FriendMessage friendMessage, String gcmPayloadPingReason) throws IOException {
   String toHandle = friendMessage.getTo();
   Message msg = new Message.Builder()
       .collapseKey(friendMessage.getFrom())
       .addData(GCM_PAYLOAD_PING_REASON, gcmPayloadPingReason)
       .addData(GCM_PAYLOAD_FROM_USER_HANDLE, friendMessage.getFrom())
       .addData(GCM_PAYLOAD_TO_USER_HANDLE, toHandle)
       .addData(GCM_PAYLOAD_MESSAGE, message)
       .build();
   if (verifyFields(msg)) {
     DeviceInfo deviceInfo = endpoint.getDeviceInfo(toHandle);
     if (deviceInfo != null) {
       LOG.info("Building friend invite message to send to user: "******" from user: "******"The device was not found in registry for user handle " + toHandle);
     }
   } else {
     LOG.warning("Empty fields in the GCM Message. No message sent.");
   }
   return false;
 }
  /**
   * Sends the message using the Sender object to the registered device.
   *
   * @param message the message to be sent in the GCM ping to the device.
   * @param sender the Sender object to be used for ping,
   * @param deviceInfo the registration id of the device.
   * @return Result the result of the ping.
   * @throws IOException
   */
  private static Result sendViaGcm(Message msg, Sender sender, DeviceInfo deviceInfo)
      throws IOException {
    Result result = sender.send(msg, deviceInfo.getDeviceRegistrationId(), 5);
    LOG.info("Sent ping to device of user: " + deviceInfo.getUserHandle());
    if (result.getMessageId() != null) {
      String canonicalRegId = result.getCanonicalRegistrationId();
      if (canonicalRegId != null) {
        endpoint.removeDeviceInfo(deviceInfo.getDeviceRegistrationId());
        deviceInfo.setDeviceRegistrationId(canonicalRegId);
        endpoint.insertDeviceInfo(deviceInfo);
      }
    } else {
      String error = result.getErrorCodeName();
      if (error.equals(Constants.ERROR_NOT_REGISTERED)) {
        endpoint.removeDeviceInfo(deviceInfo.getDeviceRegistrationId());
      }
    }

    return result;
  }
 /**
  * This accepts a message and persists it in the AppEngine datastore, it will also broadcast the
  * message to upto 10 registered android devices via Google Cloud Messaging
  *
  * @param message the entity to be inserted.
  * @return
  * @throws IOException
  */
 @ApiMethod(name = "sendMessage")
 public void sendMessage(@Named("message") String message) throws IOException {
   Sender sender = new Sender(API_KEY);
   // create a MessageData entity with a timestamp of when it was
   // received, and persist it
   MessageData messageObj = new MessageData();
   messageObj.setMessage(message);
   messageObj.setTimestamp(System.currentTimeMillis());
   EntityManager mgr = getEntityManager();
   try {
     mgr.persist(messageObj);
   } finally {
     mgr.close();
   }
   // ping a max of 10 registered devices
   CollectionResponse<DeviceInfo> response = endpoint.listDeviceInfo(null, 10);
   for (DeviceInfo deviceInfo : response.getItems()) {
     doSendViaGcm(message, sender, deviceInfo);
   }
 }