The Android Im project is currently working on the need to periodically publish user locations to its XMPP member friends using aSmack and XEP-0080.
This turned out to be more complicated than I would like, so I registered my decision here: http://www.dbotha.com/2014/11/02/xep-0080-user-location-on-android-using-pep-with -smack /
For completeness, I will talk about the important parts here. In the interest of brevity, the only XML child elements in the XEP-0080 specification that span Ill are latitude and longitude.
PEPItem to hold the user's location and convert it to the corresponding 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; }
Basically template PEPEvent for storing PEPItemLocation UserLocation:
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()); } }
Custom PacketExtensionProvider to parse the UserLocationEvent user from incoming packages.
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 LocationEvent's inbox:
PEPListener PEP_LISTENER = new PEPListener() { @Override public void eventReceived(String from, PEPEvent event) { if (event instanceof UserLocationEvent) {