private Bundle formatDistance(DataReadResult dataReadResult) {
    Bundle bundle = new Bundle();

    // Distance
    List<DataPoint> dataPointList =
        dataReadResult.getDataSet(DataType.TYPE_DISTANCE_DELTA).getDataPoints();
    float[] distance = new float[dataPointList.size()];
    long[] startTime = new long[dataPointList.size()];
    long[] endTime = new long[dataPointList.size()];
    int idx = 0;
    for (DataPoint dp : dataPointList) {
      distance[idx] = dp.getValue(Field.FIELD_DISTANCE).asFloat();
      startTime[idx] = dp.getStartTime(TimeUnit.MILLISECONDS);
      endTime[idx] = dp.getEndTime(TimeUnit.MILLISECONDS);
      ++idx;
    }
    bundle.putFloatArray("DISTANCE", distance);
    bundle.putLongArray("START_TIME", startTime);
    bundle.putLongArray("END_TIME", endTime);
    Log.d(TAG, String.format("Distance data points: %d", idx));

    return bundle;
  }
  private Bundle formatActivity(DataReadResult dataReadResult) {
    Bundle bundle = new Bundle();

    // Activity Recognition
    List<DataPoint> dataPointList =
        dataReadResult.getDataSet(DataType.TYPE_ACTIVITY_SAMPLE).getDataPoints();
    int[] activity = new int[dataPointList.size()];
    long[] endTime = new long[dataPointList.size()];
    float[] confidence = new float[dataPointList.size()];
    int idx = 0;
    for (DataPoint dp : dataPointList) {
      activity[idx] = dp.getValue(Field.FIELD_ACTIVITY).asInt();
      confidence[idx] = dp.getValue(Field.FIELD_CONFIDENCE).asFloat();
      endTime[idx] = dp.getEndTime(TimeUnit.MILLISECONDS);
      ++idx;
    }
    bundle.putIntArray("ACTIVITY", activity);
    bundle.putFloatArray("CONFIDENCE", confidence);
    bundle.putLongArray("TIMESTAMP", endTime);
    Log.d(TAG, String.format("Activity data points: %d", idx));

    return bundle;
  }
  private Bundle formatStepCount(DataReadResult dataReadResult) {
    Bundle bundle = new Bundle();

    // Step Count
    List<DataPoint> dataPointList =
        dataReadResult.getDataSet(DataType.TYPE_STEP_COUNT_DELTA).getDataPoints();
    int[] steps = new int[dataPointList.size()];
    long[] startTime = new long[dataPointList.size()];
    long[] endTime = new long[dataPointList.size()];
    int idx = 0;
    for (DataPoint dp : dataPointList) {
      steps[idx] = dp.getValue(Field.FIELD_STEPS).asInt();
      startTime[idx] = dp.getStartTime(TimeUnit.MILLISECONDS);
      endTime[idx] = dp.getEndTime(TimeUnit.MILLISECONDS);
      ++idx;
    }
    bundle.putIntArray("STEPS", steps);
    bundle.putLongArray("START_TIME", startTime);
    bundle.putLongArray("END_TIME", endTime);
    Log.d(TAG, String.format("Step data points: %d", idx));

    return bundle;
  }
  private Bundle formatCalories(DataReadResult dataReadResult) {
    Bundle bundle = new Bundle();

    // Calorie expenditure
    List<DataPoint> dataPointList =
        dataReadResult.getDataSet(DataType.TYPE_CALORIES_EXPENDED).getDataPoints();
    float[] calories = new float[dataPointList.size()];
    long[] startTime = new long[dataPointList.size()];
    long[] endTime = new long[dataPointList.size()];
    int idx = 0;
    for (DataPoint dp : dataPointList) {
      calories[idx] = dp.getValue(Field.FIELD_CALORIES).asFloat();
      startTime[idx] = dp.getStartTime(TimeUnit.MILLISECONDS);
      endTime[idx] = dp.getEndTime(TimeUnit.MILLISECONDS);
      ++idx;
    }
    bundle.putFloatArray("CALORIES", calories);
    bundle.putLongArray("START_TIME", startTime);
    bundle.putLongArray("END_TIME", endTime);
    Log.d(TAG, String.format("Calorie data points: %d", idx));

    return bundle;
  }
  @Override
  public void execute(Context ctx, Map<String, Value> context)
      throws UnknownObjectException, RuleExecutionException {
    GoogleApiClient client = ((GoogleFitChannel) channel).acquireClient(ctx);

    try {
      long now = System.currentTimeMillis();
      DataReadRequest.Builder builder =
          new DataReadRequest.Builder()
              .read(dataType.getType())
              .setTimeRange(now - 24 * 3600 * 1000, now, TimeUnit.MILLISECONDS);

      DataReadResult result = Fitness.HistoryApi.readData(client, builder.build()).await();

      DataSet dataSet = result.getDataSet(dataType.getType());
      List<DataPoint> dataPoints = dataSet.getDataPoints();

      DataPoint latest = null;
      for (DataPoint point : dataPoints) {
        if (latest == null || point.getTimestampNanos() >= latest.getTimestampNanos())
          latest = point;
      }
      if (latest == null) throw new RuleExecutionException("Google API did not return any data");

      com.google.android.gms.fitness.data.Value value = latest.getValue(dataType.getField());

      String dataTypeId = dataType.toString();

      if (value.getFormat() == Field.FORMAT_INT32)
        context.put(FITNESS_CURRENT_VALUE_PREFIX + dataTypeId, new Value.Number(value.asInt()));
      else if (value.getFormat() == Field.FORMAT_FLOAT)
        context.put(FITNESS_CURRENT_VALUE_PREFIX + dataTypeId, new Value.Number(value.asFloat()));
      else throw new RuleExecutionException("Google Fit data point has invalid type");
    } finally {
      ((GoogleFitChannel) channel).releaseClient();
    }
  }