diff options
-rw-r--r-- | src/com/vaadin/service/FileTypeResolver.java | 5 | ||||
-rw-r--r-- | src/com/vaadin/terminal/gwt/client/ui/VAudio.java | 42 | ||||
-rw-r--r-- | src/com/vaadin/terminal/gwt/client/ui/VMediaBase.java | 129 | ||||
-rw-r--r-- | src/com/vaadin/terminal/gwt/client/ui/VVideo.java | 73 | ||||
-rw-r--r-- | src/com/vaadin/ui/AbstractMedia.java | 217 | ||||
-rw-r--r-- | src/com/vaadin/ui/Audio.java | 48 | ||||
-rw-r--r-- | src/com/vaadin/ui/Video.java | 77 | ||||
-rw-r--r-- | tests/src/com/vaadin/tests/components/media/Media.java | 74 |
8 files changed, 665 insertions, 0 deletions
diff --git a/src/com/vaadin/service/FileTypeResolver.java b/src/com/vaadin/service/FileTypeResolver.java index 7e3dda29a3..5025184ccd 100644 --- a/src/com/vaadin/service/FileTypeResolver.java +++ b/src/com/vaadin/service/FileTypeResolver.java @@ -134,6 +134,9 @@ public class FileTypeResolver implements Serializable { + "audio/x-pn-realaudio ra rm ram," + "audio/x-scpls pls," + "audio/x-wav wav," + + "audio/ogg ogg," + + "audio/mp4 m4a," + + "audio/x-aac aac," + "image/bitmap bmp," + "image/gif gif," + "image/ief ief," @@ -190,6 +193,8 @@ public class FileTypeResolver implements Serializable { + "video/x-ms-asf asf asx," + "video/x-msvideo avi," + "video/x-sgi-movie movie," + + "video/ogg ogv," + + "video/mp4 mp4," + "x-world/x-vrml vrm vrml wrl"; /** diff --git a/src/com/vaadin/terminal/gwt/client/ui/VAudio.java b/src/com/vaadin/terminal/gwt/client/ui/VAudio.java new file mode 100644 index 0000000000..c8efba2657 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VAudio.java @@ -0,0 +1,42 @@ +package com.vaadin.terminal.gwt.client.ui; + +import com.google.gwt.dom.client.AudioElement; +import com.google.gwt.dom.client.Document; +import com.google.gwt.dom.client.Style; +import com.google.gwt.dom.client.Style.Unit; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.BrowserInfo; +import com.vaadin.terminal.gwt.client.UIDL; + +public class VAudio extends VMediaBase { + private static String CLASSNAME = "v-audio"; + + private AudioElement audio; + + public VAudio() { + audio = Document.get().createAudioElement(); + setMediaElement(audio); + setStyleName(CLASSNAME); + } + + @Override + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + super.updateFromUIDL(uidl, client); + Style style = audio.getStyle(); + + // Make sure that the controls are not clipped if visible. + if (shouldShowControls(uidl) + && (style.getHeight() == null || "".equals(style.getHeight()))) { + if (BrowserInfo.get().isChrome()) { + style.setHeight(32, Unit.PX); + } else { + style.setHeight(25, Unit.PX); + } + } + } + + @Override + protected String getDefaultAltHtml() { + return "Your browser does not support the <code>audio</code> element."; + } +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VMediaBase.java b/src/com/vaadin/terminal/gwt/client/ui/VMediaBase.java new file mode 100644 index 0000000000..6d6474bbec --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VMediaBase.java @@ -0,0 +1,129 @@ +package com.vaadin.terminal.gwt.client.ui; + +import com.google.gwt.dom.client.Document; +import com.google.gwt.dom.client.MediaElement; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.Util; + +public abstract class VMediaBase extends Widget implements Paintable { + public static final String ATTR_PAUSE = "pause"; + public static final String ATTR_PLAY = "play"; + public static final String ATTR_MUTED = "muted"; + public static final String ATTR_CONTROLS = "ctrl"; + public static final String ATTR_AUTOPLAY = "auto"; + public static final String TAG_SOURCE = "src"; + public static final String ATTR_RESOURCE = "res"; + public static final String ATTR_RESOURCE_TYPE = "type"; + public static final String ATTR_HTML = "html"; + public static final String ATTR_ALT_TEXT = "alt"; + + private MediaElement media; + protected ApplicationConnection client; + + /** + * Sets the MediaElement that is to receive all commands and properties. + * + * @param element + */ + public void setMediaElement(MediaElement element) { + setElement(element); + media = element; + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + if (client.updateComponent(this, uidl, true)) { + return; + } + + this.client = client; + + media.setControls(shouldShowControls(uidl)); + media.setAutoplay(shouldAutoplay(uidl)); + media.setMuted(isMediaMuted(uidl)); + + // Add all sources + for (int ix = 0; ix < uidl.getChildCount(); ix++) { + UIDL child = uidl.getChildUIDL(ix); + if (TAG_SOURCE.equals(child.getTag())) { + Element src = Document.get().createElement("source").cast(); + src.setAttribute("src", getSourceUrl(child)); + src.setAttribute("type", getSourceType(child)); + media.appendChild(src); + } + } + setAltText(uidl); + + evalPauseCommand(uidl); + evalPlayCommand(uidl); + } + + protected boolean shouldShowControls(UIDL uidl) { + return uidl.getBooleanAttribute(ATTR_CONTROLS); + } + + private boolean shouldAutoplay(UIDL uidl) { + return uidl.getBooleanAttribute(ATTR_AUTOPLAY); + } + + private boolean isMediaMuted(UIDL uidl) { + return uidl.getBooleanAttribute(ATTR_MUTED); + } + + /** + * @param uidl + * @return the URL of a resource to be used as a source for the media + */ + private String getSourceUrl(UIDL uidl) { + String url = client.translateVaadinUri(uidl + .getStringAttribute(ATTR_RESOURCE)); + if (url == null) { + return ""; + } + return url; + } + + /** + * @param uidl + * @return the mime type of the media + */ + private String getSourceType(UIDL uidl) { + return uidl.getStringAttribute(ATTR_RESOURCE_TYPE); + } + + private void setAltText(UIDL uidl) { + String alt = uidl.getStringAttribute(ATTR_ALT_TEXT); + + if (alt == null || "".equals(alt)) { + alt = getDefaultAltHtml(); + } else if (!allowHtmlContent(uidl)) { + alt = Util.escapeHTML(alt); + } + media.appendChild(Document.get().createTextNode(alt)); + } + + private boolean allowHtmlContent(UIDL uidl) { + return uidl.getBooleanAttribute(ATTR_HTML); + } + + private void evalPlayCommand(UIDL uidl) { + if (uidl.hasAttribute(ATTR_PLAY)) { + media.play(); + } + } + + private void evalPauseCommand(UIDL uidl) { + if (uidl.hasAttribute(ATTR_PAUSE)) { + media.pause(); + } + } + + /** + * @return the default HTML to show users with browsers that do not support + * HTML5 media markup. + */ + protected abstract String getDefaultAltHtml(); +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VVideo.java b/src/com/vaadin/terminal/gwt/client/ui/VVideo.java new file mode 100644 index 0000000000..61ca04544a --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VVideo.java @@ -0,0 +1,73 @@ +package com.vaadin.terminal.gwt.client.ui; + +import com.google.gwt.dom.client.Document; +import com.google.gwt.dom.client.Style.Unit; +import com.google.gwt.dom.client.VideoElement; +import com.google.gwt.user.client.Element; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.Util; + +public class VVideo extends VMediaBase { + public static final String ATTR_POSTER = "poster"; + + private static String CLASSNAME = "v-video"; + + private VideoElement video; + + public VVideo() { + video = Document.get().createVideoElement(); + setMediaElement(video); + setStyleName(CLASSNAME); + + updateDimensionsWhenMetadataLoaded(getElement()); + } + + @Override + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + if (client.updateComponent(this, uidl, true)) { + return; + } + super.updateFromUIDL(uidl, client); + setPosterFromUIDL(uidl); + } + + private void setPosterFromUIDL(UIDL uidl) { + if (uidl.hasAttribute(ATTR_POSTER)) { + video.setPoster(client.translateVaadinUri(uidl + .getStringAttribute(ATTR_POSTER))); + } + } + + /** + * Registers a listener that updates the dimensions of the widget when the + * video metadata has been loaded. + * + * @param el + */ + private native void updateDimensionsWhenMetadataLoaded(Element el) + /*-{ + var self = this; + el.addEventListener('loadedmetadata', function(e) { + $entry(self.@com.vaadin.terminal.gwt.client.ui.VVideo::updateElementDynamicSize(II)(el.videoWidth, el.videoHeight)); + }, false); + + }-*/; + + /** + * Updates the dimensions of the widget. + * + * @param w + * @param h + */ + private void updateElementDynamicSize(int w, int h) { + video.getStyle().setWidth(w, Unit.PX); + video.getStyle().setHeight(h, Unit.PX); + Util.notifyParentOfSizeChange(this, true); + } + + @Override + protected String getDefaultAltHtml() { + return "Your browser does not support the <code>video</code> element."; + } +} diff --git a/src/com/vaadin/ui/AbstractMedia.java b/src/com/vaadin/ui/AbstractMedia.java new file mode 100644 index 0000000000..e085f04367 --- /dev/null +++ b/src/com/vaadin/ui/AbstractMedia.java @@ -0,0 +1,217 @@ +package com.vaadin.ui; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import com.vaadin.terminal.PaintException; +import com.vaadin.terminal.PaintTarget; +import com.vaadin.terminal.Resource; +import com.vaadin.terminal.gwt.client.ui.VMediaBase; + +/** + * Abstract base class for the HTML5 media components. + * + * @author Vaadin Ltd + */ +public class AbstractMedia extends AbstractComponent { + + private List<Resource> sources = new ArrayList<Resource>(); + + private boolean showControls; + + private String altText; + + private boolean htmlContentAllowed; + + private boolean autoplay; + + private boolean muted; + + private boolean pause; + + private boolean play; + + /** + * Sets a single media file as the source of the media component. + * + * @param source + */ + public void setSource(Resource source) { + sources.clear(); + addSource(source); + } + + /** + * Adds an alternative media file to the sources list. Which of the sources + * is used is selected by the browser depending on which file formats it + * supports. See <a + * href="http://en.wikipedia.org/wiki/HTML5_video#Table">wikipedia</a> for a + * table of formats supported by different browsers. + * + * @param source + */ + public void addSource(Resource source) { + if (source != null) { + sources.add(source); + requestRepaint(); + } + } + + /** + * Set multiple sources at once. Which of the sources is used is selected by + * the browser depending on which file formats it supports. See <a + * href="http://en.wikipedia.org/wiki/HTML5_video#Table">wikipedia</a> for a + * table of formats supported by different browsers. + * + * @param sources + */ + public void setSources(Resource... sources) { + this.sources.addAll(Arrays.asList(sources)); + requestRepaint(); + } + + /** + * @return The sources pointed to in this media. + */ + public List<Resource> getSources() { + return sources; + } + + /** + * Sets whether or not the browser should show native media controls. + * + * @param showControls + */ + public void setShowControls(boolean showControls) { + this.showControls = showControls; + requestRepaint(); + } + + /** + * @return true if the browser is to show native media controls. + */ + public boolean isShowControls() { + return showControls; + } + + /** + * Sets the alternative text to be displayed if the browser does not support + * HTML5. This text is rendered as HTML if + * {@link #setHtmlContentAllowed(boolean)} is set to true. With HTML + * rendering, this method can also be used to implement fallback to a + * flash-based player, see the <a href= + * "https://developer.mozilla.org/En/Using_audio_and_video_in_Firefox#Using_Flash" + * >Mozilla Developer Network</a> for details. + * + * @param text + */ + public void setAltText(String text) { + altText = text; + requestRepaint(); + } + + /** + * @return The text/html that is displayed when a browser doesn't support + * HTML5. + */ + public String getAltText() { + return altText; + } + + /** + * Set whether the alternative text ({@link #setAltText(String)}) is + * rendered as HTML or not. + * + * @param htmlContentAllowed + */ + public void setHtmlContentAllowed(boolean htmlContentAllowed) { + this.htmlContentAllowed = htmlContentAllowed; + requestRepaint(); + } + + /** + * @return true if the alternative text ({@link #setAltText(String)}) is to + * be rendered as HTML. + */ + public boolean isHtmlContentAllowed() { + return htmlContentAllowed; + } + + /** + * Sets whether the media is to automatically start playback when enough + * data has been loaded. + * + * @param autoplay + */ + public void setAutoplay(boolean autoplay) { + this.autoplay = autoplay; + requestRepaint(); + } + + /** + * @return true if the media is set to automatically start playback. + */ + public boolean isAutoplay() { + return autoplay; + } + + /** + * Set whether to mute the audio or not. + * + * @param muted + */ + public void setMuted(boolean muted) { + this.muted = muted; + requestRepaint(); + } + + /** + * @return true if the audio is muted. + */ + public boolean isMuted() { + return muted; + } + + /** + * Pauses the media. + */ + public void pause() { + pause = true; + requestRepaint(); + } + + /** + * Starts playback of the media. + */ + public void play() { + play = true; + requestRepaint(); + } + + @Override + public void paintContent(PaintTarget target) throws PaintException { + super.paintContent(target); + target.addAttribute(VMediaBase.ATTR_CONTROLS, isShowControls()); + if (getAltText() != null) { + target.addAttribute(VMediaBase.ATTR_ALT_TEXT, getAltText()); + } + target.addAttribute(VMediaBase.ATTR_HTML, isHtmlContentAllowed()); + target.addAttribute(VMediaBase.ATTR_AUTOPLAY, isAutoplay()); + for (Resource r : getSources()) { + target.startTag(VMediaBase.TAG_SOURCE); + target.addAttribute(VMediaBase.ATTR_RESOURCE, r); + target.addAttribute(VMediaBase.ATTR_RESOURCE_TYPE, r.getMIMEType()); + target.endTag(VMediaBase.TAG_SOURCE); + } + target.addAttribute(VMediaBase.ATTR_MUTED, isMuted()); + if (play) { + target.addAttribute(VMediaBase.ATTR_PLAY, true); + play = false; + } + if (pause) { + target.addAttribute(VMediaBase.ATTR_PAUSE, true); + pause = false; + } + } +} diff --git a/src/com/vaadin/ui/Audio.java b/src/com/vaadin/ui/Audio.java new file mode 100644 index 0000000000..c3d7cf713e --- /dev/null +++ b/src/com/vaadin/ui/Audio.java @@ -0,0 +1,48 @@ +package com.vaadin.ui; + +import com.vaadin.terminal.Resource; +import com.vaadin.terminal.gwt.client.ui.VAudio; + +/** + * The Audio component translates into an HTML5 <audio> element and as + * such is only supported in browsers that support HTML5 media markup. Browsers + * that do not support HTML5 display the text or HTML set by calling + * {@link #setAltText(String)}. + * + * A flash-player fallback can be implemented by setting HTML content allowed ( + * {@link #setHtmlContentAllowed(boolean)} and calling + * {@link #setAltText(String)} with the flash player markup. An example of flash + * fallback can be found at the <a href= + * "https://developer.mozilla.org/En/Using_audio_and_video_in_Firefox#Using_Flash" + * >Mozilla Developer Network</a>. + * + * @author Vaadin Ltd + * @since 6.7.0 + */ +@ClientWidget(VAudio.class) +public class Audio extends AbstractMedia { + + public Audio() { + this("", null); + } + + /** + * @param caption + * The caption of the audio component. + */ + public Audio(String caption) { + this(caption, null); + } + + /** + * @param caption + * The caption of the audio component + * @param source + * The audio file to play. + */ + public Audio(String caption, Resource source) { + setCaption(caption); + setSource(source); + setShowControls(true); + } +} diff --git a/src/com/vaadin/ui/Video.java b/src/com/vaadin/ui/Video.java new file mode 100644 index 0000000000..efa7e97317 --- /dev/null +++ b/src/com/vaadin/ui/Video.java @@ -0,0 +1,77 @@ +package com.vaadin.ui; + +import com.vaadin.terminal.PaintException; +import com.vaadin.terminal.PaintTarget; +import com.vaadin.terminal.Resource; +import com.vaadin.terminal.gwt.client.ui.VVideo; + +/** + * The Video component translates into an HTML5 <video> element and as + * such is only supported in browsers that support HTML5 media markup. Browsers + * that do not support HTML5 display the text or HTML set by calling + * {@link #setAltText(String)}. + * + * A flash-player fallback can be implemented by setting HTML content allowed ( + * {@link #setHtmlContentAllowed(boolean)} and calling + * {@link #setAltText(String)} with the flash player markup. An example of flash + * fallback can be found at the <a href= + * "https://developer.mozilla.org/En/Using_audio_and_video_in_Firefox#Using_Flash" + * >Mozilla Developer Network</a>. + * + * @author Vaadin Ltd + * @since 6.7.0 + */ +@ClientWidget(VVideo.class) +public class Video extends AbstractMedia { + + private Resource poster; + + public Video() { + this("", null); + } + + /** + * @param caption + * The caption for this video. + */ + public Video(String caption) { + this(caption, null); + } + + /** + * @param caption + * The caption for this video. + * @param source + * The Resource containing the video to play. + */ + public Video(String caption, Resource source) { + setCaption(caption); + setSource(source); + setShowControls(true); + } + + /** + * Sets the poster image, which is shown in place of the video before the + * user presses play. + * + * @param poster + */ + public void setPoster(Resource poster) { + this.poster = poster; + } + + /** + * @return The poster image. + */ + public Resource getPoster() { + return poster; + } + + @Override + public void paintContent(PaintTarget target) throws PaintException { + super.paintContent(target); + if (getPoster() != null) { + target.addAttribute(VVideo.ATTR_POSTER, getPoster()); + } + } +} diff --git a/tests/src/com/vaadin/tests/components/media/Media.java b/tests/src/com/vaadin/tests/components/media/Media.java new file mode 100644 index 0000000000..ff25810042 --- /dev/null +++ b/tests/src/com/vaadin/tests/components/media/Media.java @@ -0,0 +1,74 @@ +package com.vaadin.tests.components.media; + +import com.vaadin.terminal.ExternalResource; +import com.vaadin.tests.components.TestBase; +import com.vaadin.ui.Audio; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Button.ClickListener; +import com.vaadin.ui.Video; + +public class Media extends TestBase { + + @Override + protected void setup() { + final Video v = new Video("video"); + v.setSources( + new ExternalResource( + "http://jonatan.virtuallypreinstalled.com/media/big_buck_bunny.mp4"), + new ExternalResource( + "http://jonatan.virtuallypreinstalled.com/media/big_buck_bunny.ogv")); + v.setWidth("640px"); + v.setHeight("360px"); + addComponent(v); + addComponent(new Button("Play video", new ClickListener() { + + public void buttonClick(ClickEvent event) { + v.play(); + } + + })); + addComponent(new Button("Pause video", new ClickListener() { + + public void buttonClick(ClickEvent event) { + v.pause(); + } + + })); + + final Audio a = new Audio("audio"); + a.setSources( + new ExternalResource( + "http://jonatan.virtuallypreinstalled.com/media/audio.mp3"), + new ExternalResource( + "http://jonatan.virtuallypreinstalled.com/media/audio.ogg")); + addComponent(a); + + addComponent(new Button("Play audio", new ClickListener() { + + public void buttonClick(ClickEvent event) { + a.play(); + } + + })); + addComponent(new Button("Pause audio", new ClickListener() { + + public void buttonClick(ClickEvent event) { + a.pause(); + } + + })); + } + + @Override + protected String getDescription() { + return "Video and audio files should play using the HTML5 elements. " + + "(Movie is (c) copyright 2008, Blender Foundation / www.bigbuckbunny.org)"; + } + + @Override + protected Integer getTicketNumber() { + return 6909; + } + +} |