001/** 002 * 003 * Copyright 2003-2007 Jive Software. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.jivesoftware.smack.packet; 019 020import java.util.Locale; 021 022import org.jivesoftware.smack.packet.id.StanzaIdUtil; 023import org.jivesoftware.smack.util.Objects; 024import org.jivesoftware.smack.util.TypedCloneable; 025import org.jivesoftware.smack.util.XmlStringBuilder; 026 027/** 028 * Represents XMPP presence packets. Every presence stanza(/packet) has a type, which is one of 029 * the following values: 030 * <ul> 031 * <li>{@link Presence.Type#available available} -- (Default) indicates the user is available to 032 * receive messages. 033 * <li>{@link Presence.Type#unavailable unavailable} -- the user is unavailable to receive messages. 034 * <li>{@link Presence.Type#subscribe subscribe} -- request subscription to recipient's presence. 035 * <li>{@link Presence.Type#subscribed subscribed} -- grant subscription to sender's presence. 036 * <li>{@link Presence.Type#unsubscribe unsubscribe} -- request removal of subscription to 037 * sender's presence. 038 * <li>{@link Presence.Type#unsubscribed unsubscribed} -- grant removal of subscription to 039 * sender's presence. 040 * <li>{@link Presence.Type#error error} -- the presence stanza(/packet) contains an error message. 041 * </ul><p> 042 * 043 * A number of attributes are optional: 044 * <ul> 045 * <li>Status -- free-form text describing a user's presence (i.e., gone to lunch). 046 * <li>Priority -- non-negative numerical priority of a sender's resource. The 047 * highest resource priority is the default recipient of packets not addressed 048 * to a particular resource. 049 * <li>Mode -- one of five presence modes: {@link Mode#available available} (the default), 050 * {@link Mode#chat chat}, {@link Mode#away away}, {@link Mode#xa xa} (extended away), and 051 * {@link Mode#dnd dnd} (do not disturb). 052 * </ul><p> 053 * 054 * Presence packets are used for two purposes. First, to notify the server of 055 * the user's current presence status. Second, they are used to subscribe and 056 * unsubscribe users from the roster. 057 * 058 * @author Matt Tucker 059 */ 060public final class Presence extends Stanza implements TypedCloneable<Presence> { 061 062 public static final String ELEMENT = "presence"; 063 064 private Type type = Type.available; 065 private String status = null; 066 private int priority = Integer.MIN_VALUE; 067 private Mode mode = null; 068 069 /** 070 * Creates a new presence update. Status, priority, and mode are left un-set. 071 * 072 * @param type the type. 073 */ 074 public Presence(Type type) { 075 setType(type); 076 } 077 078 /** 079 * Creates a new presence update with a specified status, priority, and mode. 080 * 081 * @param type the type. 082 * @param status a text message describing the presence update. 083 * @param priority the priority of this presence update. 084 * @param mode the mode type for this presence update. 085 */ 086 public Presence(Type type, String status, int priority, Mode mode) { 087 setType(type); 088 setStatus(status); 089 setPriority(priority); 090 setMode(mode); 091 } 092 093 /** 094 * Copy constructor. 095 * <p> 096 * This does not perform a deep clone, as extension elements are shared between the new and old 097 * instance. 098 * </p> 099 * 100 * @param other 101 */ 102 public Presence(Presence other) { 103 super(other); 104 this.type = other.type; 105 this.status = other.status; 106 this.priority = other.priority; 107 this.mode = other.mode; 108 } 109 110 /** 111 * Returns true if the {@link Type presence type} is available (online) and 112 * false if the user is unavailable (offline), or if this is a presence packet 113 * involved in a subscription operation. This is a convenience method 114 * equivalent to <tt>getType() == Presence.Type.available</tt>. Note that even 115 * when the user is available, their presence mode may be {@link Mode#away away}, 116 * {@link Mode#xa extended away} or {@link Mode#dnd do not disturb}. Use 117 * {@link #isAway()} to determine if the user is away. 118 * 119 * @return true if the presence type is available. 120 */ 121 public boolean isAvailable() { 122 return type == Type.available; 123 } 124 125 /** 126 * Returns true if the presence type is {@link Type#available available} and the presence 127 * mode is {@link Mode#away away}, {@link Mode#xa extended away}, or 128 * {@link Mode#dnd do not disturb}. False will be returned when the type or mode 129 * is any other value, including when the presence type is unavailable (offline). 130 * This is a convenience method equivalent to 131 * <tt>type == Type.available && (mode == Mode.away || mode == Mode.xa || mode == Mode.dnd)</tt>. 132 * 133 * @return true if the presence type is available and the presence mode is away, xa, or dnd. 134 */ 135 public boolean isAway() { 136 return type == Type.available && (mode == Mode.away || mode == Mode.xa || mode == Mode.dnd); 137 } 138 139 /** 140 * Returns the type of this presence packet. 141 * 142 * @return the type of the presence packet. 143 */ 144 public Type getType() { 145 return type; 146 } 147 148 /** 149 * Sets the type of the presence packet. 150 * 151 * @param type the type of the presence packet. 152 */ 153 public void setType(Type type) { 154 this.type = Objects.requireNonNull(type, "Type cannot be null"); 155 } 156 157 /** 158 * Returns the status message of the presence update, or <tt>null</tt> if there 159 * is not a status. The status is free-form text describing a user's presence 160 * (i.e., "gone to lunch"). 161 * 162 * @return the status message. 163 */ 164 public String getStatus() { 165 return status; 166 } 167 168 /** 169 * Sets the status message of the presence update. The status is free-form text 170 * describing a user's presence (i.e., "gone to lunch"). 171 * 172 * @param status the status message. 173 */ 174 public void setStatus(String status) { 175 this.status = status; 176 } 177 178 /** 179 * Returns the priority of the presence, or Integer.MIN_VALUE if no priority has been set. 180 * 181 * @return the priority. 182 */ 183 public int getPriority() { 184 return priority; 185 } 186 187 /** 188 * Sets the priority of the presence. The valid range is -128 through 128. 189 * 190 * @param priority the priority of the presence. 191 * @throws IllegalArgumentException if the priority is outside the valid range. 192 */ 193 public void setPriority(int priority) { 194 if (priority < -128 || priority > 128) { 195 throw new IllegalArgumentException("Priority value " + priority + 196 " is not valid. Valid range is -128 through 128."); 197 } 198 this.priority = priority; 199 } 200 201 /** 202 * Returns the mode of the presence update. 203 * 204 * @return the mode. 205 */ 206 public Mode getMode() { 207 if (mode == null) { 208 return Mode.available; 209 } 210 return mode; 211 } 212 213 /** 214 * Sets the mode of the presence update. A null presence mode value is interpreted 215 * to be the same thing as {@link Presence.Mode#available}. 216 * 217 * @param mode the mode. 218 */ 219 public void setMode(Mode mode) { 220 this.mode = mode; 221 } 222 223 @Override 224 public XmlStringBuilder toXML() { 225 XmlStringBuilder buf = new XmlStringBuilder(); 226 buf.halfOpenElement(ELEMENT); 227 addCommonAttributes(buf); 228 if (type != Type.available) { 229 buf.attribute("type", type); 230 } 231 buf.rightAngleBracket(); 232 233 buf.optElement("status", status); 234 if (priority != Integer.MIN_VALUE) { 235 buf.element("priority", Integer.toString(priority)); 236 } 237 if (mode != null && mode != Mode.available) { 238 buf.element("show", mode); 239 } 240 buf.append(getExtensionsXML()); 241 242 // Add the error sub-packet, if there is one. 243 appendErrorIfExists(buf); 244 245 buf.closeElement(ELEMENT); 246 247 return buf; 248 } 249 250 /** 251 * Creates and returns a copy of this presence stanza. 252 * <p> 253 * This does not perform a deep clone, as extension elements are shared between the new and old 254 * instance. 255 * </p> 256 * @return a clone of this presence. 257 */ 258 @Override 259 public Presence clone() { 260 return new Presence(this); 261 } 262 263 /** 264 * Clone this presence and set a newly generated stanza ID as the clone's ID. 265 * 266 * @return a "clone" of this presence with a different stanza ID. 267 * @since 4.1.2 268 */ 269 public Presence cloneWithNewId() { 270 Presence clone = clone(); 271 clone.setStanzaId(StanzaIdUtil.newStanzaId()); 272 return clone; 273 } 274 275 /** 276 * An enum to represent the presence type. Note that presence type is often confused 277 * with presence mode. Generally, if a user is signed in to a server, they have a presence 278 * type of {@link #available available}, even if the mode is {@link Mode#away away}, 279 * {@link Mode#dnd dnd}, etc. The presence type is only {@link #unavailable unavailable} when 280 * the user is signing out of the server. 281 */ 282 public enum Type { 283 284 /** 285 * The user is available to receive messages (default). 286 */ 287 available, 288 289 /** 290 * The user is unavailable to receive messages. 291 */ 292 unavailable, 293 294 /** 295 * Request subscription to recipient's presence. 296 */ 297 subscribe, 298 299 /** 300 * Grant subscription to sender's presence. 301 */ 302 subscribed, 303 304 /** 305 * Request removal of subscription to sender's presence. 306 */ 307 unsubscribe, 308 309 /** 310 * Grant removal of subscription to sender's presence. 311 */ 312 unsubscribed, 313 314 /** 315 * The presence stanza(/packet) contains an error message. 316 */ 317 error, 318 319 /** 320 * A presence probe as defined in section 4.3 of RFC 6121 321 */ 322 probe, 323 ; 324 325 /** 326 * Converts a String into the corresponding types. Valid String values that can be converted 327 * to types are: "available", "unavailable", "subscribe", "subscribed", "unsubscribe", 328 * "unsubscribed" and "error". 329 * 330 * @param string the String value to covert. 331 * @return the corresponding Type. 332 * @throws IllegalArgumentException when not able to parse the string parameter 333 * @throws NullPointerException if the string is null 334 */ 335 public static Type fromString(String string) { 336 return Type.valueOf(string.toLowerCase(Locale.US)); 337 } 338 } 339 340 /** 341 * An enum to represent the presence mode. 342 */ 343 public enum Mode { 344 345 /** 346 * Free to chat. 347 */ 348 chat, 349 350 /** 351 * Available (the default). 352 */ 353 available, 354 355 /** 356 * Away. 357 */ 358 away, 359 360 /** 361 * Away for an extended period of time. 362 */ 363 xa, 364 365 /** 366 * Do not disturb. 367 */ 368 dnd; 369 370 /** 371 * Converts a String into the corresponding types. Valid String values that can be converted 372 * to types are: "chat", "available", "away", "xa", and "dnd". 373 * 374 * @param string the String value to covert. 375 * @return the corresponding Type. 376 * @throws IllegalArgumentException when not able to parse the string parameter 377 * @throws NullPointerException if the string is null 378 */ 379 public static Mode fromString(String string) { 380 return Mode.valueOf(string.toLowerCase(Locale.US)); 381 } 382 } 383}