Add support for XEP-0221: Data Forms Media Element

Fixes SMACK-824.
This commit is contained in:
Florian Schmaus 2019-06-10 17:40:26 +02:00
parent 832b20a897
commit c0183775fe
9 changed files with 428 additions and 0 deletions

View File

@ -68,6 +68,7 @@ Smack Extensions and currently supported XEPs of smack-extensions
| Entity Time | [XEP-0202](https://xmpp.org/extensions/xep-0202.html) | n/a | Allows entities to communicate their local time |
| Delayed Delivery | [XEP-0203](https://xmpp.org/extensions/xep-0203.html) | n/a | Extension for communicating the fact that an XML stanza has been delivered with a delay. |
| XMPP Over BOSH | [XEP-0206](https://xmpp.org/extensions/xep-0206.html) | n/a | Use Bidirectional-streams Over Synchronous HTTP (BOSH) to transport XMPP stanzas. |
| Data Forms Media Element | [XEP-0221](https://xmpp.org/extensions/xep-0221.html) | n/a | Allows to include media data in XEP-0004 data forms. |
| Attention | [XEP-0224](https://xmpp.org/extensions/xep-0224.html) | n/a | Getting attention of another user. |
| Bits of Binary | [XEP-0231](https://xmpp.org/extensions/xep-0231.html) | n/a | Including or referring to small bits of binary data in an XML stanza. |
| Best Practices for Resource Locking | [XEP-0296](https://xmpp.org/extensions/xep-0296.html) | n/a | Specifies best practices to be followed by Jabber/XMPP clients about when to lock into, and unlock away from, resources. |

View File

@ -0,0 +1,27 @@
/**
*
* Copyright 2019 Florian Schmaus
*
* 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.mediaelement;
import org.jivesoftware.smackx.mediaelement.provider.MediaElementProvider;
import org.jivesoftware.smackx.xdata.provider.FormFieldChildElementProviderManager;
public class MediaElementManager {
static {
FormFieldChildElementProviderManager.addFormFieldChildElementProvider(new MediaElementProvider());
}
}

View File

@ -0,0 +1,176 @@
/**
*
* Copyright 2019 Florian Schmaus
*
* 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.mediaelement.element;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.packet.FullyQualifiedElement;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.util.NumberUtil;
import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jivesoftware.smackx.xdata.FormField;
import org.jivesoftware.smackx.xdata.FormFieldChildElement;
public class MediaElement implements FormFieldChildElement {
public static final String ELEMENT = "media";
public static final String NAMESPACE = "urn:xmpp:media-element";
public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
private final int height;
private final int width;
private final List<Uri> uris;
public MediaElement(Builder builder) {
this.height = builder.height;
this.width = builder.width;
this.uris = Collections.unmodifiableList(builder.uris);
}
public int getHeight() {
return height;
}
public int getWidth() {
return width;
}
public List<Uri> getUris() {
return uris;
}
@Override
public String getElementName() {
return ELEMENT;
}
@Override
public String getNamespace() {
return NAMESPACE;
}
@Override
public QName getQName() {
return QNAME;
}
@Override
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment);
xml.optIntAttribute("height", height)
.optIntAttribute("width", width)
.rightAngleBracket();
xml.append(uris, xmlEnvironment);
xml.closeElement(this);
return null;
}
public MediaElement from(FormField formField) {
return (MediaElement) formField.getFormFieldChildElement(QNAME);
}
public static Builder builder() {
return new Builder();
}
public static final class Builder {
private int height, width;
private List<Uri> uris = new ArrayList<>();
public Builder setHeightAndWidth(int height, int width) {
this.height = NumberUtil.requireUShort16(height);
this.width = NumberUtil.requireUShort16(width);
return this;
}
public Builder addUri(URI uri, String type) {
return addUri(new Uri(uri, type));
}
public Builder addUri(Uri uri) {
uris.add(uri);
return this;
}
public MediaElement build() {
return new MediaElement(this);
}
}
public static final class Uri implements FullyQualifiedElement {
public static final String ELEMENT = "uri";
public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
private final URI uri;
private final String type;
public Uri(URI uri, String type) {
this.uri = Objects.requireNonNull(uri);
this.type = StringUtils.requireNotNullNorEmpty(type, "The 'type' argument must not be null or empty");
}
public URI getUri() {
return uri;
}
public String getType() {
return type;
}
@Override
public String getElementName() {
return ELEMENT;
}
@Override
public String getNamespace() {
return NAMESPACE;
}
@Override
public QName getQName() {
return QNAME;
}
@Override
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment);
xml.attribute("type", type)
.rightAngleBracket();
xml.escape(uri.toString());
xml.closeElement(this);
return xml;
}
}
}

View File

@ -0,0 +1,21 @@
/**
*
* Copyright 2019 Florian Schmaus
*
* 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.
*/
/**
* Element classes for XEP-0221: Data Forms Media Element.
*/
package org.jivesoftware.smackx.mediaelement.element;

View File

@ -0,0 +1,21 @@
/**
*
* Copyright 2019 Florian Schmaus
*
* 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.
*/
/**
* Smacks implementation of XEP-0221: Data Forms Media Element.
*/
package org.jivesoftware.smackx.mediaelement;

View File

@ -0,0 +1,82 @@
/**
*
* Copyright 2019 Florian Schmaus
*
* 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.mediaelement.provider;
import java.io.IOException;
import java.net.URI;
import java.util.logging.Logger;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.parsing.SmackParsingException.SmackUriSyntaxParsingException;
import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.mediaelement.element.MediaElement;
import org.jivesoftware.smackx.xdata.provider.FormFieldChildElementProvider;
public class MediaElementProvider extends FormFieldChildElementProvider<MediaElement> {
private static final Logger LOGGER = Logger.getLogger(MediaElementProvider.class.getName());
@Override
public QName getQName() {
return MediaElement.QNAME;
}
@Override
public MediaElement parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws IOException, XmlPullParserException, SmackUriSyntaxParsingException {
Integer height = ParserUtils.getIntegerAttribute(parser, "height");
Integer width = ParserUtils.getIntegerAttribute(parser, "width");
MediaElement.Builder mediaElementBuilder = MediaElement.builder();
if (height != null && width != null) {
mediaElementBuilder.setHeightAndWidth(height, width);
} else if (height != null || width != null) {
LOGGER.warning("Only one of height and width set while parsing media element");
}
outerloop: while (true) {
XmlPullParser.TagEvent event = parser.nextTag();
switch (event) {
case START_ELEMENT:
QName qname = parser.getQName();
if (qname.equals(MediaElement.Uri.QNAME)) {
MediaElement.Uri uri = parseUri(parser);
mediaElementBuilder.addUri(uri);
}
break;
case END_ELEMENT:
if (parser.getDepth() == initialDepth) {
break outerloop;
}
break;
}
}
return mediaElementBuilder.build();
}
private MediaElement.Uri parseUri(XmlPullParser parser)
throws SmackUriSyntaxParsingException, XmlPullParserException, IOException {
String type = parser.getAttributeValue("type");
URI uri = ParserUtils.getUriFromNextText(parser);
return new MediaElement.Uri(uri, type);
}
}

View File

@ -0,0 +1,21 @@
/**
*
* Copyright 2019 Florian Schmaus
*
* 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.
*/
/**
* Provider classes for XEP-0221: Data Forms Media Element.
*/
package org.jivesoftware.smackx.mediaelement.provider;

View File

@ -2,6 +2,7 @@
<startupClasses>
<className>org.jivesoftware.smackx.disco.ServiceDiscoveryManager</className>
<className>org.jivesoftware.smackx.xhtmlim.XHTMLManager</className>
<className>org.jivesoftware.smackx.mediaelement.MediaElementManager</className>
<className>org.jivesoftware.smackx.muc.MultiUserChatManager</className>
<className>org.jivesoftware.smackx.muc.bookmarkautojoin.MucBookmarkAutojoinManager</className>
<className>org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager</className>

View File

@ -0,0 +1,78 @@
/**
*
* Copyright 2019 Florian Schmaus
*
* 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.mediaelement.provider;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import java.util.List;
import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.test.util.SmackTestUtil;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.mediaelement.element.MediaElement;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
public class MediaElementProviderTest {
@ParameterizedTest
@EnumSource(SmackTestUtil.XmlPullParserKind.class)
public void simpleMediaElementTest(SmackTestUtil.XmlPullParserKind parserKind) throws XmlPullParserException, IOException, SmackParsingException {
final String xml =
"<media xmlns='urn:xmpp:media-element' height='80' width='290'>" +
"<uri type='audio/x-wav'>" +
"http://victim.example.com/challenges/speech.wav?F3A6292C" +
"</uri>" +
"</media>";
MediaElement mediaElement = SmackTestUtil.parse(xml, MediaElementProvider.class, parserKind);
assertEquals(80, mediaElement.getHeight());
assertEquals(290, mediaElement.getWidth());
List<MediaElement.Uri> uris = mediaElement.getUris();
assertEquals(1, uris.size());
MediaElement.Uri uri = uris.get(0);
assertEquals("audio/x-wav", uri.getType());
assertEquals("http://victim.example.com/challenges/speech.wav?F3A6292C", uri.getUri().toString());
}
@ParameterizedTest
@EnumSource(SmackTestUtil.XmlPullParserKind.class)
public void parseMediaElementTest(SmackTestUtil.XmlPullParserKind parserKind) throws XmlPullParserException, IOException, SmackParsingException {
final String xml =
"<media xmlns='urn:xmpp:media-element'>" +
"<uri type='audio/x-wav'>" +
"http://victim.example.com/challenges/speech.wav?F3A6292C" +
"</uri>" +
"<uri type='audio/ogg; codecs=speex'>" +
"cid:sha1+a15a505e360702b79c75a5f67773072ed392f52a@bob.xmpp.org" +
"</uri>" +
"<uri type='audio/mpeg'>" +
"http://victim.example.com/challenges/speech.mp3?F3A6292C" +
"</uri>" +
"</media>";
MediaElement mediaElement = SmackTestUtil.parse(xml, MediaElementProvider.class, parserKind);
List<MediaElement.Uri> uris = mediaElement.getUris();
assertEquals(3, uris.size());
}
}