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