blob: a9d1f12b8214fb30f52057de801f8002192db9ef [file] [log] [blame]
Shuyi Chend7955ce2013-05-22 14:51:55 -07001/**
2 * $RCSfile$
3 * $Revision$
4 * $Date$
5 *
6 * Copyright 2003-2006 Jive Software.
7 *
8 * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21package org.jivesoftware.smackx;
22
23import org.jivesoftware.smack.*;
24import org.jivesoftware.smack.filter.AndFilter;
25import org.jivesoftware.smack.filter.IQTypeFilter;
26import org.jivesoftware.smack.filter.PacketIDFilter;
27import org.jivesoftware.smack.filter.PacketTypeFilter;
28import org.jivesoftware.smack.packet.IQ;
29import org.jivesoftware.smack.packet.Message;
30import org.jivesoftware.smack.packet.Packet;
31import org.jivesoftware.smack.packet.Presence;
32import org.jivesoftware.smackx.packet.DiscoverInfo;
33import org.jivesoftware.smackx.packet.LastActivity;
34
35/**
36 * A last activity manager for handling information about the last activity
37 * associated with a Jabber ID. A manager handles incoming LastActivity requests
38 * of existing Connections. It also allows to request last activity information
39 * of other users.
40 * <p>
41 *
42 * LastActivity (XEP-0012) based on the sending JID's type allows for retrieval
43 * of:
44 * <ol>
45 * <li>How long a particular user has been idle
46 * <li>How long a particular user has been logged-out and the message the
47 * specified when doing so.
48 * <li>How long a host has been up.
49 * </ol>
50 * <p/>
51 *
52 * For example to get the idle time of a user logged in a resource, simple send
53 * the LastActivity packet to them, as in the following code:
54 * <p>
55 *
56 * <pre>
57 * Connection con = new XMPPConnection(&quot;jabber.org&quot;);
58 * con.login(&quot;john&quot;, &quot;doe&quot;);
59 * LastActivity activity = LastActivity.getLastActivity(con, &quot;xray@jabber.org/Smack&quot;);
60 * </pre>
61 *
62 * To get the lapsed time since the last user logout is the same as above but
63 * with out the resource:
64 *
65 * <pre>
66 * LastActivity activity = LastActivity.getLastActivity(con, &quot;xray@jabber.org&quot;);
67 * </pre>
68 *
69 * To get the uptime of a host, you simple send the LastActivity packet to it,
70 * as in the following code example:
71 * <p>
72 *
73 * <pre>
74 * LastActivity activity = LastActivity.getLastActivity(con, &quot;jabber.org&quot;);
75 * </pre>
76 *
77 * @author Gabriel Guardincerri
78 * @see <a href="http://xmpp.org/extensions/xep-0012.html">XEP-0012: Last
79 * Activity</a>
80 */
81
82public class LastActivityManager {
83
84 private long lastMessageSent;
85
86 private Connection connection;
87
88 // Enable the LastActivity support on every established connection
89 static {
90 Connection.addConnectionCreationListener(new ConnectionCreationListener() {
91 public void connectionCreated(Connection connection) {
92 new LastActivityManager(connection);
93 }
94 });
95 }
96
97 /**
98 * Creates a last activity manager to response last activity requests.
99 *
100 * @param connection
101 * The Connection that the last activity requests will use.
102 */
103 private LastActivityManager(Connection connection) {
104 this.connection = connection;
105
106 // Listen to all the sent messages to reset the idle time on each one
107 connection.addPacketSendingListener(new PacketListener() {
108 public void processPacket(Packet packet) {
109 Presence presence = (Presence) packet;
110 Presence.Mode mode = presence.getMode();
111 if (mode == null) return;
112 switch (mode) {
113 case available:
114 case chat:
115 // We assume that only a switch to available and chat indicates user activity
116 // since other mode changes could be also a result of some sort of automatism
117 resetIdleTime();
118 }
119 }
120 }, new PacketTypeFilter(Presence.class));
121
122 connection.addPacketListener(new PacketListener() {
123 @Override
124 public void processPacket(Packet packet) {
125 Message message = (Message) packet;
126 // if it's not an error message, reset the idle time
127 if (message.getType() == Message.Type.error) return;
128 resetIdleTime();
129 }
130 }, new PacketTypeFilter(Message.class));
131
132 // Register a listener for a last activity query
133 connection.addPacketListener(new PacketListener() {
134
135 public void processPacket(Packet packet) {
136 LastActivity message = new LastActivity();
137 message.setType(IQ.Type.RESULT);
138 message.setTo(packet.getFrom());
139 message.setFrom(packet.getTo());
140 message.setPacketID(packet.getPacketID());
141 message.setLastActivity(getIdleTime());
142
143 LastActivityManager.this.connection.sendPacket(message);
144 }
145
146 }, new AndFilter(new IQTypeFilter(IQ.Type.GET), new PacketTypeFilter(LastActivity.class)));
147 ServiceDiscoveryManager.getInstanceFor(connection).addFeature(LastActivity.NAMESPACE);
148 resetIdleTime();
149 }
150
151 /**
152 * Resets the idle time to 0, this should be invoked when a new message is
153 * sent.
154 */
155 private void resetIdleTime() {
156 long now = System.currentTimeMillis();
157 synchronized (this) {
158 lastMessageSent = now;
159 }
160 }
161
162 /**
163 * The idle time is the lapsed time between the last message sent and now.
164 *
165 * @return the lapsed time between the last message sent and now.
166 */
167 private long getIdleTime() {
168 long lms;
169 long now = System.currentTimeMillis();
170 synchronized (this) {
171 lms = lastMessageSent;
172 }
173 return ((now - lms) / 1000);
174 }
175
176 /**
177 * Returns the last activity of a particular jid. If the jid is a full JID
178 * (i.e., a JID of the form of 'user@host/resource') then the last activity
179 * is the idle time of that connected resource. On the other hand, when the
180 * jid is a bare JID (e.g. 'user@host') then the last activity is the lapsed
181 * time since the last logout or 0 if the user is currently logged in.
182 * Moreover, when the jid is a server or component (e.g., a JID of the form
183 * 'host') the last activity is the uptime.
184 *
185 * @param con
186 * the current Connection.
187 * @param jid
188 * the JID of the user.
189 * @return the LastActivity packet of the jid.
190 * @throws XMPPException
191 * thrown if a server error has occured.
192 */
193 public static LastActivity getLastActivity(Connection con, String jid) throws XMPPException {
194 LastActivity activity = new LastActivity();
195 activity.setTo(jid);
196
197 PacketCollector collector = con.createPacketCollector(new PacketIDFilter(activity.getPacketID()));
198 con.sendPacket(activity);
199
200 LastActivity response = (LastActivity) collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
201
202 // Cancel the collector.
203 collector.cancel();
204 if (response == null) {
205 throw new XMPPException("No response from server on status set.");
206 }
207 if (response.getError() != null) {
208 throw new XMPPException(response.getError());
209 }
210 return response;
211 }
212
213 /**
214 * Returns true if Last Activity (XEP-0012) is supported by a given JID
215 *
216 * @param connection the connection to be used
217 * @param jid a JID to be tested for Last Activity support
218 * @return true if Last Activity is supported, otherwise false
219 */
220 public static boolean isLastActivitySupported(Connection connection, String jid) {
221 try {
222 DiscoverInfo result =
223 ServiceDiscoveryManager.getInstanceFor(connection).discoverInfo(jid);
224 return result.containsFeature(LastActivity.NAMESPACE);
225 }
226 catch (XMPPException e) {
227 return false;
228 }
229 }
230}