summaryrefslogtreecommitdiffstats
blob: e3e68cb3968c22e52f738c2c24e20a5babbb804c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/*
 * Copyright (c) 2010-2015, Isode Limited, London, England.
 * All rights reserved.
 */
package com.isode.stroke.presence;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import com.isode.stroke.client.StanzaChannel;
import com.isode.stroke.elements.Presence;
import com.isode.stroke.elements.StatusShow;
import com.isode.stroke.jid.JID;
import com.isode.stroke.signals.Signal1;
import com.isode.stroke.signals.SignalConnection;
import com.isode.stroke.signals.Slot1;

public class PresenceOracle {
	private final Map<JID,Map<JID,Presence>> entries_ = new HashMap<JID,Map<JID,Presence>>();
	private final StanzaChannel stanzaChannel_;
	private final SignalConnection onPresenceReceivedSignal;
	private final SignalConnection onAvailableChangedSignal;


	public final Signal1<Presence> onPresenceChange = new Signal1<Presence>();

	public PresenceOracle(StanzaChannel stanzaChannel) {
		stanzaChannel_ = stanzaChannel;
		onPresenceReceivedSignal = stanzaChannel_.onPresenceReceived.connect(new Slot1<Presence>() {
				@Override
				public void call(Presence p1) {
					handleIncomingPresence(p1);
				}
			});
		onAvailableChangedSignal = stanzaChannel_.onAvailableChanged.connect(new Slot1<Boolean>() {
				@Override
				public void call(Boolean p1) {
					handleStanzaChannelAvailableChanged(p1);
				}
			});
	}

	void delete() {
		onPresenceReceivedSignal.disconnect();
		onAvailableChangedSignal.disconnect();
	}

	void handleStanzaChannelAvailableChanged(boolean available) {
		if (available) {
			entries_.clear();
		}
	}


	void handleIncomingPresence(Presence presence) {
		JID bareJID = presence.getFrom().toBare();
		if (Presence.Type.Subscribe.equals(presence.getType())) {
		}
		else {
			Presence passedPresence = presence;
			if (presence.getType() == Presence.Type.Unsubscribe) {
				/* 3921bis says that we don't follow up with an unavailable, so simulate this ourselves */
				passedPresence = new Presence();
				passedPresence.setType(Presence.Type.Unavailable);
				passedPresence.setFrom(bareJID);
				passedPresence.setStatus(presence.getStatus());
			}
			Map<JID,Presence> jidMap = entries_.get(bareJID);
			if (jidMap == null) jidMap = new HashMap<JID,Presence>();
			if (passedPresence.getFrom().isBare() && Presence.Type.Unavailable.equals(presence.getType())) {
				/* Have a bare-JID only presence of offline */
				jidMap.clear();
			} else if (Presence.Type.Available.equals(passedPresence.getType())) {
				/* Don't have a bare-JID only offline presence once there are available presences */
				jidMap.remove(bareJID);
			}
			if (Presence.Type.Unavailable.equals(passedPresence.getType()) && jidMap.size() > 1) {
				jidMap.remove(passedPresence.getFrom());
			} else {
				jidMap.put(passedPresence.getFrom(), passedPresence);
			}
			entries_.put(bareJID, jidMap);
			onPresenceChange.emit(passedPresence);
		}
	}

	public Presence getLastPresence(final JID jid) {
		Map<JID,Presence> presenceMap = entries_.get(jid.toBare());
		if (presenceMap == null) return null;
		
		Presence i = presenceMap.get(jid);
		if (i != null) {
			return i;
		} else {
			return null;
		}
	}

	public Collection<Presence> getAllPresence(final JID bareJID) {
		Collection<Presence> results = new ArrayList<Presence>();
		
		Map<JID,Presence> presenceMap = entries_.get(bareJID);
		if (presenceMap == null) return results;
		
		results.addAll(presenceMap.values());
		return results;
	}

	public Presence getHighestPriorityPresence(final JID bareJID) {
		Map<JID,Presence> presenceMap = entries_.get(bareJID);
		if (presenceMap == null) return null;

		Presence highest = null;
		for (Presence current : presenceMap.values()) {
			if (highest == null
					|| current.getPriority() > highest.getPriority()
					|| (current.getPriority() == highest.getPriority()
							&& StatusShow.typeToAvailabilityOrdering(current.getShow()) > StatusShow.typeToAvailabilityOrdering(highest.getShow()))) {
				highest = current;
			}

		}
		return highest;
	}
}