/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Copyright @ 2015 Atlassian Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.java.sip.communicator.impl.protocol.jabber; import java.util.*; import net.java.sip.communicator.impl.protocol.jabber.extensions.geolocation.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.event.*; import net.java.sip.communicator.util.*; import org.jivesoftware.smack.*; import org.jivesoftware.smack.filter.*; import org.jivesoftware.smack.packet.*; import org.jivesoftware.smack.provider.*; import org.jivesoftware.smack.util.StringUtils; /** * The Jabber implementation of an OperationSetGeolocation done with the * XEP-0080: User Geolocation. This class broadcast our own geolocation and * manage the geolocation status of our buddies. * * Currently, we send geolocation message in presence. We passively listen * to buddies geolocation when their presence are updated. * * @author Guillaume Schreiner */ public class OperationSetGeolocationJabberImpl implements OperationSetGeolocation { /** * Our logger. */ private static final Logger logger = Logger.getLogger(OperationSetGeolocationJabberImpl.class); /** * The list of Geolocation status listeners interested in receiving presence * notifications of changes in geolocation of contacts in our contact list. */ private final List<GeolocationListener> geolocationContactsListeners = new Vector<GeolocationListener>(); /** * A callback to the provider */ private final ProtocolProviderServiceJabberImpl jabberProvider; /** * A callback to the persistent presence operation set. */ private final OperationSetPersistentPresence opsetprez; /** * Constuctor * * @param provider <tt>ProtocolProviderServiceJabberImpl</tt> */ public OperationSetGeolocationJabberImpl( ProtocolProviderServiceJabberImpl provider) { this.jabberProvider = provider; this.opsetprez = provider .getOperationSet(OperationSetPersistentPresence.class); this.jabberProvider.addRegistrationStateChangeListener( new RegistrationStateListener()); // Add the custom GeolocationExtension to the Smack library ProviderManager pManager = ProviderManager.getInstance(); pManager.addExtensionProvider( GeolocationPacketExtensionProvider.ELEMENT_NAME , GeolocationPacketExtensionProvider.NAMESPACE , new GeolocationPacketExtensionProvider()); } /** * Broadcast our current Geolocation trough this provider using a Jabber * presence message. * * @param geolocation our current Geolocation ready to be sent */ public void publishGeolocation(Map<String, String> geolocation) { GeolocationPresence myGeolocPrez = new GeolocationPresence(opsetprez); GeolocationPacketExtension geolocExt = GeolocationJabberUtils .convertMapToExtension(geolocation); myGeolocPrez.setGeolocationExtention(geolocExt); this.jabberProvider.getConnection() .sendPacket(myGeolocPrez.getGeolocPresence()); } /** * Retrieve the geolocation of the given contact. * <p> * Note: Currently not implemented because we can not actively poll the * server for the presence of a given contact ? * <p> * @param contactIdentifier the <tt>Contact</tt> we want to retrieve its * geolocation by its identifier. * @return the <tt>Geolocation</tt> of the contact. */ public Map<String, String> queryContactGeolocation(String contactIdentifier) { /** @todo implement queryContactGeolocation() */ return null; } /** * Registers a listener that would get notifications any time a contact * refreshed its geolocation via Presence. * * @param listener the <tt>ContactGeolocationPresenceListener</tt> to * register */ public void addGeolocationListener(GeolocationListener listener) { synchronized (geolocationContactsListeners) { geolocationContactsListeners.add(listener); } } /** * Remove a listener that would get notifications any time a contact * refreshed its geolocation via Presence. * * @param listener the <tt>ContactGeolocationPresenceListener</tt> to * register */ public void removeGeolocationListener(GeolocationListener listener) { synchronized (geolocationContactsListeners) { geolocationContactsListeners.remove(listener); } } /** * Our listener that will tell us when we're registered to server * and we are ready to launch the listener for GeolocationPacketExtension * packets */ private class RegistrationStateListener implements RegistrationStateChangeListener { /** * The method is called by a ProtocolProvider implementation whenever * a change in the registration state of the corresponding provider had * occurred. * @param evt ProviderStatusChangeEvent the event describing the status * change. */ public void registrationStateChanged(RegistrationStateChangeEvent evt) { if (logger.isDebugEnabled()) logger.debug("The Jabber provider changed state from: " + evt.getOldState() + " to: " + evt.getNewState()); if (evt.getNewState() == RegistrationState.REGISTERED) { PacketExtensionFilter filterGeoloc = new PacketExtensionFilter( GeolocationPacketExtensionProvider.ELEMENT_NAME, GeolocationPacketExtensionProvider.NAMESPACE ); // launch the listener try { jabberProvider.getConnection().addPacketListener( new GeolocationPresencePacketListener() , filterGeoloc ); } catch (Exception e) { logger.error(e); } } else if (evt.getNewState() == RegistrationState.UNREGISTERED || evt.getNewState() == RegistrationState.AUTHENTICATION_FAILED || evt.getNewState() == RegistrationState.CONNECTION_FAILED) { } } } /** * This class listen to GeolocationExtension into Presence Packet. * If GeolocationExtension is found, an event is sent. * * @author Guillaume Schreiner */ private class GeolocationPresencePacketListener implements PacketListener { /** * Match incoming packets with geolocation Extension tags for * dispatching a new event. * * @param packet matching Geolocation Extension tags. */ public void processPacket(Packet packet) { String from = StringUtils.parseBareAddress(packet.getFrom()); GeolocationPacketExtension geolocExt = (GeolocationPacketExtension) packet.getExtension( GeolocationPacketExtensionProvider.ELEMENT_NAME, GeolocationPacketExtensionProvider.NAMESPACE); if (geolocExt != null) { if (logger.isDebugEnabled()) logger.debug("GeolocationExtension found from " + from + ":" + geolocExt.toXML()); Map<String, String> newGeolocation = GeolocationJabberUtils.convertExtensionToMap(geolocExt); this.fireGeolocationContactChangeEvent( from, newGeolocation); } } /** * Notify registred listeners for a new incoming GeolocationExtension. * * @param sourceContact which send a new Geolocation. * @param newGeolocation the new given Geolocation. */ public void fireGeolocationContactChangeEvent( String sourceContact, Map<String, String> newGeolocation) { if (logger.isDebugEnabled()) logger.debug("Trying to dispatch geolocation contact update for " + sourceContact); Contact source = opsetprez.findContactByID(sourceContact); GeolocationEvent evt = new GeolocationEvent( source , jabberProvider , newGeolocation , OperationSetGeolocationJabberImpl.this); if (logger.isDebugEnabled()) logger.debug("Dispatching geolocation contact update. Listeners=" + geolocationContactsListeners.size() + " evt=" + evt); GeolocationListener[] listeners; synchronized (geolocationContactsListeners) { listeners = geolocationContactsListeners.toArray( new GeolocationListener[ geolocationContactsListeners.size()]); } for (GeolocationListener listener : listeners) listener.contactGeolocationChanged(evt); } } }