From 260c5539b55ef2c9186003a42064801c074f96af Mon Sep 17 00:00:00 2001 From: adiaholic Date: Wed, 1 May 2019 13:29:10 +0530 Subject: [PATCH] Add support for XEP-0118: UserTune This commit will enable user to communicate information about music to which user is listening. This feature is less of a requirement and more like fun to me. An attempt at solving SMACK-257. Incase you see any chances of improvement, please let me know :) --- documentation/extensions/index.md | 1 + .../smackx/usertune/UserTuneListener.java | 27 ++ .../smackx/usertune/UserTuneManager.java | 143 ++++++++++ .../usertune/element/UserTuneElement.java | 267 ++++++++++++++++++ .../smackx/usertune/element/package-info.java | 21 ++ .../smackx/usertune/package-info.java | 21 ++ .../usertune/provider/UserTuneProvider.java | 89 ++++++ .../usertune/provider/package-info.java | 21 ++ .../extensions.providers | 7 + .../smackx/usertune/UserTuneElementTest.java | 74 +++++ .../smackx/usertune/UserTuneManagerTest.java | 58 ++++ .../usertune/UserTuneIntegrationTest.java | 89 ++++++ .../smackx/usertune/package-info.java | 17 ++ 13 files changed, 835 insertions(+) create mode 100644 smack-extensions/src/main/java/org/jivesoftware/smackx/usertune/UserTuneListener.java create mode 100644 smack-extensions/src/main/java/org/jivesoftware/smackx/usertune/UserTuneManager.java create mode 100644 smack-extensions/src/main/java/org/jivesoftware/smackx/usertune/element/UserTuneElement.java create mode 100644 smack-extensions/src/main/java/org/jivesoftware/smackx/usertune/element/package-info.java create mode 100644 smack-extensions/src/main/java/org/jivesoftware/smackx/usertune/package-info.java create mode 100644 smack-extensions/src/main/java/org/jivesoftware/smackx/usertune/provider/UserTuneProvider.java create mode 100644 smack-extensions/src/main/java/org/jivesoftware/smackx/usertune/provider/package-info.java create mode 100644 smack-extensions/src/test/java/org/jivesoftware/smackx/usertune/UserTuneElementTest.java create mode 100644 smack-extensions/src/test/java/org/jivesoftware/smackx/usertune/UserTuneManagerTest.java create mode 100644 smack-integration-test/src/main/java/org/jivesoftware/smackx/usertune/UserTuneIntegrationTest.java create mode 100644 smack-integration-test/src/main/java/org/jivesoftware/smackx/usertune/package-info.java diff --git a/documentation/extensions/index.md b/documentation/extensions/index.md index b5c279f7d..5043f940d 100644 --- a/documentation/extensions/index.md +++ b/documentation/extensions/index.md @@ -56,6 +56,7 @@ Smack Extensions and currently supported XEPs of smack-extensions | [SI File Transfer](filetransfer.md) | [XEP-0096](https://xmpp.org/extensions/xep-0096.html) | n/a | Transfer files between two users over XMPP. | | User Mood | [XEP-0107](https://xmpp.org/extensions/xep-0107.html) | 1.2.1 | Communicate the users current mood. | | [Entity Capabilities](caps.md) | [XEP-0115](https://xmpp.org/extensions/xep-0115.html) | n/a | Broadcasting and dynamic discovery of entity capabilities. | +| User Tune | [XEP-0118](https://xmpp.org/extensions/xep-0118.html) | n/a | Defines a payload format for communicating information about music to which a user is listening. | | Data Forms Validation | [XEP-0122](https://xmpp.org/extensions/xep-0122.html) | n/a | Enables an application to specify additional validation guidelines . | | Service Administration | [XEP-0133](https://xmpp.org/extensions/xep-0133.html) | n/a | Recommended best practices for service-level administration of servers and components using Ad-Hoc Commands. | | Stream Compression | [XEP-0138](https://xmpp.org/extensions/xep-0138.html) | n/a | Support for optional compression of the XMPP stream. diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/usertune/UserTuneListener.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/usertune/UserTuneListener.java new file mode 100644 index 000000000..c513579fc --- /dev/null +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/usertune/UserTuneListener.java @@ -0,0 +1,27 @@ +/** + * + * Copyright 2019 Aditya Borikar. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.usertune; + +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smackx.usertune.element.UserTuneElement; + +import org.jxmpp.jid.BareJid; + +public interface UserTuneListener { + + void onUserTuneUpdated(BareJid jid, Message message, UserTuneElement userTuneElement); +} diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/usertune/UserTuneManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/usertune/UserTuneManager.java new file mode 100644 index 000000000..9b158627e --- /dev/null +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/usertune/UserTuneManager.java @@ -0,0 +1,143 @@ +/** + * + * Copyright 2019 Aditya Borikar. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.usertune; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.WeakHashMap; + +import org.jivesoftware.smack.AsyncButOrdered; +import org.jivesoftware.smack.Manager; +import org.jivesoftware.smack.SmackException.NoResponseException; +import org.jivesoftware.smack.SmackException.NotConnectedException; +import org.jivesoftware.smack.SmackException.NotLoggedInException; +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.XMPPException.XMPPErrorException; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; +import org.jivesoftware.smackx.pep.PepListener; +import org.jivesoftware.smackx.pep.PepManager; +import org.jivesoftware.smackx.pubsub.EventElement; +import org.jivesoftware.smackx.pubsub.ItemsExtension; +import org.jivesoftware.smackx.pubsub.PayloadItem; +import org.jivesoftware.smackx.pubsub.PubSubException.NotALeafNodeException; +import org.jivesoftware.smackx.usertune.element.UserTuneElement; + +import org.jxmpp.jid.BareJid; +import org.jxmpp.jid.EntityBareJid; + +/** + * Entry point for Smacks API for XEP-0118: User Tune. + *
+ * To publish a UserTune, please use {@link #publishUserTune(UserTuneElement)} method. This will publish the node. + *
+ * To stop publishing a UserTune, please use {@link #clearUserTune()} method. This will send a disabling publish signal. + *
+ * To add a UserTune listener in order to remain updated with other users UserTune, use {@link #addUserTuneListener(UserTuneListener)} method. + *
+ * To link a UserTuneElement with {@link Message}, use 'message.addExtension(userTuneElement)'. + *
+ * An example to illustrate is provided inside UserTuneElementTest inside the test package. + *
+ * @see + * XEP-0118: User Tune + */ +public final class UserTuneManager extends Manager { + + public static final String USERTUNE_NODE = "http://jabber.org/protocol/tune"; + public static final String USERTUNE_NOTIFY = USERTUNE_NODE + "+notify"; + + private static final Map INSTANCES = new WeakHashMap<>(); + + private final Set userTuneListeners = new HashSet<>(); + private final AsyncButOrdered asyncButOrdered = new AsyncButOrdered<>(); + private final PepManager pepManager; + private boolean ENABLE_USER_TUNE_NOTIFICATIONS_BY_DEFAULT = true; + + public static synchronized UserTuneManager getInstanceFor(XMPPConnection connection) throws NotLoggedInException { + UserTuneManager manager = INSTANCES.get(connection); + if (manager == null) { + manager = new UserTuneManager(connection); + INSTANCES.put(connection, manager); + } + return manager; + } + + private UserTuneManager(XMPPConnection connection) throws NotLoggedInException { + super(connection); + pepManager = PepManager.getInstanceFor(connection); + pepManager.addPepListener(new PepListener() { + @Override + public void eventReceived(EntityBareJid from, EventElement event, Message message) { + if (!USERTUNE_NODE.equals(event.getEvent().getNode())) { + return; + } + + final BareJid contact = from.asBareJid(); + asyncButOrdered.performAsyncButOrdered(contact, () -> { + ItemsExtension items = (ItemsExtension) event.getExtensions().get(0); + PayloadItem payload = (PayloadItem) items.getItems().get(0); + UserTuneElement tune = (UserTuneElement) payload.getPayload(); + + for (UserTuneListener listener : userTuneListeners) { + synchronized (userTuneListeners) { + listener.onUserTuneUpdated(contact, message, tune); + } + } + }); + } + }); + if (ENABLE_USER_TUNE_NOTIFICATIONS_BY_DEFAULT) { + enableUserTuneNotifications(); + } + } + + public void setUserTuneNotificationsEnabledByDefault(boolean bool) { + ENABLE_USER_TUNE_NOTIFICATIONS_BY_DEFAULT = bool; + } + + public void enableUserTuneNotifications() { + ServiceDiscoveryManager.getInstanceFor(connection()).addFeature(USERTUNE_NOTIFY); + } + + @SuppressWarnings("unused") + private void disableUserTuneNotifications() { + ServiceDiscoveryManager.getInstanceFor(connection()).removeFeature(USERTUNE_NOTIFY); + } + + public void clearUserTune() throws NotLoggedInException, NotALeafNodeException, NoResponseException, NotConnectedException, XMPPErrorException, InterruptedException { + publishUserTune(UserTuneElement.EMPTY_USER_TUNE); + } + + public void publishUserTune(UserTuneElement userTuneElement) throws NotLoggedInException, NotALeafNodeException, NoResponseException, NotConnectedException, XMPPErrorException, InterruptedException { + // TODO: To prevent a large number of updates when a user is skipping through tracks, an implementation SHOULD wait several seconds before publishing new tune information. + pepManager.publish(USERTUNE_NODE, new PayloadItem<>(userTuneElement)); + } + + public void addUserTuneListener(UserTuneListener listener) { + synchronized (userTuneListeners) { + userTuneListeners.add(listener); + } + } + + public void removeUserTuneListener(UserTuneListener listener) { + synchronized (userTuneListeners) { + userTuneListeners.remove(listener); + } + } +} diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/usertune/element/UserTuneElement.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/usertune/element/UserTuneElement.java new file mode 100644 index 000000000..6cd1ca271 --- /dev/null +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/usertune/element/UserTuneElement.java @@ -0,0 +1,267 @@ +/** + * + * Copyright 2019 Aditya Borikar. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.usertune.element; + +import java.net.URI; + +import org.jivesoftware.smack.datatypes.UInt16; +import org.jivesoftware.smack.packet.ExtensionElement; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.packet.XmlEnvironment; +import org.jivesoftware.smack.util.EqualsUtil; +import org.jivesoftware.smack.util.HashCode; +import org.jivesoftware.smack.util.XmlStringBuilder; + +/** + * {@link ExtensionElement} that contains the UserTune.
+ * Instance of UserTuneElement can be created using {@link Builder#build()} + * method. + */ +public final class UserTuneElement implements ExtensionElement { + + public static final String NAMESPACE = "http://jabber.org/protocol/tune"; + public static final String ELEMENT = "tune"; + + private final String artist; + private final UInt16 length; + private final Integer rating; + private final String source; + private final String title; + private final String track; + private final URI uri; + + public static final UserTuneElement EMPTY_USER_TUNE = null; + + private UserTuneElement(Builder builder) { + this.artist = builder.artist; + this.length = builder.length; + this.rating = builder.rating; + this.source = builder.source; + this.title = builder.title; + this.track = builder.track; + this.uri = builder.uri; + } + + @Override + public String getNamespace() { + return NAMESPACE; + } + + @Override + public String getElementName() { + return ELEMENT; + } + + public String getArtist() { + return artist; + } + + public UInt16 getLength() { + return length; + } + + public Integer getRating() { + return rating; + } + + public String getSource() { + return source; + } + + public String getTitle() { + return title; + } + + public String getTrack() { + return track; + } + + public URI getUri() { + return uri; + } + + @Override + public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) { + XmlStringBuilder xml = new XmlStringBuilder(this); + if (isEmptyUserTune()) { + return xml.closeEmptyElement(); + } + xml.rightAngleBracket(); + xml.optElement("artist", artist); + xml.optElement("length", length); + xml.optElement("rating", rating); + xml.optElement("source", source); + xml.optElement("title", title); + xml.optElement("track", track); + xml.optElement("uri", uri); + return xml.closeElement(getElementName()); + } + + private boolean isEmptyUserTune() { + return this.equals(EMPTY_USER_TUNE); + } + + public static boolean hasUserTuneElement(Message message) { + return message.hasExtension(ELEMENT, NAMESPACE); + } + + public static UserTuneElement from(Message message) { + return message.getExtension(ELEMENT, NAMESPACE); + } + + @Override + public int hashCode() { + return HashCode.builder() + .append(artist) + .append(length) + .append(rating) + .append(source) + .append(title) + .append(track) + .append(uri).build(); + } + + @Override + public boolean equals(Object obj) { + return EqualsUtil + .equals(this, obj, (equalsBuilder, otherTune) -> equalsBuilder + .append(artist, otherTune.artist) + .append(length, otherTune.length) + .append(rating, otherTune.rating) + .append(source, otherTune.source) + .append(title, otherTune.source) + .append(title, otherTune.title) + .append(track, otherTune.track) + .append(uri, otherTune.uri)); + } + + /** + * Returns a new instance of {@link Builder}. + * @return Builder + */ + public static Builder getBuilder() { + return new Builder(); + } + + /** + * This class defines a Builder class for {@link UserTuneElement}.
+ * {@link UserTuneElement} instance can be obtained using the {@link #build()} method as follows.
+ * UserTuneElement.Builder builder = new UserTuneElement.Builder(); + * builder.setSource("Yessongs"); builder.setTitle("Heart of the Sunrise"); + * UserTuneElement userTuneElement = builder.build();
+ * Values such as title, source, artist, length, source, track and uri can be set using their respective setters through {@link Builder} + */ + public static final class Builder { + private String artist; + private UInt16 length; + private Integer rating; + private String source; + private String title; + private String track; + private URI uri; + + private Builder() { + } + + /** + * Artist is an optional element in UserTuneElement. + * @param artist. + * @return builder. + */ + public Builder setArtist(String artist) { + this.artist = artist; + return this; + } + + /** + * Length is an optional element in UserTuneElement. + * @param length. + * @return builder. + */ + public Builder setLength(int length) { + return setLength(UInt16.from(length)); + } + + /** + * Length is an optional element in UserTuneElement. + * @param length. + * @return builder. + */ + public Builder setLength(UInt16 length) { + this.length = length; + return this; + } + + /** + * Rating is an optional element in UserTuneElement. + * @param rating. + * @return builder. + */ + public Builder setRating(int rating) { + this.rating = rating; + return this; + } + + /** + * Source is an optional element in UserTuneElement. + * @param source. + * @return builder. + */ + public Builder setSource(String source) { + this.source = source; + return this; + } + + /** + * Title is an optional element in UserTuneElement. + * @param title. + * @return builder. + */ + public Builder setTitle(String title) { + this.title = title; + return this; + } + + /** + * Track is an optional element in UserTuneElement. + * @param track. + * @return builder. + */ + public Builder setTrack(String track) { + this.track = track; + return this; + } + + /** + * URI is an optional element in UserTuneElement. + * @param uri. + * @return builder. + */ + public Builder setUri(URI uri) { + this.uri = uri; + return this; + } + + /** + * This method is called to build a UserTuneElement. + * @return UserTuneElement. + */ + public UserTuneElement build() { + return new UserTuneElement(this); + } + } +} diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/usertune/element/package-info.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/usertune/element/package-info.java new file mode 100644 index 000000000..06849e26a --- /dev/null +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/usertune/element/package-info.java @@ -0,0 +1,21 @@ +/** + * + * Copyright 2019 Aditya Borikar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Smack's API for XEP-0118: User Tune. + */ +package org.jivesoftware.smackx.usertune.element; diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/usertune/package-info.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/usertune/package-info.java new file mode 100644 index 000000000..182ee237e --- /dev/null +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/usertune/package-info.java @@ -0,0 +1,21 @@ +/** + * + * Copyright 2019 Aditya Borikar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Smack's API for XEP-0118: User Tune. + */ +package org.jivesoftware.smackx.usertune; diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/usertune/provider/UserTuneProvider.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/usertune/provider/UserTuneProvider.java new file mode 100644 index 000000000..5b1c423ef --- /dev/null +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/usertune/provider/UserTuneProvider.java @@ -0,0 +1,89 @@ +/** + * + * Copyright 2019 Aditya Borikar. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.usertune.provider; + +import java.io.IOException; +import java.net.URI; + +import org.jivesoftware.smack.packet.XmlEnvironment; +import org.jivesoftware.smack.parsing.SmackParsingException; +import org.jivesoftware.smack.provider.ExtensionElementProvider; +import org.jivesoftware.smack.util.ParserUtils; +import org.jivesoftware.smack.xml.XmlPullParser; +import org.jivesoftware.smack.xml.XmlPullParserException; +import org.jivesoftware.smackx.usertune.element.UserTuneElement; + +/** + * This is the Provider Class for {@link UserTuneElement}. + */ +public class UserTuneProvider extends ExtensionElementProvider { + + public static final UserTuneProvider INSTANCE = new UserTuneProvider(); + + @Override + public UserTuneElement parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) + throws XmlPullParserException, IOException, SmackParsingException { + + UserTuneElement.Builder builder = UserTuneElement.getBuilder(); + XmlPullParser.TagEvent tag = parser.nextTag(); + outerloop: while (true) { + switch (tag) { + case START_ELEMENT: + String name = parser.getName(); + String namespace = parser.getNamespace(); + if (!UserTuneElement.NAMESPACE.equals(namespace)) { + continue outerloop; + } + while (tag == XmlPullParser.TagEvent.START_ELEMENT) { + switch (name) { + case "artist": + builder.setArtist(parser.nextText()); + break; + case "length": + builder.setLength(ParserUtils.getIntegerFromNextText(parser)); + break; + case "rating": + builder.setRating(ParserUtils.getIntegerFromNextText(parser)); + break; + case "source": + builder.setSource(parser.nextText()); + break; + case "title": + builder.setTitle(parser.nextText()); + break; + case "track": + builder.setTrack(parser.nextText()); + break; + case "uri": + URI uri = ParserUtils.getUriFromNextText(parser); + builder.setUri(uri); + break; + } + tag = parser.nextTag(); + name = parser.getName(); + } + break; + case END_ELEMENT: + if (parser.getDepth() == initialDepth) { + break outerloop; + } + break; + } + } + return builder.build(); + } +} diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/usertune/provider/package-info.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/usertune/provider/package-info.java new file mode 100644 index 000000000..09bcf2adc --- /dev/null +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/usertune/provider/package-info.java @@ -0,0 +1,21 @@ +/** + * + * Copyright 2019 Aditya Borikar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Smack's API for XEP-0118: User Tune. + */ +package org.jivesoftware.smackx.usertune.provider; diff --git a/smack-extensions/src/main/resources/org.jivesoftware.smack.extensions/extensions.providers b/smack-extensions/src/main/resources/org.jivesoftware.smack.extensions/extensions.providers index a34d5435d..c673a9a2f 100644 --- a/smack-extensions/src/main/resources/org.jivesoftware.smack.extensions/extensions.providers +++ b/smack-extensions/src/main/resources/org.jivesoftware.smack.extensions/extensions.providers @@ -589,4 +589,11 @@ org.jivesoftware.smackx.mood.provider.MoodProvider + + + tune + http://jabber.org/protocol/tune + org.jivesoftware.smackx.usertune.provider.UserTuneProvider + + diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/usertune/UserTuneElementTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/usertune/UserTuneElementTest.java new file mode 100644 index 000000000..9aea03774 --- /dev/null +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/usertune/UserTuneElementTest.java @@ -0,0 +1,74 @@ +/** + * + * Copyright 2019 Aditya Borikar. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.usertune; + +import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import org.jivesoftware.smack.parsing.SmackParsingException; +import org.jivesoftware.smack.test.util.SmackTestSuite; +import org.jivesoftware.smack.test.util.SmackTestUtil; +import org.jivesoftware.smack.test.util.SmackTestUtil.XmlPullParserKind; +import org.jivesoftware.smack.xml.XmlPullParser; +import org.jivesoftware.smack.xml.XmlPullParserException; +import org.jivesoftware.smackx.usertune.element.UserTuneElement; +import org.jivesoftware.smackx.usertune.provider.UserTuneProvider; + +import org.junit.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +public class UserTuneElementTest extends SmackTestSuite { + + private final String xml = "" + + "Yes" + + "686" + + "8" + + "Yessongs" + + "Heart of the Sunrise" + + "3" + + "http://www.yesworld.com/lyrics/Fragile.html#9" + + ""; + + @Test + public void toXmlTest() throws IOException, XmlPullParserException, SmackParsingException, URISyntaxException { + + URI uri = new URI("http://www.yesworld.com/lyrics/Fragile.html#9"); + + UserTuneElement.Builder builder = UserTuneElement.getBuilder(); + UserTuneElement userTuneElement = builder.setArtist("Yes") + .setLength(686) + .setRating(8) + .setSource("Yessongs") + .setTitle("Heart of the Sunrise") + .setTrack("3") + .setUri(uri) + .build(); + assertXmlSimilar(xml, userTuneElement.toXML().toString()); + } + + @ParameterizedTest + @EnumSource(value = SmackTestUtil.XmlPullParserKind.class) + public void userTuneElementProviderTest(XmlPullParserKind parserKind) throws XmlPullParserException, IOException, SmackParsingException { + XmlPullParser parser = SmackTestUtil.getParserFor(xml, parserKind); + UserTuneElement parsed = UserTuneProvider.INSTANCE.parse(parser); + assertXmlSimilar(xml, parsed.toXML().toString()); + } +} diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/usertune/UserTuneManagerTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/usertune/UserTuneManagerTest.java new file mode 100644 index 000000000..bb21fae84 --- /dev/null +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/usertune/UserTuneManagerTest.java @@ -0,0 +1,58 @@ +/** + * + * Copyright 2019 Aditya Borikar. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.usertune; + +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertNotNull; +import static junit.framework.TestCase.assertTrue; + +import java.net.URI; +import java.net.URISyntaxException; + +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.test.util.SmackTestSuite; +import org.jivesoftware.smackx.usertune.element.UserTuneElement; + +import org.junit.Test; + +public class UserTuneManagerTest extends SmackTestSuite{ + + @Test + public void addMessage() throws URISyntaxException { + + UserTuneElement.Builder builder = UserTuneElement.getBuilder(); + builder.setArtist("Yes"); + builder.setLength(686); + builder.setRating(8); + builder.setSource("Yessongs"); + builder.setTitle("Heart of the Sunrise"); + builder.setTrack("3"); + URI uri = new URI("http://www.yesworld.com/lyrics/Fragile.html#9"); + builder.setUri(uri); + UserTuneElement userTuneElement = builder.build(); + + Message message = new Message(); + message.addExtension(userTuneElement); + + assertTrue(message.hasExtension(UserTuneElement.ELEMENT, UserTuneElement.NAMESPACE)); + assertTrue(UserTuneElement.hasUserTuneElement(message)); + + UserTuneElement element = UserTuneElement.from(message); + assertNotNull(element); + assertEquals(userTuneElement, element); + } +} diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/usertune/UserTuneIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/usertune/UserTuneIntegrationTest.java new file mode 100644 index 000000000..86383a819 --- /dev/null +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/usertune/UserTuneIntegrationTest.java @@ -0,0 +1,89 @@ +/** + * + * Copyright 2019 Aditya Borikar. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.usertune; + +import java.net.URI; + +import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.SmackException.NotLoggedInException; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smackx.usertune.element.UserTuneElement; + +import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest; +import org.igniterealtime.smack.inttest.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; +import org.igniterealtime.smack.inttest.util.IntegrationTestRosterUtil; +import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint; +import org.junit.AfterClass; + +import org.jxmpp.jid.BareJid; + +public class UserTuneIntegrationTest extends AbstractSmackIntegrationTest { + + private final UserTuneManager utm1; + private final UserTuneManager utm2; + + public UserTuneIntegrationTest(SmackIntegrationTestEnvironment environment) throws NotLoggedInException { + super(environment); + utm1 = UserTuneManager.getInstanceFor(conOne); + utm2 = UserTuneManager.getInstanceFor(conTwo); + } + + @SmackIntegrationTest + public void test() throws Exception { + URI uri = new URI("http://www.yesworld.com/lyrics/Fragile.html#9"); + UserTuneElement.Builder builder = UserTuneElement.getBuilder(); + UserTuneElement userTuneElement1 = builder.setArtist("Yes") + .setLength(686) + .setRating(8) + .setSource("Yessongs") + .setTitle("Heart of the Sunrise") + .setTrack("3") + .setUri(uri) + .build(); + + IntegrationTestRosterUtil.ensureBothAccountsAreSubscribedToEachOther(conOne, conTwo, timeout); + + final SimpleResultSyncPoint userTuneReceived = new SimpleResultSyncPoint(); + + final UserTuneListener userTuneListener = new UserTuneListener() { + @Override + public void onUserTuneUpdated(BareJid jid, Message message, UserTuneElement userTuneElement) { + if (userTuneElement.equals(userTuneElement1)) { + userTuneReceived.signal(); + } + } + }; + + utm2.addUserTuneListener(userTuneListener); + + try { + utm1.publishUserTune(userTuneElement1); + userTuneReceived.waitForResult(timeout); + } finally { + utm2.removeUserTuneListener(userTuneListener); + } + } + + @AfterClass + public void unsubscribe() + throws SmackException.NotLoggedInException, XMPPException.XMPPErrorException, + SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException { + IntegrationTestRosterUtil.ensureBothAccountsAreNotInEachOthersRoster(conOne, conTwo); + } +} diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/usertune/package-info.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/usertune/package-info.java new file mode 100644 index 000000000..b4fb83a44 --- /dev/null +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/usertune/package-info.java @@ -0,0 +1,17 @@ +/** + * + * Copyright 2019 Aditya Borikar. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.usertune;