/* * Copyright 2000-2016 Vaadin Ltd. * * 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 com.vaadin.ui; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jsoup.nodes.Attributes; import org.jsoup.nodes.Element; import org.jsoup.nodes.Node; import com.vaadin.server.ConnectorResource; import com.vaadin.server.DownloadStream; import com.vaadin.server.Resource; import com.vaadin.server.ResourceReference; import com.vaadin.server.VaadinRequest; import com.vaadin.server.VaadinResponse; import com.vaadin.server.VaadinSession; import com.vaadin.shared.communication.URLReference; import com.vaadin.shared.ui.AbstractMediaState; import com.vaadin.shared.ui.MediaControl; import com.vaadin.ui.declarative.DesignAttributeHandler; import com.vaadin.ui.declarative.DesignContext; /** * Abstract base class for the HTML5 media components. * * @author Vaadin Ltd */ public abstract class AbstractMedia extends AbstractComponent { @Override protected AbstractMediaState getState() { return (AbstractMediaState) super.getState(); } @Override protected AbstractMediaState getState(boolean markAsDirty) { return (AbstractMediaState) super.getState(markAsDirty); } /** * Sets a single media file as the source of the media component. * * @param source */ public void setSource(Resource source) { clearSources(); addSource(source); } private void clearSources() { getState().sources.clear(); getState().sourceTypes.clear(); } /** * 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) { List<URLReference> sources = getState().sources; sources.add(new ResourceReference(source, this, Integer.toString(sources.size()))); getState().sourceTypes.add(source.getMIMEType()); } } @Override public boolean handleConnectorRequest(VaadinRequest request, VaadinResponse response, String path) throws IOException { Matcher matcher = Pattern.compile("(\\d+)(/.*)?").matcher(path); if (!matcher.matches()) { return super.handleConnectorRequest(request, response, path); } DownloadStream stream; VaadinSession session = getSession(); session.lock(); try { List<URLReference> sources = getState().sources; int sourceIndex = Integer.parseInt(matcher.group(1)); if (sourceIndex < 0 || sourceIndex >= sources.size()) { getLogger().log(Level.WARNING, "Requested source index {0} is out of bounds", sourceIndex); return false; } URLReference reference = sources.get(sourceIndex); ConnectorResource resource = (ConnectorResource) ResourceReference .getResource(reference); stream = resource.getStream(); } finally { session.unlock(); } stream.writeResponse(request, response); return true; } private Logger getLogger() { return Logger.getLogger(AbstractMedia.class.getName()); } /** * 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) { clearSources(); for (Resource source : sources) { addSource(source); } } /** * @return The sources pointed to in this media. */ public List<Resource> getSources() { ArrayList<Resource> sources = new ArrayList<>(); for (URLReference ref : getState(false).sources) { sources.add(((ResourceReference) ref).getResource()); } return sources; } /** * Sets whether or not the browser should show native media controls. * * @param showControls */ public void setShowControls(boolean showControls) { getState().showControls = showControls; } /** * @return true if the browser is to show native media controls. */ public boolean isShowControls() { return getState(false).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 altText */ public void setAltText(String altText) { getState().altText = altText; } /** * @return The text/html that is displayed when a browser doesn't support * HTML5. */ public String getAltText() { return getState(false).altText; } /** * Set whether the alternative text ({@link #setAltText(String)}) is * rendered as HTML or not. * * @param htmlContentAllowed */ public void setHtmlContentAllowed(boolean htmlContentAllowed) { getState().htmlContentAllowed = htmlContentAllowed; } /** * @return true if the alternative text ({@link #setAltText(String)}) is to * be rendered as HTML. */ public boolean isHtmlContentAllowed() { return getState(false).htmlContentAllowed; } /** * Sets whether the media is to automatically start playback when enough * data has been loaded. * * @param autoplay */ public void setAutoplay(boolean autoplay) { getState().autoplay = autoplay; } /** * @return true if the media is set to automatically start playback. */ public boolean isAutoplay() { return getState(false).autoplay; } /** * Set whether to mute the audio or not. * * @param muted */ public void setMuted(boolean muted) { getState().muted = muted; } /** * @return true if the audio is muted. */ public boolean isMuted() { return getState(false).muted; } /** * Pauses the media. */ public void pause() { getRpcProxy(MediaControl.class).pause(); } /** * Starts playback of the media. */ public void play() { getRpcProxy(MediaControl.class).play(); } @Override public void writeDesign(Element design, DesignContext designContext) { super.writeDesign(design, designContext); String altText = getAltText(); if (altText != null && !altText.isEmpty()) { design.append(altText); } for (Resource r : getSources()) { Attributes attr = design.appendElement("source").attributes(); DesignAttributeHandler.writeAttribute("href", attr, r, null, Resource.class, designContext); } } @Override public void readDesign(Element design, DesignContext designContext) { super.readDesign(design, designContext); String altText = ""; for (Node child : design.childNodes()) { if (child instanceof Element && ((Element) child).tagName().equals("source") && child.hasAttr("href")) { addSource(DesignAttributeHandler.readAttribute("href", child.attributes(), Resource.class)); } else { altText += child.toString(); } } altText = altText.trim(); if (!altText.isEmpty()) { setAltText(altText); } } @Override protected Collection<String> getCustomAttributes() { Collection<String> result = super.getCustomAttributes(); result.add("alt-text"); return result; } }