/*
This file is part of Project MAXS.
MAXS and its modules is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
MAXS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with MAXS. If not, see <http://www.gnu.org/licenses/>.
*/
package org.projectmaxs.transport.xmpp.xmppservice;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.roster.Roster;
import org.jivesoftware.smack.roster.RosterEntry;
import org.jivesoftware.smack.roster.RosterListener;
import org.jivesoftware.smack.roster.packet.RosterPacket;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.Jid;
import org.projectmaxs.shared.global.util.Log;
import org.projectmaxs.transport.xmpp.Settings;
public class XMPPRoster extends StateChangeListener implements RosterListener {
private static final Log LOG = Log.getLog();
private final List<MasterJidListener> mMasterJidListeners = new LinkedList<MasterJidListener>();
private Settings mSettings;
private Roster mRoster;
private XMPPConnection mConnection;
private boolean mMasterJidAvailable;
public XMPPRoster(Settings settings) {
mSettings = settings;
}
/*
* StateChangeListener callbacks
*/
@Override
public void newConnection(XMPPConnection connection) {
mConnection = connection;
mRoster = Roster.getInstanceFor(connection);
mRoster.addRosterListener(this);
}
@Override
public void connected(XMPPConnection connection) {
Set<EntityBareJid> masterJids = mSettings.getMasterJids();
for (EntityBareJid jid : masterJids)
friendJid(jid);
checkMasterJids();
}
@Override
public void disconnected(XMPPConnection connection) {
mMasterJidAvailable = false;
}
/*
* RosterListener callbacks
*/
@Override
public void entriesAdded(Collection<Jid> arg0) {}
@Override
public void entriesDeleted(Collection<Jid> arg0) {}
@Override
public void entriesUpdated(Collection<Jid> arg0) {}
@Override
public void presenceChanged(Presence presence) {
checkMasterJids();
}
protected boolean isMasterJidAvailable() {
return mMasterJidAvailable;
}
protected void addMasterJidListener(MasterJidListener listener) {
mMasterJidListeners.add(listener);
}
protected void removeMasterJidListener(MasterJidListener listener) {
mMasterJidListeners.remove(listener);
}
private void checkMasterJids() {
boolean masterJidAvailable = false;
for (BareJid jid : mSettings.getMasterJids()) {
Presence presence = mRoster.getPresence(jid);
if (presence.isAvailable()) {
if (mSettings.isExcludedResource(presence.getFrom().getResourceOrNull())) {
// Skip excluded resources
continue;
}
masterJidAvailable = true;
// we found at least one available master JID, break here
break;
}
}
if (mMasterJidAvailable == false && masterJidAvailable == true) {
for (MasterJidListener listener : mMasterJidListeners)
listener.masterJidAvailable();
}
mMasterJidAvailable = masterJidAvailable;
}
/**
* Subscribe and request subscription with a given JID. Essentially become a
* "friend" of the JID.
*
* @param userID
*/
private void friendJid(BareJid userID) {
if (!mRoster.contains(userID)) {
try {
mRoster.createEntry(userID, userID.toString(), null);
grantSubscription(userID, mConnection);
requestSubscription(userID, mConnection);
} catch (Exception e) {
// TODO
return;
}
}
RosterEntry rosterEntry = mRoster.getEntry(userID);
// async code here, the server may not have added the entry yet, so bail
// out here
if (rosterEntry == null) return;
RosterPacket.ItemType type = rosterEntry.getType();
switch (type) {
case from:
requestSubscription(userID, mConnection);
break;
case to:
grantSubscription(userID, mConnection);
break;
case none:
grantSubscription(userID, mConnection);
requestSubscription(userID, mConnection);
break;
case both:
default:
break;
}
}
/**
* grants the given JID the subscription (e.g. viewing your online state)
*
* @param jid
* @param connection
*/
private static void grantSubscription(BareJid jid, XMPPConnection connection) {
Presence presence = new Presence(Presence.Type.subscribed);
sendPresenceTo(jid, presence, connection);
}
/**
* request the subscription from a given JID
*
* @param jid
* @param connection
*/
private static void requestSubscription(BareJid jid, XMPPConnection connection) {
Presence presence = new Presence(Presence.Type.subscribe);
sendPresenceTo(jid, presence, connection);
}
private static void sendPresenceTo(BareJid to, Presence presence, XMPPConnection connection) {
presence.setTo(to);
try {
connection.sendStanza(presence);
} catch (InterruptedException | NotConnectedException e) {
LOG.w("sendPresenceTo", e);
}
}
public static class MasterJidListener {
public void masterJidAvailable() {}
}
}