4

I want to create an application for Android that enables me to get the geolocation of a user. This has to be made as a client-server app and for the server side I'm using OpenFire. For getting the user's location I would have to use XEP-0080, right? And SmackAPI also? I'm completely new to XMPP and Smack, so if anyone could get me a few pointers or maybe examples or any kind of documentation about this I'd be very grateful.

Thanks in advance for any help.

Robert
  • 41
  • 1
  • 2

2 Answers2

3

An Android project I’m currently working on required periodically publishing a user’s location to their XMPP roster friends using aSmack & XEP-0080.

It turned out trickier than I would have liked so I documented my solution here: http://www.dbotha.com/2014/11/02/xep-0080-user-location-on-android-using-pep-with-smack/

For completeness I'll cover the important parts here. In the interest of brevity the only XML child elements from the XEP-0080 specification that I’ll cover are those relating to latitude and longitude.

A PEPItem to hold the user location and transform it into the appropriate XML:

public class UserLocation extends PEPItem {

    public static final String NODE =
        "http://jabber.org/protocol/geoloc";

    public final double latitude, longitude;

    public UserLocation(double latitude, double longitude) {
        this(StringUtils.randomString(16), latitude, longitude);
    }

    public UserLocation(double latitude, double longitude,
            String id) {
        super(id);
        this.latitude = latitude;
        this.longitude = longitude;
    }

    @Override
    java.lang.String getNode() {
        return NODE;
    }

    // return an XML element approximately inline
    // with the XEP-0080 spec
    @Override
    java.lang.String getItemDetailsXML() {
        return String.format(
            "<geoloc xmlns='%s'><lat>%f</lat>" +
            "<lon>%f</lon></geoloc>",
            NODE, latitude, longitude);
    }
}

A mostly boilerplate PEPEvent to hold the UserLocation PEPItem:

public class UserLocationEvent extends PEPEvent {

    private final UserLocation location;

    public UserLocationEvent(UserLocation location) {
        this.location = location;
    }

    public UserLocation getLocation() {
        return location;
    }

    @Override
    public String getNamespace() {
        return "http://jabber.org/protocol/pubsub#event";
    }

    @Override
    public String toXML() {
        return String.format("<event xmlns=" +
            "'http://jabber.org/protocol/pubsub#event' >" +
            "<items node='%s' >%s</items></event>",
            UserLocation.NODE, location.toXML());
    }
}

A custom PacketExtensionProvider to parse out the UserLocationEvent's from incoming packets where present.

public class UserLocationProvider
        implements PacketExtensionProvider {

    // This method will get called whenever aSmack discovers a
    // packet extension containing a publish element with the
    // attribute node='http://jabber.org/protocol/geoloc'
    @Override
    public PacketExtension parseExtension(XmlPullParser parser)
            throws Exception {

        boolean stop = false;
        String id = null;
        double latitude = 0;
        double longitude = 0;
        String openTag = null;

        while (!stop) {
            int eventType = parser.next();

            switch (eventType) {
                case XmlPullParser.START_TAG:
                    openTag = parser.getName();
                    if ("item".equals(openTag)) {
                        id = parser.getAttributeValue("", "id");
                    }

                    break;

                case XmlPullParser.TEXT:
                    if ("lat".equals(openTag)) {
                        try {
                            latitude = Double.parseDouble(
                                parser.getText());
                        } catch (NumberFormatException ex) {
                            /* ignore */
                        }
                    } else if ("lon".equals(openTag)) {
                        try {
                            longitude = Double.parseDouble(
                                parser.getText());
                        } catch (NumberFormatException ex) {
                            /* ignore */
                        }
                    }

                    break;

                case XmlPullParser.END_TAG:
                    // Stop parsing when we hit </item>
                    stop = "item".equals(parser.getName());
                    openTag = null;
                    break;
            }
        }

        return new UserLocationEvent(
            new UserLocation(id, latitude, longitude));
    }
}

Now tying it all together:

XMPPTCPConnection connection = new XMPPTCPConnection();

ServiceDiscoveryManager sdm = ServiceDiscoveryManager
    .getInstanceFor(connection);
sdm.addFeature("http://jabber.org/protocol/geoloc");
sdm.addFeature("http://jabber.org/protocol/geoloc+notify");

EntityCapsManager capsManager = EntityCapsManager
    .getInstanceFor(connection);
capsManager.enableEntityCaps();

PEPProvider pepProvider = new PEPProvider();
pepProvider.registerPEPParserExtension(
    "http://jabber.org/protocol/geoloc",
    new UserLocationProvider());
ProviderManager.addExtensionProvider("event",
    "http://jabber.org/protocol/pubsub#event", pepProvider);
PEPManager pepManager = new PEPManager(connection);
pepManager.addPEPListener(PEP_LISTENER);

connection.connect();
connection.login(username, password);

And finally a listener for incoming LocationEvent's:

PEPListener PEP_LISTENER = new PEPListener() {
    @Override
    public void eventReceived(String from, PEPEvent event) {
        if (event instanceof UserLocationEvent) {
            // do something interesting
        }
    }
};
dbotha
  • 1,501
  • 4
  • 20
  • 38
  • Would be great if you would be willing to send the code upstream: https://github.com/igniterealtime/Smack/wiki/Smack-Jobs#implement-xep-0080-user-location and https://igniterealtime.org/issues/browse/SMACK-610 – Flow Nov 03 '14 at 17:44
  • There is a little problem with extending PEPItem. Its abstract methods are without access modifier, but you need public modifier to override them (because without access modifier they are visible only for its package - imported library JAR, and when you use your own package - extend PEPItem in your own package, they will not be accessible). I solved it with rebuilding aSmack library jar with added public modifiers in PEPItem class. – tomm Mar 16 '15 at 14:21
  • I found next problem while parsing latitude and longitude from xml. I get string containing double, but not with dot but with comma (my phone is localized for slovak language not english). So exceptions occured while parsing in UserLocationProvider. Solution could be either replacing all commas with dots in string before parsing in `UserLocationProvider` or setting locale (e.g. Locale.ENGLISH) in `UserLocation.getItemDetailsXML()` for `format()` method (so not using this method without locale parameter, which results in using device predefined locale). – tomm May 10 '15 at 21:10
2

I believe this is close to what you are trying to accomplish.

XEP-0080 User Location in Smack Library

Community
  • 1
  • 1
Rob Goodwin
  • 2,676
  • 2
  • 27
  • 47
  • thnx for this link, I've seen it and am planning to use it, but do you have any complete example of using XMPP extensions in Java programs, because like I said, I'm completely new to this area. thnx again for help – Robert Apr 27 '10 at 22:37
  • I just need a few clues on what to do first. Like I said, I'm a complete noob in this area. Do I have to configure my Openfire server first (create new plug-in) for this kind of stuff or does it support it by default or do I simply have to develop Android app that does what it does. This is for my thesis, so it's kindda important. Thanks in advance – Robert Apr 27 '10 at 23:53
  • Does Openfire support XEP-0800 – Rob Goodwin Apr 28 '10 at 13:58
  • As much as I know I does not, but I think it is possible to create a plug-in for it, right? – Robert Apr 29 '10 at 12:57
  • 1
    I don't think that an XMPP server needs support for XEP-0800, since it just of a definition of a user location format. I can be transported via Publish-Subscribe (XEP-0060) or Personal Eventing (XEP-0163). Openfire does support XEP-0060. – Flow Feb 10 '12 at 16:33