diff --git a/build.gradle b/build.gradle index 2bf8a8a953..0ec29add45 100644 --- a/build.gradle +++ b/build.gradle @@ -19,6 +19,9 @@ repositories { maven { url "https://raw.github.com/whispersystems/maven/master/gson/releases/" } + maven { + url "https://raw.github.com/whispersystems/maven/master/smil/releases/" + } } dependencies { @@ -27,6 +30,7 @@ dependencies { compile 'se.emilsjolander:stickylistheaders:2.2.0' compile 'com.google.android.gms:play-services:5.0.89' compile 'com.astuetz:pagerslidingtabstrip:1.0.1' + compile 'org.w3c:smil:1.0.0' androidTestCompile 'com.squareup:fest-android:1.0.8' @@ -40,6 +44,7 @@ dependencyVerification { 'se.emilsjolander:stickylistheaders:89146b46c96fea0e40200474a2625cda10fe94891e4128f53cdb42375091b9b6', 'com.google.android.gms:play-services:38f326e525830f1d70f60f594ceafcbdf5b312287ddbecd338fd1ed7958a4b1e', 'com.astuetz:pagerslidingtabstrip:f1641396732c7132a7abb837e482e5ee2b0ebb8d10813fc52bbaec2c15c184c2', + 'org.w3c:smil:085dc40f2bb249651578bfa07499fd08b16ad0886dbe2c4078586a408da62f9b', 'com.google.protobuf:protobuf-java:ad9769a22989e688a46af4d3accc348cc501ced22118033230542bc916e33f0b', 'com.madgag:sc-light-jdk15on:931f39d351429fb96c2f749e7ecb1a256a8ebbf5edca7995c9cc085b94d1841d', 'com.googlecode.libphonenumber:libphonenumber:eba17eae81dd622ea89a00a3a8c025b2f25d342e0d9644c5b62e16f15687c3ab', diff --git a/src/org/thoughtcrime/securesms/dom/AttrImpl.java b/src/org/thoughtcrime/securesms/dom/AttrImpl.java new file mode 100644 index 0000000000..e13e9e25bf --- /dev/null +++ b/src/org/thoughtcrime/securesms/dom/AttrImpl.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * 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.thoughtcrime.securesms.dom; + +import org.w3c.dom.Attr; +import org.w3c.dom.DOMException; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.TypeInfo; + +public class AttrImpl extends NodeImpl implements Attr { + private String mName; + private String mValue; + + /* + * Internal methods + */ + + protected AttrImpl(DocumentImpl owner, String name) { + super(owner); + mName = name; + } + + /* + * Attr Interface Methods + */ + + public String getName() { + return mName; + } + + public Element getOwnerElement() { + // TODO Auto-generated method stub + return null; + } + + public boolean getSpecified() { + return mValue != null; + } + + public String getValue() { + return mValue; + } + + // Instead of setting a Text> with the content of the + // String value as defined in the specs, we directly set here the + // internal mValue member. + public void setValue(String value) throws DOMException { + mValue = value; + } + + /* + * Node Interface Methods + */ + + @Override + public String getNodeName() { + return mName; + } + + @Override + public short getNodeType() { + return Node.ATTRIBUTE_NODE; + } + + @Override + public Node getParentNode() { + return null; + } + + @Override + public Node getPreviousSibling() { + return null; + } + + @Override + public Node getNextSibling() { + return null; + } + + @Override + public void setNodeValue(String nodeValue) throws DOMException { + setValue(nodeValue); + } + + public TypeInfo getSchemaTypeInfo() { + return null; + } + + public boolean isId() { + return false; + } +} diff --git a/src/org/thoughtcrime/securesms/dom/DocumentImpl.java b/src/org/thoughtcrime/securesms/dom/DocumentImpl.java new file mode 100644 index 0000000000..08816627a7 --- /dev/null +++ b/src/org/thoughtcrime/securesms/dom/DocumentImpl.java @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * 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.thoughtcrime.securesms.dom; + +import org.w3c.dom.Attr; +import org.w3c.dom.CDATASection; +import org.w3c.dom.Comment; +import org.w3c.dom.DOMConfiguration; +import org.w3c.dom.DOMException; +import org.w3c.dom.DOMImplementation; +import org.w3c.dom.Document; +import org.w3c.dom.DocumentFragment; +import org.w3c.dom.DocumentType; +import org.w3c.dom.Element; +import org.w3c.dom.EntityReference; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.ProcessingInstruction; +import org.w3c.dom.Text; + +public abstract class DocumentImpl extends NodeImpl implements Document { + + /* + * Internal methods + */ + + public DocumentImpl() { + super(null); + } + + /* + * Document Interface Methods + */ + + public Attr createAttribute(String name) throws DOMException { + return new AttrImpl(this, name); + } + + public Attr createAttributeNS(String namespaceURI, String qualifiedName) + throws DOMException { + // TODO Auto-generated method stub + return null; + } + + public CDATASection createCDATASection(String data) throws DOMException { + // TODO Auto-generated method stub + return null; + } + + public Comment createComment(String data) { + // TODO Auto-generated method stub + return null; + } + + public DocumentFragment createDocumentFragment() { + // TODO Auto-generated method stub + return null; + } + + public abstract Element createElement(String tagName) throws DOMException; + + public Element createElementNS(String namespaceURI, String qualifiedName) + throws DOMException { + // TODO Auto-generated method stub + return null; + } + + public EntityReference createEntityReference(String name) throws DOMException { + // TODO Auto-generated method stub + return null; + } + + public ProcessingInstruction createProcessingInstruction(String target, String data) + throws DOMException { + // TODO Auto-generated method stub + return null; + } + + public Text createTextNode(String data) { + // TODO Auto-generated method stub + return null; + } + + public DocumentType getDoctype() { + // TODO Auto-generated method stub + return null; + } + + public abstract Element getDocumentElement(); + + public Element getElementById(String elementId) { + // TODO Auto-generated method stub + return null; + } + + public NodeList getElementsByTagName(String tagname) { + // TODO Auto-generated method stub + return null; + } + + public NodeList getElementsByTagNameNS(String namespaceURI, String localName) { + // TODO Auto-generated method stub + return null; + } + + public DOMImplementation getImplementation() { + // TODO Auto-generated method stub + return null; + } + + public Node importNode(Node importedNode, boolean deep) throws DOMException { + // TODO Auto-generated method stub + return null; + } + + /* + * Node Interface methods + */ + + @Override + public short getNodeType() { + return Node.DOCUMENT_NODE; + } + + @Override + public String getNodeName() { + // The value of nodeName is "#document" when Node is a Document + return "#document"; + } + + public String getInputEncoding() { + return null; + } + + public String getXmlEncoding() { + return null; + } + + public boolean getXmlStandalone() { + return false; + } + + public void setXmlStandalone(boolean xmlStandalone) throws DOMException {} + + public String getXmlVersion() { + return null; + } + + public void setXmlVersion(String xmlVersion) throws DOMException {} + + public boolean getStrictErrorChecking() { + return true; + } + + public void setStrictErrorChecking(boolean strictErrorChecking) {} + + public String getDocumentURI() { + return null; + } + + public void setDocumentURI(String documentURI) {} + + public Node adoptNode(Node source) throws DOMException { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, null); + } + + public DOMConfiguration getDomConfig() { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, null); + } + + public void normalizeDocument() { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, null); + } + + public Node renameNode(Node n, String namespaceURI, String qualifiedName) + throws DOMException { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, null); + } +} diff --git a/src/org/thoughtcrime/securesms/dom/ElementImpl.java b/src/org/thoughtcrime/securesms/dom/ElementImpl.java new file mode 100644 index 0000000000..583858f4ee --- /dev/null +++ b/src/org/thoughtcrime/securesms/dom/ElementImpl.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * 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.thoughtcrime.securesms.dom; + +import org.w3c.dom.Attr; +import org.w3c.dom.DOMException; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.NodeList; +import org.w3c.dom.TypeInfo; + +public class ElementImpl extends NodeImpl implements Element { + private String mTagName; + private NamedNodeMap mAttributes = new NamedNodeMapImpl(); + + /* + * Internal methods + */ + + protected ElementImpl(DocumentImpl owner, String tagName) { + super(owner); + mTagName = tagName; + } + + /* + * Element Interface methods + */ + + public String getAttribute(String name) { + Attr attrNode = getAttributeNode(name); + String attrValue = ""; + if (attrNode != null) { + attrValue = attrNode.getValue(); + } + return attrValue; + } + + public String getAttributeNS(String namespaceURI, String localName) { + // TODO Auto-generated method stub + return null; + } + + public Attr getAttributeNode(String name) { + return (Attr)mAttributes.getNamedItem(name); + } + + public Attr getAttributeNodeNS(String namespaceURI, String localName) { + // TODO Auto-generated method stub + return null; + } + + public NodeList getElementsByTagName(String name) { + return new NodeListImpl(this, name, true); + } + + public NodeList getElementsByTagNameNS(String namespaceURI, String localName) { + // TODO Auto-generated method stub + return null; + } + + public String getTagName() { + return mTagName; + } + + public boolean hasAttribute(String name) { + return (getAttributeNode(name) != null); + } + + public boolean hasAttributeNS(String namespaceURI, String localName) { + // TODO Auto-generated method stub + return false; + } + + public void removeAttribute(String name) throws DOMException { + // TODO Auto-generated method stub + + } + + public void removeAttributeNS(String namespaceURI, String localName) + throws DOMException { + // TODO Auto-generated method stub + + } + + public Attr removeAttributeNode(Attr oldAttr) throws DOMException { + // TODO Auto-generated method stub + return null; + } + + public void setAttribute(String name, String value) throws DOMException { + Attr attribute = getAttributeNode(name); + if (attribute == null) { + attribute = mOwnerDocument.createAttribute(name); + } + attribute.setNodeValue(value); + mAttributes.setNamedItem(attribute); + } + + public void setAttributeNS(String namespaceURI, String qualifiedName, + String value) throws DOMException { + // TODO Auto-generated method stub + + } + + public Attr setAttributeNode(Attr newAttr) throws DOMException { + // TODO Auto-generated method stub + return null; + } + + public Attr setAttributeNodeNS(Attr newAttr) throws DOMException { + // TODO Auto-generated method stub + return null; + } + + /* + * Node Interface methods + */ + + @Override + public short getNodeType() { + return ELEMENT_NODE; + } + + @Override + public String getNodeName() { + // The value of nodeName is tagName when Node is an Element + return mTagName; + } + + @Override + public NamedNodeMap getAttributes() { + return mAttributes; + } + + @Override + public boolean hasAttributes() { + return (mAttributes.getLength() > 0); + } + + public TypeInfo getSchemaTypeInfo() { + return null; + } + + public void setIdAttribute(String name, boolean isId) throws DOMException { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, null); + } + + public void setIdAttributeNS(String namespaceURI, String localName, + boolean isId) throws DOMException { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, null); + } + + public void setIdAttributeNode(Attr idAttr, boolean isId) + throws DOMException { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, null); + } +} diff --git a/src/org/thoughtcrime/securesms/dom/NamedNodeMapImpl.java b/src/org/thoughtcrime/securesms/dom/NamedNodeMapImpl.java new file mode 100644 index 0000000000..9eff2dfe74 --- /dev/null +++ b/src/org/thoughtcrime/securesms/dom/NamedNodeMapImpl.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * 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.thoughtcrime.securesms.dom; + +import java.util.Vector; + +import org.w3c.dom.DOMException; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + +public class NamedNodeMapImpl implements NamedNodeMap { + + private Vector mNodes = new Vector(); + + public int getLength() { + return mNodes.size(); + } + + public Node getNamedItem(String name) { + Node node = null; + for (int i = 0; i < mNodes.size(); i++) { + if (name.equals(mNodes.elementAt(i).getNodeName())) { + node = mNodes.elementAt(i); + break; + } + } + return node; + } + + public Node getNamedItemNS(String namespaceURI, String localName) { + // TODO Auto-generated method stub + return null; + } + + public Node item(int index) { + if (index < mNodes.size()) { + return mNodes.elementAt(index); + } + return null; + } + + public Node removeNamedItem(String name) throws DOMException { + Node node = getNamedItem(name); + if (node == null) { + throw new DOMException(DOMException.NOT_FOUND_ERR, "Not found"); + } else { + mNodes.remove(node); + } + return node; + } + + public Node removeNamedItemNS(String namespaceURI, String localName) + throws DOMException { + // TODO Auto-generated method stub + return null; + } + + public Node setNamedItem(Node arg) throws DOMException { + Node existing = getNamedItem(arg.getNodeName()); + if (existing != null) { + mNodes.remove(existing); + } + mNodes.add(arg); + return existing; + } + + public Node setNamedItemNS(Node arg) throws DOMException { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/src/org/thoughtcrime/securesms/dom/NodeImpl.java b/src/org/thoughtcrime/securesms/dom/NodeImpl.java new file mode 100644 index 0000000000..1b38e63105 --- /dev/null +++ b/src/org/thoughtcrime/securesms/dom/NodeImpl.java @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * 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.thoughtcrime.securesms.dom; + +import java.util.NoSuchElementException; +import java.util.Vector; + +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.UserDataHandler; +import org.w3c.dom.events.Event; +import org.w3c.dom.events.EventException; +import org.w3c.dom.events.EventListener; +import org.w3c.dom.events.EventTarget; + +import org.thoughtcrime.securesms.dom.events.EventTargetImpl; + +public abstract class NodeImpl implements Node, EventTarget { + private Node mParentNode; + private final Vector mChildNodes = new Vector(); + DocumentImpl mOwnerDocument; + private final EventTarget mEventTarget = new EventTargetImpl(this); + + /* + * Internal methods + */ + + protected NodeImpl(DocumentImpl owner) { + mOwnerDocument = owner; + } + + /* + * Node Interface Methods + */ + + public Node appendChild(Node newChild) throws DOMException { + ((NodeImpl)newChild).setParentNode(this); + mChildNodes.remove(newChild); + mChildNodes.add(newChild); + return newChild; + } + + public Node cloneNode(boolean deep) { + // TODO Auto-generated method stub + return null; + } + + public NamedNodeMap getAttributes() { + // Default. Override in Element. + return null; + } + + public NodeList getChildNodes() { + return new NodeListImpl(this, null, false); + } + + public Node getFirstChild() { + Node firstChild = null; + try { + firstChild = mChildNodes.firstElement(); + } + catch (NoSuchElementException e) { + // Ignore and return null + } + return firstChild; + } + + public Node getLastChild() { + Node lastChild = null; + try { + lastChild = mChildNodes.lastElement(); + } + catch (NoSuchElementException e) { + // Ignore and return null + } + return lastChild; + } + + public String getLocalName() { + // TODO Auto-generated method stub + return null; + } + + public String getNamespaceURI() { + // TODO Auto-generated method stub + return null; + } + + public Node getNextSibling() { + if ((mParentNode != null) && (this != mParentNode.getLastChild())) { + Vector siblings = ((NodeImpl)mParentNode).mChildNodes; + int indexOfThis = siblings.indexOf(this); + return siblings.elementAt(indexOfThis + 1); + } + return null; + } + + public abstract String getNodeName(); + + public abstract short getNodeType(); + + public String getNodeValue() throws DOMException { + // Default behaviour. Override if required. + return null; + } + + public Document getOwnerDocument() { + return mOwnerDocument; + } + + public Node getParentNode() { + return mParentNode; + } + + public String getPrefix() { + // TODO Auto-generated method stub + return null; + } + + public Node getPreviousSibling() { + if ((mParentNode != null) && (this != mParentNode.getFirstChild())) { + Vector siblings = ((NodeImpl)mParentNode).mChildNodes; + int indexOfThis = siblings.indexOf(this); + return siblings.elementAt(indexOfThis - 1); + } + return null; + } + + public boolean hasAttributes() { + // Default. Override in Element. + return false; + } + + public boolean hasChildNodes() { + return !(mChildNodes.isEmpty()); + } + + public Node insertBefore(Node newChild, Node refChild) throws DOMException { + // TODO Auto-generated method stub + return null; + } + + public boolean isSupported(String feature, String version) { + // TODO Auto-generated method stub + return false; + } + + public void normalize() { + // TODO Auto-generated method stub + } + + public Node removeChild(Node oldChild) throws DOMException { + if (mChildNodes.contains(oldChild)) { + mChildNodes.remove(oldChild); + ((NodeImpl)oldChild).setParentNode(null); + } else { + throw new DOMException(DOMException.NOT_FOUND_ERR, "Child does not exist"); + } + return null; + } + + public Node replaceChild(Node newChild, Node oldChild) throws DOMException { + if (mChildNodes.contains(oldChild)) { + // Try to remove the new child if available + try { + mChildNodes.remove(newChild); + } catch (DOMException e) { + // Ignore exception + } + mChildNodes.setElementAt(newChild, mChildNodes.indexOf(oldChild)); + ((NodeImpl)newChild).setParentNode(this); + ((NodeImpl)oldChild).setParentNode(null); + } else { + throw new DOMException(DOMException.NOT_FOUND_ERR, "Old child does not exist"); + } + return oldChild; + } + + public void setNodeValue(String nodeValue) throws DOMException { + // Default behaviour. Override if required. + } + + public void setPrefix(String prefix) throws DOMException { + // TODO Auto-generated method stub + } + + private void setParentNode(Node parentNode) { + mParentNode = parentNode; + } + + /* + * EventTarget Interface + */ + + public void addEventListener(String type, EventListener listener, boolean useCapture) { + mEventTarget.addEventListener(type, listener, useCapture); + } + + public void removeEventListener(String type, EventListener listener, boolean useCapture) { + mEventTarget.removeEventListener(type, listener, useCapture); + } + + public boolean dispatchEvent(Event evt) throws EventException { + return mEventTarget.dispatchEvent(evt); + } + + public String getBaseURI() { + return null; + } + + public short compareDocumentPosition(Node other) throws DOMException { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, null); + } + + public String getTextContent() throws DOMException { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, null); + } + + public void setTextContent(String textContent) throws DOMException { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, null); + } + + public boolean isSameNode(Node other) { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, null); + } + + public String lookupPrefix(String namespaceURI) { + return null; + } + + public boolean isDefaultNamespace(String namespaceURI) { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, null); + } + + public String lookupNamespaceURI(String prefix) { + return null; + } + + public boolean isEqualNode(Node arg) { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, null); + } + + public Object getFeature(String feature, String version) { + return null; + } + + public Object setUserData(String key, Object data, + UserDataHandler handler) { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, null); + } + + public Object getUserData(String key) { + return null; + } +} diff --git a/src/org/thoughtcrime/securesms/dom/NodeListImpl.java b/src/org/thoughtcrime/securesms/dom/NodeListImpl.java new file mode 100644 index 0000000000..9ac9161587 --- /dev/null +++ b/src/org/thoughtcrime/securesms/dom/NodeListImpl.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * 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.thoughtcrime.securesms.dom; + +import java.util.ArrayList; + +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +public class NodeListImpl implements NodeList { + private ArrayList mSearchNodes; + private ArrayList mStaticNodes; + private Node mRootNode; + private String mTagName; + private boolean mDeepSearch; + + /* + * Internal Interface + */ + + /** + * Constructs a NodeList by searching for all descendants or the direct + * children of a root node with a given tag name. + * @param rootNode The root Node of the search. + * @param tagName The tag name to be searched for. If null, all descendants + * will be returned. + * @param deep Limit the search to the direct children of rootNode if false, + * to all descendants otherwise. + */ + public NodeListImpl(Node rootNode, String tagName, boolean deepSearch) { + mRootNode = rootNode; + mTagName = tagName; + mDeepSearch = deepSearch; + } + + /** + * Constructs a NodeList for a given static node list. + * @param nodes The static node list. + */ + public NodeListImpl(ArrayList nodes) { + mStaticNodes = nodes; + } + + /* + * NodeListImpl Interface + */ + + public int getLength() { + if (mStaticNodes == null) { + fillList(mRootNode); + return mSearchNodes.size(); + } else { + return mStaticNodes.size(); + } + } + + public Node item(int index) { + Node node = null; + if (mStaticNodes == null) { + fillList(mRootNode); + try { + node = mSearchNodes.get(index); + } catch (IndexOutOfBoundsException e) { + // Do nothing and return null + } + } else { + try { + node = mStaticNodes.get(index); + } catch (IndexOutOfBoundsException e) { + // Do nothing and return null + } + } + return node; + } + + /** + * A preorder traversal is done in the following order: + * + * Visit root. + * Traverse children from left to right in preorder. + * + * This method fills the live node list. + * @param The root of preorder traversal + * @return The next match + */ + private void fillList(Node node) { + // (Re)-initialize the container if this is the start of the search. + // Visit the root of this iteration otherwise. + if (node == mRootNode) { + mSearchNodes = new ArrayList(); + } else { + if ((mTagName == null) || node.getNodeName().equals(mTagName)) { + mSearchNodes.add(node); + } + } + + // Descend one generation... + node = node.getFirstChild(); + + // ...and visit in preorder the children if we are in deep search + // or directly add the children to the list otherwise. + while (node != null) { + if (mDeepSearch) { + fillList(node); + } else { + if ((mTagName == null) || node.getNodeName().equals(mTagName)) { + mSearchNodes.add(node); + } + } + node = node.getNextSibling(); + } + } +} diff --git a/src/org/thoughtcrime/securesms/dom/events/EventImpl.java b/src/org/thoughtcrime/securesms/dom/events/EventImpl.java new file mode 100644 index 0000000000..24c3ef6282 --- /dev/null +++ b/src/org/thoughtcrime/securesms/dom/events/EventImpl.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * 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.thoughtcrime.securesms.dom.events; + +import org.w3c.dom.events.Event; +import org.w3c.dom.events.EventTarget; + +public class EventImpl implements Event { + + // Event type informations + private String mEventType; + private boolean mCanBubble; + private boolean mCancelable; + + // Flags whether the event type information was set + // FIXME: Can we use mEventType for this purpose? + private boolean mInitialized; + + // Target of this event + private EventTarget mTarget; + + // Event status variables + private short mEventPhase; + private boolean mStopPropagation; + private boolean mPreventDefault; + private EventTarget mCurrentTarget; + private int mSeekTo; + + private final long mTimeStamp = System.currentTimeMillis(); + + public boolean getBubbles() { + return mCanBubble; + } + + public boolean getCancelable() { + return mCancelable; + } + + public EventTarget getCurrentTarget() { + return mCurrentTarget; + } + + public short getEventPhase() { + return mEventPhase; + } + + public EventTarget getTarget() { + return mTarget; + } + + public long getTimeStamp() { + return mTimeStamp; + } + + public String getType() { + return mEventType; + } + + public void initEvent(String eventTypeArg, boolean canBubbleArg, + boolean cancelableArg) { + mEventType = eventTypeArg; + mCanBubble = canBubbleArg; + mCancelable = cancelableArg; + mInitialized = true; + } + + public void initEvent(String eventTypeArg, boolean canBubbleArg, boolean cancelableArg, + int seekTo) { + mSeekTo = seekTo; + initEvent(eventTypeArg, canBubbleArg, cancelableArg); + } + + public void preventDefault() { + mPreventDefault = true; + } + + public void stopPropagation() { + mStopPropagation = true; + } + + /* + * Internal Interface + */ + + boolean isInitialized() { + return mInitialized; + } + + boolean isPreventDefault() { + return mPreventDefault; + } + + boolean isPropogationStopped() { + return mStopPropagation; + } + + void setTarget(EventTarget target) { + mTarget = target; + } + + void setEventPhase(short eventPhase) { + mEventPhase = eventPhase; + } + + void setCurrentTarget(EventTarget currentTarget) { + mCurrentTarget = currentTarget; + } + + public int getSeekTo() { + return mSeekTo; + } +} diff --git a/src/org/thoughtcrime/securesms/dom/events/EventTargetImpl.java b/src/org/thoughtcrime/securesms/dom/events/EventTargetImpl.java new file mode 100644 index 0000000000..b629b6e9ca --- /dev/null +++ b/src/org/thoughtcrime/securesms/dom/events/EventTargetImpl.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * 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.thoughtcrime.securesms.dom.events; + +import java.util.ArrayList; + +import org.w3c.dom.events.Event; +import org.w3c.dom.events.EventException; +import org.w3c.dom.events.EventListener; +import org.w3c.dom.events.EventTarget; + +import android.util.Log; + +public class EventTargetImpl implements EventTarget { + private static final String TAG = "EventTargetImpl"; + private ArrayList mListenerEntries; + private EventTarget mNodeTarget; + + static class EventListenerEntry + { + final String mType; + final EventListener mListener; + final boolean mUseCapture; + + EventListenerEntry(String type, EventListener listener, boolean useCapture) + { + mType = type; + mListener = listener; + mUseCapture = useCapture; + } + } + + public EventTargetImpl(EventTarget target) { + mNodeTarget = target; + } + + public void addEventListener(String type, EventListener listener, boolean useCapture) { + if ((type == null) || type.equals("") || (listener == null)) { + return; + } + + // Make sure we have only one entry + removeEventListener(type, listener, useCapture); + + if (mListenerEntries == null) { + mListenerEntries = new ArrayList(); + } + mListenerEntries.add(new EventListenerEntry(type, listener, useCapture)); + } + + public boolean dispatchEvent(Event evt) throws EventException { + // We need to use the internal APIs to modify and access the event status + EventImpl eventImpl = (EventImpl)evt; + + if (!eventImpl.isInitialized()) { + throw new EventException(EventException.UNSPECIFIED_EVENT_TYPE_ERR, + "Event not initialized"); + } else if ((eventImpl.getType() == null) || eventImpl.getType().equals("")) { + throw new EventException(EventException.UNSPECIFIED_EVENT_TYPE_ERR, + "Unspecified even type"); + } + + // Initialize event status + eventImpl.setTarget(mNodeTarget); + + // TODO: At this point, to support event capturing and bubbling, we should + // establish the chain of EventTargets from the top of the tree to this + // event's target. + + // TODO: CAPTURING_PHASE skipped + + // Handle AT_TARGET + // Invoke handleEvent of non-capturing listeners on this EventTarget. + eventImpl.setEventPhase(Event.AT_TARGET); + eventImpl.setCurrentTarget(mNodeTarget); + if (!eventImpl.isPropogationStopped() && (mListenerEntries != null)) { + for (int i = 0; i < mListenerEntries.size(); i++) { + EventListenerEntry listenerEntry = mListenerEntries.get(i); + if (!listenerEntry.mUseCapture + && listenerEntry.mType.equals(eventImpl.getType())) { + try { + listenerEntry.mListener.handleEvent(eventImpl); + } + catch (Exception e) { + // Any exceptions thrown inside an EventListener will + // not stop propagation of the event + Log.w(TAG, "Catched EventListener exception", e); + } + } + } + } + + if (eventImpl.getBubbles()) { + // TODO: BUBBLING_PHASE skipped + } + + return eventImpl.isPreventDefault(); + } + + public void removeEventListener(String type, EventListener listener, + boolean useCapture) { + if (null == mListenerEntries) { + return; + } + for (int i = 0; i < mListenerEntries.size(); i ++) { + EventListenerEntry listenerEntry = mListenerEntries.get(i); + if ((listenerEntry.mUseCapture == useCapture) + && (listenerEntry.mListener == listener) + && listenerEntry.mType.equals(type)) { + mListenerEntries.remove(i); + break; + } + } + } + +} diff --git a/src/org/thoughtcrime/securesms/dom/smil/ElementParallelTimeContainerImpl.java b/src/org/thoughtcrime/securesms/dom/smil/ElementParallelTimeContainerImpl.java new file mode 100644 index 0000000000..7a1d15d965 --- /dev/null +++ b/src/org/thoughtcrime/securesms/dom/smil/ElementParallelTimeContainerImpl.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2007-2008 Esmertec AG. + * Copyright (C) 2007-2008 The Android Open Source Project + * + * 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.thoughtcrime.securesms.dom.smil; + +import java.util.ArrayList; + +import org.w3c.dom.DOMException; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.smil.ElementParallelTimeContainer; +import org.w3c.dom.smil.ElementTime; +import org.w3c.dom.smil.SMILElement; +import org.w3c.dom.smil.Time; +import org.w3c.dom.smil.TimeList; + +import org.thoughtcrime.securesms.dom.NodeListImpl; + +public abstract class ElementParallelTimeContainerImpl extends ElementTimeContainerImpl + implements ElementParallelTimeContainer { + private final static String ENDSYNC_ATTRIBUTE_NAME = "endsync"; + private final static String ENDSYNC_FIRST = "first"; + private final static String ENDSYNC_LAST = "last"; + private final static String ENDSYNC_ALL = "all"; + private final static String ENDSYNC_MEDIA = "media"; + + /* + * Internal Interface + */ + + ElementParallelTimeContainerImpl(SMILElement element) { + super(element); + } + + public String getEndSync() { + String endsync = mSmilElement.getAttribute(ENDSYNC_ATTRIBUTE_NAME); + if ((endsync == null) || (endsync.length() == 0)) { + setEndSync(ENDSYNC_LAST); + return ENDSYNC_LAST; + } + if (ENDSYNC_FIRST.equals(endsync) || ENDSYNC_LAST.equals(endsync) || + ENDSYNC_ALL.equals(endsync) || ENDSYNC_MEDIA.equals(endsync)) { + return endsync; + } + + // FIXME add the checking for ID-Value and smil1.0-Id-value. + + setEndSync(ENDSYNC_LAST); + return ENDSYNC_LAST; + } + + public void setEndSync(String endSync) throws DOMException { + if (ENDSYNC_FIRST.equals(endSync) || ENDSYNC_LAST.equals(endSync) || + ENDSYNC_ALL.equals(endSync) || ENDSYNC_MEDIA.equals(endSync)) { + mSmilElement.setAttribute(ENDSYNC_ATTRIBUTE_NAME, endSync); + } else { // FIXME add the support for ID-Value and smil1.0-Id-value. + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, + "Unsupported endsync value" + endSync); + } + } + + @Override + public float getDur() { + float dur = super.getDur(); + if (dur == 0) { + dur = getImplicitDuration(); + } + return dur; + } + + public float getImplicitDuration() { + float dur = -1.0F; + if (ENDSYNC_LAST.equals(getEndSync())) { + NodeList children = getTimeChildren(); + for (int i = 0; i < children.getLength(); ++i) { + ElementTime child = (ElementTime) children.item(i); + TimeList endTimeList = child.getEnd(); + for (int j = 0; j < endTimeList.getLength(); ++j) { + Time endTime = endTimeList.item(j); + if (endTime.getTimeType() == Time.SMIL_TIME_INDEFINITE) { + // Return "indefinite" here. + return -1.0F; + } + if (endTime.getResolved()) { + float end = (float)endTime.getResolvedOffset(); + dur = (end > dur) ? end : dur; + } + } + } + } // Other endsync types are not supported now. + + return dur; + } + + public NodeList getActiveChildrenAt(float instant) { + /* + * Find the closest Time of ElementTime before instant. + * Add ElementTime to list of active elements if the Time belongs to the begin-list, + * do not add it otherwise. + */ + ArrayList activeChildren = new ArrayList(); + NodeList children = getTimeChildren(); + int childrenLen = children.getLength(); + for (int i = 0; i < childrenLen; ++i) { + double maxOffset = 0.0; + boolean active = false; + ElementTime child = (ElementTime) children.item(i); + + TimeList beginList = child.getBegin(); + int len = beginList.getLength(); + for (int j = 0; j < len; ++j) { + Time begin = beginList.item(j); + if (begin.getResolved()) { + double resolvedOffset = begin.getResolvedOffset() * 1000.0; + if ((resolvedOffset <= instant) && (resolvedOffset >= maxOffset)) { + maxOffset = resolvedOffset; + active = true; + } + } + } + + TimeList endList = child.getEnd(); + len = endList.getLength(); + for (int j = 0; j < len; ++j) { + Time end = endList.item(j); + if (end.getResolved()) { + double resolvedOffset = end.getResolvedOffset() * 1000.0; + if ((resolvedOffset <= instant) && (resolvedOffset >= maxOffset)) { + maxOffset = resolvedOffset; + active = false; + } + } + } + + if (active) { + activeChildren.add((Node) child); + } + } + return new NodeListImpl(activeChildren); + } +} diff --git a/src/org/thoughtcrime/securesms/dom/smil/ElementSequentialTimeContainerImpl.java b/src/org/thoughtcrime/securesms/dom/smil/ElementSequentialTimeContainerImpl.java new file mode 100644 index 0000000000..5ef5837a07 --- /dev/null +++ b/src/org/thoughtcrime/securesms/dom/smil/ElementSequentialTimeContainerImpl.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * 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.thoughtcrime.securesms.dom.smil; + +import java.util.ArrayList; + +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.smil.ElementSequentialTimeContainer; +import org.w3c.dom.smil.ElementTime; +import org.w3c.dom.smil.SMILElement; + +import org.thoughtcrime.securesms.dom.NodeListImpl; + +public abstract class ElementSequentialTimeContainerImpl extends + ElementTimeContainerImpl implements ElementSequentialTimeContainer { + + /* + * Internal Interface + */ + + ElementSequentialTimeContainerImpl(SMILElement element) { + super(element); + } + + /* + * ElementSequentialTimeContainer Interface + */ + + public NodeList getActiveChildrenAt(float instant) { + NodeList allChildren = this.getTimeChildren(); + ArrayList nodes = new ArrayList(); + for (int i = 0; i < allChildren.getLength(); i++) { + instant -= ((ElementTime) allChildren.item(i)).getDur(); + if (instant < 0) { + nodes.add(allChildren.item(i)); + return new NodeListImpl(nodes); + } + } + return new NodeListImpl(nodes); + } + + public float getDur() { + float dur = super.getDur(); + if (dur == 0) { + NodeList children = getTimeChildren(); + for (int i = 0; i < children.getLength(); ++i) { + ElementTime child = (ElementTime) children.item(i); + if (child.getDur() < 0) { + // Return "indefinite" since containing a child whose duration is indefinite. + return -1.0F; + } + dur += child.getDur(); + } + } + return dur; + } +} diff --git a/src/org/thoughtcrime/securesms/dom/smil/ElementTimeContainerImpl.java b/src/org/thoughtcrime/securesms/dom/smil/ElementTimeContainerImpl.java new file mode 100644 index 0000000000..63c64b8603 --- /dev/null +++ b/src/org/thoughtcrime/securesms/dom/smil/ElementTimeContainerImpl.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * 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.thoughtcrime.securesms.dom.smil; + +import org.w3c.dom.smil.ElementTimeContainer; +import org.w3c.dom.smil.SMILElement; + +public abstract class ElementTimeContainerImpl extends ElementTimeImpl implements + ElementTimeContainer { + + /* + * Internal Interface + */ + + ElementTimeContainerImpl(SMILElement element) { + super(element); + } +} diff --git a/src/org/thoughtcrime/securesms/dom/smil/ElementTimeImpl.java b/src/org/thoughtcrime/securesms/dom/smil/ElementTimeImpl.java new file mode 100644 index 0000000000..3c50600056 --- /dev/null +++ b/src/org/thoughtcrime/securesms/dom/smil/ElementTimeImpl.java @@ -0,0 +1,348 @@ +/* + * Copyright (C) 2007-2008 Esmertec AG. + * Copyright (C) 2007-2008 The Android Open Source Project + * + * 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.thoughtcrime.securesms.dom.smil; + +import java.util.ArrayList; + +import org.w3c.dom.DOMException; +import org.w3c.dom.smil.ElementTime; +import org.w3c.dom.smil.SMILElement; +import org.w3c.dom.smil.Time; +import org.w3c.dom.smil.TimeList; + +import android.util.Log; + +public abstract class ElementTimeImpl implements ElementTime { + private static final String TAG = "ElementTimeImpl"; + + private static final String FILL_REMOVE_ATTRIBUTE = "remove"; + private static final String FILL_FREEZE_ATTRIBUTE = "freeze"; + private static final String FILL_HOLD_ATTRIBUTE = "hold"; + private static final String FILL_TRANSITION_ATTRIBUTE = "transition"; + private static final String FILL_AUTO_ATTRIBUTE = "auto"; + private static final String FILL_ATTRIBUTE_NAME = "fill"; + private static final String FILLDEFAULT_ATTRIBUTE_NAME = "fillDefault"; + + final SMILElement mSmilElement; + + /* + * Internal Interface + */ + ElementTimeImpl(SMILElement element) { + mSmilElement = element; + } + + // Default implementation. Override if required. + int getBeginConstraints() { + return TimeImpl.ALLOW_ALL; + } + + // Default implementation. Override if required + int getEndConstraints() { + return TimeImpl.ALLOW_ALL; + } + + /** + * To get the parent node on the ElementTime tree. It is in opposition to getTimeChildren. + * @return the parent ElementTime. Returns null if there is no parent. + */ + abstract ElementTime getParentElementTime(); + + /* + * ElementTime Interface + */ + + public TimeList getBegin() { + String[] beginTimeStringList = mSmilElement.getAttribute("begin").split(";"); + + // TODO: Check other constraints on parsed values, e.g., "single, non-negative offset values + ArrayList beginTimeList = new ArrayList(); + // Initialize Time instances and add them to Vector + for (int i = 0; i < beginTimeStringList.length; i++) { + try { + beginTimeList.add(new TimeImpl(beginTimeStringList[i], getBeginConstraints())); + } catch (IllegalArgumentException e) { + // Ignore badly formatted times + } + } + if (beginTimeList.size() == 0) { + /* + * What is the right default value? + * + * In MMS SMIL, this method may be called either on an instance of: + * + * 1 - ElementSequentialTimeContainer (The SMILDocument) + * 2 - ElementParallelTimeContainer (A Time-Child of the SMILDocument, which is a seq) + * 3 - ElementTime (A SMILMediaElement). + * + * 1 - In the first case, the default start time is obviously 0. + * 2 - In the second case, the specifications mentions that + * "For children of a sequence, the only legal value for begin is + * a (single) non-negative offset value. The default begin value is 0." + * 3 - In the third case, the specification mentions that + * "The default value of begin for children of a par is 0." + * + * In short, if no value is specified, the default is always 0. + */ + + beginTimeList.add(new TimeImpl("0", TimeImpl.ALLOW_ALL)); + } + return new TimeListImpl(beginTimeList); + } + + public float getDur() { + float dur = 0; + try { + String durString = mSmilElement.getAttribute("dur"); + if (durString != null) { + dur = TimeImpl.parseClockValue(durString) / 1000f; + } + } catch (IllegalArgumentException e) { + // Do nothing and return the minimum value + } + + return dur; + } + + public TimeList getEnd() { + ArrayList endTimeList = new ArrayList(); + + String[] endTimeStringList = mSmilElement.getAttribute("end").split(";"); + int len = endTimeStringList.length; + if (!((len == 1) && (endTimeStringList[0].length() == 0))) { // Ensure the end field is set. + // Initialize Time instances and add them to Vector + for (int i = 0; i < len; i++) { + try { + endTimeList.add(new TimeImpl(endTimeStringList[i], + getEndConstraints())); + } catch (IllegalArgumentException e) { + // Ignore badly formatted times + Log.e(TAG, "Malformed time value.", e); + } + } + } + + // "end" time is not specified + if (endTimeList.size() == 0) { + // Get duration + float duration = getDur(); + + if (duration < 0) { + endTimeList.add(new TimeImpl("indefinite", getEndConstraints())); + } else { + // Get begin + TimeList begin = getBegin(); + for (int i = 0; i < begin.getLength(); i++) { + endTimeList.add(new TimeImpl( + // end = begin + dur + begin.item(i).getResolvedOffset() + duration + "s", + getEndConstraints())); + } + } + } + + return new TimeListImpl(endTimeList); + } + + private boolean beginAndEndAreZero() { + TimeList begin = getBegin(); + TimeList end = getEnd(); + if (begin.getLength() == 1 && end.getLength() == 1) { + Time beginTime = begin.item(0); + Time endTime = end.item(0); + return beginTime.getOffset() == 0. && endTime.getOffset() == 0.; + } + return false; + } + + public short getFill() { + String fill = mSmilElement.getAttribute(FILL_ATTRIBUTE_NAME); + if (fill.equalsIgnoreCase(FILL_FREEZE_ATTRIBUTE)) { + return FILL_FREEZE; + } else if (fill.equalsIgnoreCase(FILL_REMOVE_ATTRIBUTE)) { + return FILL_REMOVE; + } else if (fill.equalsIgnoreCase(FILL_HOLD_ATTRIBUTE)) { + // FIXME handle it as freeze for now + return FILL_FREEZE; + } else if (fill.equalsIgnoreCase(FILL_TRANSITION_ATTRIBUTE)) { + // FIXME handle it as freeze for now + return FILL_FREEZE; + } else if (!fill.equalsIgnoreCase(FILL_AUTO_ATTRIBUTE)) { + /* + * fill = default + * The fill behavior for the element is determined by the value of the fillDefault + * attribute. This is the default value. + */ + short fillDefault = getFillDefault(); + if (fillDefault != FILL_AUTO) { + return fillDefault; + } + } + + /* + * fill = auto + * The fill behavior for this element depends on whether the element specifies any of + * the attributes that define the simple or active duration: + * - If none of the attributes dur, end, repeatCount or repeatDur are specified on + * the element, then the element will have a fill behavior identical to that if it were + * specified as "freeze". + * - Otherwise, the element will have a fill behavior identical to that if it were + * specified as "remove". + */ + if (((mSmilElement.getAttribute("dur").length() == 0) && + (mSmilElement.getAttribute("end").length() == 0) && + (mSmilElement.getAttribute("repeatCount").length() == 0) && + (mSmilElement.getAttribute("repeatDur").length() == 0)) || + beginAndEndAreZero()) { + return FILL_FREEZE; + } else { + return FILL_REMOVE; + } + } + + public short getFillDefault() { + String fillDefault = mSmilElement.getAttribute(FILLDEFAULT_ATTRIBUTE_NAME); + if (fillDefault.equalsIgnoreCase(FILL_REMOVE_ATTRIBUTE)) { + return FILL_REMOVE; + } else if (fillDefault.equalsIgnoreCase(FILL_FREEZE_ATTRIBUTE)) { + return FILL_FREEZE; + } else if (fillDefault.equalsIgnoreCase(FILL_AUTO_ATTRIBUTE)) { + return FILL_AUTO; + } else if (fillDefault.equalsIgnoreCase(FILL_HOLD_ATTRIBUTE)) { + // FIXME handle it as freeze for now + return FILL_FREEZE; + } else if (fillDefault.equalsIgnoreCase(FILL_TRANSITION_ATTRIBUTE)) { + // FIXME handle it as freeze for now + return FILL_FREEZE; + } else { + /* + * fillDefault = inherit + * Specifies that the value of this attribute (and of the fill behavior) are + * inherited from the fillDefault value of the parent element. + * This is the default value. + */ + ElementTime parent = getParentElementTime(); + if (parent == null) { + /* + * fillDefault = auto + * If there is no parent element, the value is "auto". + */ + return FILL_AUTO; + } else { + return ((ElementTimeImpl) parent).getFillDefault(); + } + } + } + + public float getRepeatCount() { + String repeatCount = mSmilElement.getAttribute("repeatCount"); + try { + float value = Float.parseFloat(repeatCount); + if (value > 0) { + return value; + } else { + return 0; // default + } + } catch (NumberFormatException e) { + return 0; // default + } + } + + public float getRepeatDur() { + try { + float repeatDur = + TimeImpl.parseClockValue(mSmilElement.getAttribute("repeatDur")); + if (repeatDur > 0) { + return repeatDur; + } else { + return 0; // default + } + } catch (IllegalArgumentException e) { + return 0; // default + } + } + + public short getRestart() { + String restart = mSmilElement.getAttribute("restart"); + if (restart.equalsIgnoreCase("never")) { + return RESTART_NEVER; + } else if (restart.equalsIgnoreCase("whenNotActive")) { + return RESTART_WHEN_NOT_ACTIVE; + } else { + return RESTART_ALWAYS; // default + } + } + + public void setBegin(TimeList begin) throws DOMException { + // TODO Implement this + mSmilElement.setAttribute("begin", "indefinite"); + } + + public void setDur(float dur) throws DOMException { + // In SMIL 3.0, the dur could be a timecount-value which may contain fractions. + // However, in MMS 1.3, the dur SHALL be expressed in integer milliseconds. + mSmilElement.setAttribute("dur", Integer.toString((int)(dur * 1000)) + "ms"); + } + + public void setEnd(TimeList end) throws DOMException { + // TODO Implement this + mSmilElement.setAttribute("end", "indefinite"); + } + + public void setFill(short fill) throws DOMException { + if (fill == FILL_FREEZE) { + mSmilElement.setAttribute(FILL_ATTRIBUTE_NAME, FILL_FREEZE_ATTRIBUTE); + } else { + mSmilElement.setAttribute(FILL_ATTRIBUTE_NAME, FILL_REMOVE_ATTRIBUTE); // default + } + } + + public void setFillDefault(short fillDefault) throws DOMException { + if (fillDefault == FILL_FREEZE) { + mSmilElement.setAttribute(FILLDEFAULT_ATTRIBUTE_NAME, FILL_FREEZE_ATTRIBUTE); + } else { + mSmilElement.setAttribute(FILLDEFAULT_ATTRIBUTE_NAME, FILL_REMOVE_ATTRIBUTE); + } + } + + public void setRepeatCount(float repeatCount) throws DOMException { + String repeatCountString = "indefinite"; + if (repeatCount > 0) { + repeatCountString = Float.toString(repeatCount); + } + mSmilElement.setAttribute("repeatCount", repeatCountString); + } + + public void setRepeatDur(float repeatDur) throws DOMException { + String repeatDurString = "indefinite"; + if (repeatDur > 0) { + repeatDurString = Float.toString(repeatDur) + "ms"; + } + mSmilElement.setAttribute("repeatDur", repeatDurString); + } + + public void setRestart(short restart) throws DOMException { + if (restart == RESTART_NEVER) { + mSmilElement.setAttribute("restart", "never"); + } else if (restart == RESTART_WHEN_NOT_ACTIVE) { + mSmilElement.setAttribute("restart", "whenNotActive"); + } else { + mSmilElement.setAttribute("restart", "always"); + } + } +} diff --git a/src/org/thoughtcrime/securesms/dom/smil/SmilDocumentImpl.java b/src/org/thoughtcrime/securesms/dom/smil/SmilDocumentImpl.java new file mode 100644 index 0000000000..a4af0bdd66 --- /dev/null +++ b/src/org/thoughtcrime/securesms/dom/smil/SmilDocumentImpl.java @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * 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.thoughtcrime.securesms.dom.smil; + +import org.w3c.dom.DOMException; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.events.DocumentEvent; +import org.w3c.dom.events.Event; +import org.w3c.dom.smil.ElementSequentialTimeContainer; +import org.w3c.dom.smil.ElementTime; +import org.w3c.dom.smil.SMILDocument; +import org.w3c.dom.smil.SMILElement; +import org.w3c.dom.smil.SMILLayoutElement; +import org.w3c.dom.smil.TimeList; + +import org.thoughtcrime.securesms.dom.DocumentImpl; +import org.thoughtcrime.securesms.dom.events.EventImpl; + +public class SmilDocumentImpl extends DocumentImpl implements SMILDocument, DocumentEvent { + /* + * The sequential time container cannot be initialized here because the real container + * is body, which hasn't been created yet. It will be initialized when the body has + * already been created. Please see getBody(). + */ + ElementSequentialTimeContainer mSeqTimeContainer; + + public final static String SMIL_DOCUMENT_START_EVENT = "SmilDocumentStart"; + public final static String SMIL_DOCUMENT_END_EVENT = "SimlDocumentEnd"; + + /* + * Internal methods + */ + public SmilDocumentImpl() { + super(); + } + + /* + * ElementSequentialTimeContainer stuff + */ + + public NodeList getActiveChildrenAt(float instant) { + return mSeqTimeContainer.getActiveChildrenAt(instant); + } + + public NodeList getTimeChildren() { + return mSeqTimeContainer.getTimeChildren(); + } + + public boolean beginElement() { + return mSeqTimeContainer.beginElement(); + } + + public boolean endElement() { + return mSeqTimeContainer.endElement(); + } + + public TimeList getBegin() { + return mSeqTimeContainer.getBegin(); + } + + public float getDur() { + return mSeqTimeContainer.getDur(); + } + + public TimeList getEnd() { + return mSeqTimeContainer.getEnd(); + } + + public short getFill() { + return mSeqTimeContainer.getFill(); + } + + public short getFillDefault() { + return mSeqTimeContainer.getFillDefault(); + } + + public float getRepeatCount() { + return mSeqTimeContainer.getRepeatCount(); + } + + public float getRepeatDur() { + return mSeqTimeContainer.getRepeatDur(); + } + + public short getRestart() { + return mSeqTimeContainer.getRestart(); + } + + public void pauseElement() { + mSeqTimeContainer.pauseElement(); + } + + public void resumeElement() { + mSeqTimeContainer.resumeElement(); + } + + public void seekElement(float seekTo) { + mSeqTimeContainer.seekElement(seekTo); + } + + public void setBegin(TimeList begin) throws DOMException { + mSeqTimeContainer.setBegin(begin); + } + + public void setDur(float dur) throws DOMException { + mSeqTimeContainer.setDur(dur); + } + + public void setEnd(TimeList end) throws DOMException { + mSeqTimeContainer.setEnd(end); + } + + public void setFill(short fill) throws DOMException { + mSeqTimeContainer.setFill(fill); + } + + public void setFillDefault(short fillDefault) throws DOMException { + mSeqTimeContainer.setFillDefault(fillDefault); + } + + public void setRepeatCount(float repeatCount) throws DOMException { + mSeqTimeContainer.setRepeatCount(repeatCount); + } + + public void setRepeatDur(float repeatDur) throws DOMException { + mSeqTimeContainer.setRepeatDur(repeatDur); + } + + public void setRestart(short restart) throws DOMException { + mSeqTimeContainer.setRestart(restart); + } + + /* + * Document Interface + */ + + @Override + public Element createElement(String tagName) throws DOMException { + // Find the appropriate class for this element + tagName = tagName.toLowerCase(); + if (tagName.equals("text") || + tagName.equals("img") || + tagName.equals("video")) { + return new SmilRegionMediaElementImpl(this, tagName); + } else if (tagName.equals("audio")) { + return new SmilMediaElementImpl(this, tagName); + } else if (tagName.equals("layout")) { + return new SmilLayoutElementImpl(this, tagName); + } else if (tagName.equals("root-layout")) { + return new SmilRootLayoutElementImpl(this, tagName); + } else if (tagName.equals("region")) { + return new SmilRegionElementImpl(this, tagName); + } else if (tagName.equals("ref")) { + return new SmilRefElementImpl(this, tagName); + } else if (tagName.equals("par")) { + return new SmilParElementImpl(this, tagName); + } else { + // This includes also the structural nodes SMIL, + // HEAD, BODY, for which no specific types are defined. + return new SmilElementImpl(this, tagName); + } + } + + @Override + public SMILElement getDocumentElement() { + Node rootElement = getFirstChild(); + if (rootElement == null || !(rootElement instanceof SMILElement)) { + // The root doesn't exist. Create a new one. + rootElement = createElement("smil"); + appendChild(rootElement); + } + + return (SMILElement) rootElement; + } + + /* + * SMILElement Interface + */ + + public SMILElement getHead() { + Node rootElement = getDocumentElement(); + Node headElement = rootElement.getFirstChild(); + if (headElement == null || !(headElement instanceof SMILElement)) { + // The head doesn't exist. Create a new one. + headElement = createElement("head"); + rootElement.appendChild(headElement); + } + + return (SMILElement) headElement; + } + + public SMILElement getBody() { + Node rootElement = getDocumentElement(); + Node headElement = getHead(); + Node bodyElement = headElement.getNextSibling(); + if (bodyElement == null || !(bodyElement instanceof SMILElement)) { + // The body doesn't exist. Create a new one. + bodyElement = createElement("body"); + rootElement.appendChild(bodyElement); + } + + // Initialize the real sequential time container, which is body. + mSeqTimeContainer = new ElementSequentialTimeContainerImpl((SMILElement) bodyElement) { + public NodeList getTimeChildren() { + return getBody().getElementsByTagName("par"); + } + + public boolean beginElement() { + Event startEvent = createEvent("Event"); + startEvent.initEvent(SMIL_DOCUMENT_START_EVENT, false, false); + dispatchEvent(startEvent); + return true; + } + + public boolean endElement() { + Event endEvent = createEvent("Event"); + endEvent.initEvent(SMIL_DOCUMENT_END_EVENT, false, false); + dispatchEvent(endEvent); + return true; + } + + public void pauseElement() { + // TODO Auto-generated method stub + + } + + public void resumeElement() { + // TODO Auto-generated method stub + + } + + public void seekElement(float seekTo) { + // TODO Auto-generated method stub + + } + + ElementTime getParentElementTime() { + return null; + } + }; + + return (SMILElement) bodyElement; + } + + public SMILLayoutElement getLayout() { + Node headElement = getHead(); + Node layoutElement = null; + + // Find the layout element under HEAD + layoutElement = headElement.getFirstChild(); + while ((layoutElement != null) && !(layoutElement instanceof SMILLayoutElement)) { + layoutElement = layoutElement.getNextSibling(); + } + + if (layoutElement == null) { + // The layout doesn't exist. Create a default one. + layoutElement = new SmilLayoutElementImpl(this, "layout"); + headElement.appendChild(layoutElement); + } + return (SMILLayoutElement) layoutElement; + } + + /* + * DocumentEvent Interface + */ + public Event createEvent(String eventType) throws DOMException { + if ("Event".equals(eventType)) { + return new EventImpl(); + } else { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, + "Not supported interface"); + } + } +} diff --git a/src/org/thoughtcrime/securesms/dom/smil/SmilElementImpl.java b/src/org/thoughtcrime/securesms/dom/smil/SmilElementImpl.java new file mode 100644 index 0000000000..f050f46f68 --- /dev/null +++ b/src/org/thoughtcrime/securesms/dom/smil/SmilElementImpl.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * 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.thoughtcrime.securesms.dom.smil; + +import org.w3c.dom.DOMException; +import org.w3c.dom.smil.SMILElement; + +import org.thoughtcrime.securesms.dom.ElementImpl; + +public class SmilElementImpl extends ElementImpl implements SMILElement { + /** + * This constructor is used by the factory methods of the SmilDocument. + * + * @param owner The SMIL document to which this element belongs to + * @param tagName The tag name of the element + */ + SmilElementImpl(SmilDocumentImpl owner, String tagName) + { + super(owner, tagName.toLowerCase()); + } + + public String getId() { + // TODO Auto-generated method stub + return null; + } + + public void setId(String id) throws DOMException { + // TODO Auto-generated method stub + + } + +} diff --git a/src/org/thoughtcrime/securesms/dom/smil/SmilLayoutElementImpl.java b/src/org/thoughtcrime/securesms/dom/smil/SmilLayoutElementImpl.java new file mode 100644 index 0000000000..102c547643 --- /dev/null +++ b/src/org/thoughtcrime/securesms/dom/smil/SmilLayoutElementImpl.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * 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.thoughtcrime.securesms.dom.smil; + +import org.thoughtcrime.securesms.util.SmilUtil; +import org.w3c.dom.NodeList; +import org.w3c.dom.smil.SMILLayoutElement; +import org.w3c.dom.smil.SMILRootLayoutElement; + +public class SmilLayoutElementImpl extends SmilElementImpl implements + SMILLayoutElement { + SmilLayoutElementImpl(SmilDocumentImpl owner, String tagName) { + super(owner, tagName); + } + + public boolean getResolved() { + // TODO Auto-generated method stub + return false; + } + + public String getType() { + return this.getAttribute("type"); + } + + public NodeList getRegions() { + return this.getElementsByTagName("region"); + } + + public SMILRootLayoutElement getRootLayout() { + NodeList childNodes = this.getChildNodes(); + SMILRootLayoutElement rootLayoutNode = null; + int childrenCount = childNodes.getLength(); + for (int i = 0; i < childrenCount; i++) { + if (childNodes.item(i).getNodeName().equals("root-layout")) { + rootLayoutNode = (SMILRootLayoutElement)childNodes.item(i); + } + } + if (null == rootLayoutNode) { + // root-layout node is not set. Create a default one. + rootLayoutNode = (SMILRootLayoutElement) getOwnerDocument().createElement("root-layout"); + rootLayoutNode.setWidth(SmilUtil.ROOT_WIDTH); + rootLayoutNode.setHeight(SmilUtil.ROOT_HEIGHT); + appendChild(rootLayoutNode); + } + return rootLayoutNode; + } + +} diff --git a/src/org/thoughtcrime/securesms/dom/smil/SmilMediaElementImpl.java b/src/org/thoughtcrime/securesms/dom/smil/SmilMediaElementImpl.java new file mode 100644 index 0000000000..71a16d58dd --- /dev/null +++ b/src/org/thoughtcrime/securesms/dom/smil/SmilMediaElementImpl.java @@ -0,0 +1,343 @@ +/* + * Copyright (C) 2007-2008 Esmertec AG. + * Copyright (C) 2007-2008 The Android Open Source Project + * + * 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.thoughtcrime.securesms.dom.smil; + +import org.w3c.dom.DOMException; +import org.w3c.dom.events.DocumentEvent; +import org.w3c.dom.events.Event; +import org.w3c.dom.smil.ElementTime; +import org.w3c.dom.smil.SMILMediaElement; +import org.w3c.dom.smil.TimeList; + +import android.util.Log; + +import org.thoughtcrime.securesms.dom.events.EventImpl; + +public class SmilMediaElementImpl extends SmilElementImpl implements + SMILMediaElement { + public final static String SMIL_MEDIA_START_EVENT = "SmilMediaStart"; + public final static String SMIL_MEDIA_END_EVENT = "SmilMediaEnd"; + public final static String SMIL_MEDIA_PAUSE_EVENT = "SmilMediaPause"; + public final static String SMIL_MEDIA_SEEK_EVENT = "SmilMediaSeek"; + private final static String TAG = "Mms:smil"; + private static final boolean DEBUG = false; + private static final boolean LOCAL_LOGV = false; + + ElementTime mElementTime = new ElementTimeImpl(this) { + private Event createEvent(String eventType) { + DocumentEvent doc = + (DocumentEvent)SmilMediaElementImpl.this.getOwnerDocument(); + Event event = doc.createEvent("Event"); + event.initEvent(eventType, false, false); + if (LOCAL_LOGV) { + Log.v(TAG, "Dispatching 'begin' event to " + + SmilMediaElementImpl.this.getTagName() + " " + + SmilMediaElementImpl.this.getSrc() + " at " + + System.currentTimeMillis()); + } + return event; + } + + private Event createEvent(String eventType, int seekTo) { + DocumentEvent doc = + (DocumentEvent)SmilMediaElementImpl.this.getOwnerDocument(); + EventImpl event = (EventImpl) doc.createEvent("Event"); + event.initEvent(eventType, false, false, seekTo); + if (LOCAL_LOGV) { + Log.v(TAG, "Dispatching 'begin' event to " + + SmilMediaElementImpl.this.getTagName() + " " + + SmilMediaElementImpl.this.getSrc() + " at " + + System.currentTimeMillis()); + } + return event; + } + + public boolean beginElement() { + Event startEvent = createEvent(SMIL_MEDIA_START_EVENT); + dispatchEvent(startEvent); + return true; + } + + public boolean endElement() { + Event endEvent = createEvent(SMIL_MEDIA_END_EVENT); + dispatchEvent(endEvent); + return true; + } + + public void resumeElement() { + Event resumeEvent = createEvent(SMIL_MEDIA_START_EVENT); + dispatchEvent(resumeEvent); + } + + public void pauseElement() { + Event pauseEvent = createEvent(SMIL_MEDIA_PAUSE_EVENT); + dispatchEvent(pauseEvent); + } + + public void seekElement(float seekTo) { + Event seekEvent = createEvent(SMIL_MEDIA_SEEK_EVENT, (int) seekTo); + dispatchEvent(seekEvent); + } + + @Override + public float getDur() { + float dur = super.getDur(); + if (dur == 0) { + // Duration is not specified, So get the implicit duration. + String tag = getTagName(); + if (tag.equals("video") || tag.equals("audio")) { + // Continuous media + // FIXME Should get the duration of the media. "indefinite" instead here. + dur = -1.0F; + } else if (tag.equals("text") || tag.equals("img")) { + // Discrete media + dur = 0; + } else { + Log.w(TAG, "Unknown media type"); + } + } + return dur; + } + + @Override + ElementTime getParentElementTime() { + return ((SmilParElementImpl) mSmilElement.getParentNode()).mParTimeContainer; + } + }; + + /* + * Internal Interface + */ + + SmilMediaElementImpl(SmilDocumentImpl owner, String tagName) { + super(owner, tagName); + } + + /* + * SMILMediaElement Interface + */ + + public String getAbstractAttr() { + return this.getAttribute("abstract"); + } + + public String getAlt() { + return this.getAttribute("alt"); + } + + public String getAuthor() { + return this.getAttribute("author"); + } + + public String getClipBegin() { + return this.getAttribute("clipBegin"); + } + + public String getClipEnd() { + return this.getAttribute("clipEnd"); + } + + public String getCopyright() { + return this.getAttribute("copyright"); + } + + public String getLongdesc() { + return this.getAttribute("longdesc"); + } + + public String getPort() { + return this.getAttribute("port"); + } + + public String getReadIndex() { + return this.getAttribute("readIndex"); + } + + public String getRtpformat() { + return this.getAttribute("rtpformat"); + } + + public String getSrc() { + return this.getAttribute("src"); + } + + public String getStripRepeat() { + return this.getAttribute("stripRepeat"); + } + + public String getTitle() { + return this.getAttribute("title"); + } + + public String getTransport() { + return this.getAttribute("transport"); + } + + public String getType() { + return this.getAttribute("type"); + } + + public void setAbstractAttr(String abstractAttr) throws DOMException { + this.setAttribute("abstract", abstractAttr); + } + + public void setAlt(String alt) throws DOMException { + this.setAttribute("alt", alt); + } + + public void setAuthor(String author) throws DOMException { + this.setAttribute("author", author); + } + + public void setClipBegin(String clipBegin) throws DOMException { + this.setAttribute("clipBegin", clipBegin); + } + + public void setClipEnd(String clipEnd) throws DOMException { + this.setAttribute("clipEnd", clipEnd); + } + + public void setCopyright(String copyright) throws DOMException { + this.setAttribute("copyright", copyright); + } + + public void setLongdesc(String longdesc) throws DOMException { + this.setAttribute("longdesc", longdesc); + + } + + public void setPort(String port) throws DOMException { + this.setAttribute("port", port); + } + + public void setReadIndex(String readIndex) throws DOMException { + this.setAttribute("readIndex", readIndex); + } + + public void setRtpformat(String rtpformat) throws DOMException { + this.setAttribute("rtpformat", rtpformat); + } + + public void setSrc(String src) throws DOMException { + this.setAttribute("src", src); + } + + public void setStripRepeat(String stripRepeat) throws DOMException { + this.setAttribute("stripRepeat", stripRepeat); + } + + public void setTitle(String title) throws DOMException { + this.setAttribute("title", title); + } + + public void setTransport(String transport) throws DOMException { + this.setAttribute("transport", transport); + } + + public void setType(String type) throws DOMException { + this.setAttribute("type", type); + } + + /* + * TimeElement Interface + */ + + public boolean beginElement() { + return mElementTime.beginElement(); + } + + public boolean endElement() { + return mElementTime.endElement(); + } + + public TimeList getBegin() { + return mElementTime.getBegin(); + } + + public float getDur() { + return mElementTime.getDur(); + } + + public TimeList getEnd() { + return mElementTime.getEnd(); + } + + public short getFill() { + return mElementTime.getFill(); + } + + public short getFillDefault() { + return mElementTime.getFillDefault(); + } + + public float getRepeatCount() { + return mElementTime.getRepeatCount(); + } + + public float getRepeatDur() { + return mElementTime.getRepeatDur(); + } + + public short getRestart() { + return mElementTime.getRestart(); + } + + public void pauseElement() { + mElementTime.pauseElement(); + } + + public void resumeElement() { + mElementTime.resumeElement(); + } + + public void seekElement(float seekTo) { + mElementTime.seekElement(seekTo); + } + + public void setBegin(TimeList begin) throws DOMException { + mElementTime.setBegin(begin); + } + + public void setDur(float dur) throws DOMException { + mElementTime.setDur(dur); + } + + public void setEnd(TimeList end) throws DOMException { + mElementTime.setEnd(end); + } + + public void setFill(short fill) throws DOMException { + mElementTime.setFill(fill); + } + + public void setFillDefault(short fillDefault) throws DOMException { + mElementTime.setFillDefault(fillDefault); + } + + public void setRepeatCount(float repeatCount) throws DOMException { + mElementTime.setRepeatCount(repeatCount); + } + + public void setRepeatDur(float repeatDur) throws DOMException { + mElementTime.setRepeatDur(repeatDur); + } + + public void setRestart(short restart) throws DOMException { + mElementTime.setRestart(restart); + } +} diff --git a/src/org/thoughtcrime/securesms/dom/smil/SmilParElementImpl.java b/src/org/thoughtcrime/securesms/dom/smil/SmilParElementImpl.java new file mode 100644 index 0000000000..71bac5ec85 --- /dev/null +++ b/src/org/thoughtcrime/securesms/dom/smil/SmilParElementImpl.java @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * 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.thoughtcrime.securesms.dom.smil; + +import java.util.ArrayList; + +import org.w3c.dom.DOMException; +import org.w3c.dom.NodeList; +import org.w3c.dom.events.DocumentEvent; +import org.w3c.dom.events.Event; +import org.w3c.dom.smil.ElementParallelTimeContainer; +import org.w3c.dom.smil.ElementTime; +import org.w3c.dom.smil.SMILParElement; +import org.w3c.dom.smil.Time; +import org.w3c.dom.smil.TimeList; + +public class SmilParElementImpl extends SmilElementImpl implements SMILParElement { + public final static String SMIL_SLIDE_START_EVENT = "SmilSlideStart"; + public final static String SMIL_SLIDE_END_EVENT = "SmilSlideEnd"; + + ElementParallelTimeContainer mParTimeContainer = + new ElementParallelTimeContainerImpl(this) { + @Override + public TimeList getBegin() { + /* + * For children of a sequence, the only legal value for begin is + * a (single) non-negative offset value. + */ + TimeList beginTimeList = super.getBegin(); + if (beginTimeList.getLength() > 1) { + ArrayList singleTimeContainer = new ArrayList(); + singleTimeContainer.add(beginTimeList.item(0)); + beginTimeList = new TimeListImpl(singleTimeContainer); + } + return beginTimeList; + } + + public NodeList getTimeChildren() { + return getChildNodes(); + } + + public boolean beginElement() { + DocumentEvent doc = (DocumentEvent) SmilParElementImpl.this.getOwnerDocument(); + Event startEvent = doc.createEvent("Event"); + startEvent.initEvent(SMIL_SLIDE_START_EVENT, false, false); + dispatchEvent(startEvent); + return true; + } + + public boolean endElement() { + DocumentEvent doc = (DocumentEvent) SmilParElementImpl.this.getOwnerDocument(); + Event endEvent = doc.createEvent("Event"); + endEvent.initEvent(SMIL_SLIDE_END_EVENT, false, false); + dispatchEvent(endEvent); + return true; + } + + public void pauseElement() { + // TODO Auto-generated method stub + + } + + public void resumeElement() { + // TODO Auto-generated method stub + + } + + public void seekElement(float seekTo) { + // TODO Auto-generated method stub + + } + + ElementTime getParentElementTime() { + return ((SmilDocumentImpl) mSmilElement.getOwnerDocument()).mSeqTimeContainer; + } + }; + + /* + * Internal Interface + */ + + SmilParElementImpl(SmilDocumentImpl owner, String tagName) + { + super(owner, tagName.toUpperCase()); + } + + int getBeginConstraints() { + /* + * For children of a sequence, the only legal value for begin is + * a (single) non-negative offset value. + */ + return (TimeImpl.ALLOW_OFFSET_VALUE); // Do not set ALLOW_NEGATIVE_VALUE + } + + /* + * ElementParallelTimeContainer + */ + + public String getEndSync() { + return mParTimeContainer.getEndSync(); + } + + public float getImplicitDuration() { + return mParTimeContainer.getImplicitDuration(); + } + + public void setEndSync(String endSync) throws DOMException { + mParTimeContainer.setEndSync(endSync); + } + + public NodeList getActiveChildrenAt(float instant) { + return mParTimeContainer.getActiveChildrenAt(instant); + } + + public NodeList getTimeChildren() { + return mParTimeContainer.getTimeChildren(); + } + + public boolean beginElement() { + return mParTimeContainer.beginElement(); + } + + public boolean endElement() { + return mParTimeContainer.endElement(); + } + + public TimeList getBegin() { + return mParTimeContainer.getBegin(); + } + + public float getDur() { + return mParTimeContainer.getDur(); + } + + public TimeList getEnd() { + return mParTimeContainer.getEnd(); + } + + public short getFill() { + return mParTimeContainer.getFill(); + } + + public short getFillDefault() { + return mParTimeContainer.getFillDefault(); + } + + public float getRepeatCount() { + return mParTimeContainer.getRepeatCount(); + } + + public float getRepeatDur() { + return mParTimeContainer.getRepeatDur(); + } + + public short getRestart() { + return mParTimeContainer.getRestart(); + } + + public void pauseElement() { + mParTimeContainer.pauseElement(); + } + + public void resumeElement() { + mParTimeContainer.resumeElement(); + } + + public void seekElement(float seekTo) { + mParTimeContainer.seekElement(seekTo); + } + + public void setBegin(TimeList begin) throws DOMException { + mParTimeContainer.setBegin(begin); + } + + public void setDur(float dur) throws DOMException { + mParTimeContainer.setDur(dur); + } + + public void setEnd(TimeList end) throws DOMException { + mParTimeContainer.setEnd(end); + } + + public void setFill(short fill) throws DOMException { + mParTimeContainer.setFill(fill); + } + + public void setFillDefault(short fillDefault) throws DOMException { + mParTimeContainer.setFillDefault(fillDefault); + } + + public void setRepeatCount(float repeatCount) throws DOMException { + mParTimeContainer.setRepeatCount(repeatCount); + } + + public void setRepeatDur(float repeatDur) throws DOMException { + mParTimeContainer.setRepeatDur(repeatDur); + } + + public void setRestart(short restart) throws DOMException { + mParTimeContainer.setRestart(restart); + } +} diff --git a/src/org/thoughtcrime/securesms/dom/smil/SmilRefElementImpl.java b/src/org/thoughtcrime/securesms/dom/smil/SmilRefElementImpl.java new file mode 100644 index 0000000000..f2cbfa5d3e --- /dev/null +++ b/src/org/thoughtcrime/securesms/dom/smil/SmilRefElementImpl.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * 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.thoughtcrime.securesms.dom.smil; + +import org.w3c.dom.smil.SMILRefElement; + +public class SmilRefElementImpl extends SmilRegionMediaElementImpl implements + SMILRefElement { + + SmilRefElementImpl(SmilDocumentImpl owner, String tagName) { + super(owner, tagName); + } + +} diff --git a/src/org/thoughtcrime/securesms/dom/smil/SmilRegionElementImpl.java b/src/org/thoughtcrime/securesms/dom/smil/SmilRegionElementImpl.java new file mode 100644 index 0000000000..8485e1fb6d --- /dev/null +++ b/src/org/thoughtcrime/securesms/dom/smil/SmilRegionElementImpl.java @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2007-2008 Esmertec AG. + * Copyright (C) 2007-2008 The Android Open Source Project + * + * 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.thoughtcrime.securesms.dom.smil; + +import org.w3c.dom.DOMException; +import org.w3c.dom.smil.SMILDocument; +import org.w3c.dom.smil.SMILRegionElement; + +import android.util.Log; + +public class SmilRegionElementImpl extends SmilElementImpl implements + SMILRegionElement { + + /* + * Internal Interface + */ + + private static final String HIDDEN_ATTRIBUTE = "hidden"; + private static final String SLICE_ATTRIBUTE = "slice"; + private static final String SCROLL_ATTRIBUTE = "scroll"; + private static final String MEET_ATTRIBUTE = "meet"; + private static final String FILL_ATTRIBUTE = "fill"; + private static final String ID_ATTRIBUTE_NAME = "id"; + private static final String WIDTH_ATTRIBUTE_NAME = "width"; + private static final String TITLE_ATTRIBUTE_NAME = "title"; + private static final String HEIGHT_ATTRIBUTE_NAME = "height"; + private static final String BACKGROUND_COLOR_ATTRIBUTE_NAME = "backgroundColor"; + private static final String Z_INDEX_ATTRIBUTE_NAME = "z-index"; + private static final String TOP_ATTRIBUTE_NAME = "top"; + private static final String LEFT_ATTRIBUTE_NAME = "left"; + private static final String RIGHT_ATTRIBUTE_NAME = "right"; + private static final String BOTTOM_ATTRIBUTE_NAME = "bottom"; + private static final String FIT_ATTRIBUTE_NAME = "fit"; + private static final String TAG = "SmilRegionElementImpl"; + private static final boolean DEBUG = false; + private static final boolean LOCAL_LOGV = false; + + SmilRegionElementImpl(SmilDocumentImpl owner, String tagName) { + super(owner, tagName); + } + + /* + * SMILRegionElement Interface + */ + + public String getFit() { + String fit = getAttribute(FIT_ATTRIBUTE_NAME); + if (FILL_ATTRIBUTE.equalsIgnoreCase(fit)) { + return FILL_ATTRIBUTE; + } else if (MEET_ATTRIBUTE.equalsIgnoreCase(fit)) { + return MEET_ATTRIBUTE; + } else if (SCROLL_ATTRIBUTE.equalsIgnoreCase(fit)) { + return SCROLL_ATTRIBUTE; + } else if (SLICE_ATTRIBUTE.equalsIgnoreCase(fit)) { + return SLICE_ATTRIBUTE; + } else { + return HIDDEN_ATTRIBUTE; + } + } + + public int getLeft() { + try { + return parseRegionLength(getAttribute(LEFT_ATTRIBUTE_NAME), true); + } catch (NumberFormatException _) { + if (LOCAL_LOGV) { + Log.v(TAG, "Left attribute is not set or incorrect."); + } + } + try { + int bbw = ((SMILDocument) getOwnerDocument()).getLayout().getRootLayout().getWidth(); + int right = parseRegionLength(getAttribute(RIGHT_ATTRIBUTE_NAME), true); + int width = parseRegionLength(getAttribute(WIDTH_ATTRIBUTE_NAME), true); + return bbw - right - width; + } catch (NumberFormatException _) { + if (LOCAL_LOGV) { + Log.v(TAG, "Right or width attribute is not set or incorrect."); + } + } + return 0; + } + + public int getTop() { + try { + return parseRegionLength(getAttribute(TOP_ATTRIBUTE_NAME), false); + } catch (NumberFormatException _) { + if (LOCAL_LOGV) { + Log.v(TAG, "Top attribute is not set or incorrect."); + } + } + try { + int bbh = ((SMILDocument) getOwnerDocument()).getLayout().getRootLayout().getHeight(); + int bottom = parseRegionLength(getAttribute(BOTTOM_ATTRIBUTE_NAME), false); + int height = parseRegionLength(getAttribute(HEIGHT_ATTRIBUTE_NAME), false); + return bbh - bottom - height; + } catch (NumberFormatException _) { + if (LOCAL_LOGV) { + Log.v(TAG, "Bottom or height attribute is not set or incorrect."); + } + } + return 0; + } + + public int getZIndex() { + try { + return Integer.parseInt(this.getAttribute(Z_INDEX_ATTRIBUTE_NAME)); + } catch (NumberFormatException _) { + return 0; + } + } + + public void setFit(String fit) throws DOMException { + if (fit.equalsIgnoreCase(FILL_ATTRIBUTE) + || fit.equalsIgnoreCase(MEET_ATTRIBUTE) + || fit.equalsIgnoreCase(SCROLL_ATTRIBUTE) + || fit.equalsIgnoreCase(SLICE_ATTRIBUTE)) { + this.setAttribute(FIT_ATTRIBUTE_NAME, fit.toLowerCase()); + } else { + this.setAttribute(FIT_ATTRIBUTE_NAME, HIDDEN_ATTRIBUTE); + } + } + + public void setLeft(int left) throws DOMException { + this.setAttribute(LEFT_ATTRIBUTE_NAME, String.valueOf(left)); + } + + public void setTop(int top) throws DOMException { + this.setAttribute(TOP_ATTRIBUTE_NAME, String.valueOf(top)); + } + + public void setZIndex(int zIndex) throws DOMException { + if (zIndex > 0) { + this.setAttribute(Z_INDEX_ATTRIBUTE_NAME, Integer.toString(zIndex)); + } else { + this.setAttribute(Z_INDEX_ATTRIBUTE_NAME, Integer.toString(0)); + } + } + + public String getBackgroundColor() { + return this.getAttribute(BACKGROUND_COLOR_ATTRIBUTE_NAME); + } + + public int getHeight() { + try { + final int height = parseRegionLength(getAttribute(HEIGHT_ATTRIBUTE_NAME), false); + return height == 0 ? + ((SMILDocument) getOwnerDocument()).getLayout().getRootLayout().getHeight() : + height; + } catch (NumberFormatException _) { + if (LOCAL_LOGV) { + Log.v(TAG, "Height attribute is not set or incorrect."); + } + } + int bbh = ((SMILDocument) getOwnerDocument()).getLayout().getRootLayout().getHeight(); + try { + bbh -= parseRegionLength(getAttribute(TOP_ATTRIBUTE_NAME), false); + } catch (NumberFormatException _) { + if (LOCAL_LOGV) { + Log.v(TAG, "Top attribute is not set or incorrect."); + } + } + try { + bbh -= parseRegionLength(getAttribute(BOTTOM_ATTRIBUTE_NAME), false); + } catch (NumberFormatException _) { + if (LOCAL_LOGV) { + Log.v(TAG, "Bottom attribute is not set or incorrect."); + } + } + return bbh; + } + + public String getTitle() { + return this.getAttribute(TITLE_ATTRIBUTE_NAME); + } + + public int getWidth() { + try { + final int width = parseRegionLength(getAttribute(WIDTH_ATTRIBUTE_NAME), true); + return width == 0 ? + ((SMILDocument) getOwnerDocument()).getLayout().getRootLayout().getWidth() : + width; + } catch (NumberFormatException _) { + if (LOCAL_LOGV) { + Log.v(TAG, "Width attribute is not set or incorrect."); + } + } + int bbw = ((SMILDocument) getOwnerDocument()).getLayout().getRootLayout().getWidth(); + try { + bbw -= parseRegionLength(getAttribute(LEFT_ATTRIBUTE_NAME), true); + } catch (NumberFormatException _) { + if (LOCAL_LOGV) { + Log.v(TAG, "Left attribute is not set or incorrect."); + } + } + try { + bbw -= parseRegionLength(getAttribute(RIGHT_ATTRIBUTE_NAME), true); + } catch (NumberFormatException _) { + if (LOCAL_LOGV) { + Log.v(TAG, "Right attribute is not set or incorrect."); + } + } + return bbw; + } + + public void setBackgroundColor(String backgroundColor) throws DOMException { + this.setAttribute(BACKGROUND_COLOR_ATTRIBUTE_NAME, backgroundColor); + } + + public void setHeight(int height) throws DOMException { + this.setAttribute(HEIGHT_ATTRIBUTE_NAME, String.valueOf(height) + "px"); + } + + public void setTitle(String title) throws DOMException { + this.setAttribute(TITLE_ATTRIBUTE_NAME, title); + } + + public void setWidth(int width) throws DOMException { + this.setAttribute(WIDTH_ATTRIBUTE_NAME, String.valueOf(width) + "px"); + } + + /* + * SMILElement Interface + */ + + @Override + public String getId() { + return this.getAttribute(ID_ATTRIBUTE_NAME); + } + + @Override + public void setId(String id) throws DOMException { + this.setAttribute(ID_ATTRIBUTE_NAME, id); + } + + /* + * Internal Interface + */ + + private int parseRegionLength(String length, boolean horizontal) { + if (length.endsWith("px")) { + length = length.substring(0, length.indexOf("px")); + return Integer.parseInt(length); + } else if (length.endsWith("%")) { + double value = 0.01*Integer.parseInt(length.substring(0, length.length() - 1)); + if (horizontal) { + value *= ((SMILDocument) getOwnerDocument()).getLayout().getRootLayout().getWidth(); + } else { + value *= ((SMILDocument) getOwnerDocument()).getLayout().getRootLayout().getHeight(); + } + return (int) Math.round(value); + } else { + return Integer.parseInt(length); + } + } + + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return super.toString() + + ": id=" + getId() + + ", width=" + getWidth() + + ", height=" + getHeight() + + ", left=" + getLeft() + + ", top=" + getTop(); + } +} diff --git a/src/org/thoughtcrime/securesms/dom/smil/SmilRegionMediaElementImpl.java b/src/org/thoughtcrime/securesms/dom/smil/SmilRegionMediaElementImpl.java new file mode 100644 index 0000000000..85b5890c3d --- /dev/null +++ b/src/org/thoughtcrime/securesms/dom/smil/SmilRegionMediaElementImpl.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * 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.thoughtcrime.securesms.dom.smil; + +import org.w3c.dom.NodeList; +import org.w3c.dom.smil.SMILDocument; +import org.w3c.dom.smil.SMILRegionElement; +import org.w3c.dom.smil.SMILRegionMediaElement; + +public class SmilRegionMediaElementImpl extends SmilMediaElementImpl implements + SMILRegionMediaElement { + private SMILRegionElement mRegion; + + SmilRegionMediaElementImpl(SmilDocumentImpl owner, String tagName) { + super(owner, tagName); + } + + public SMILRegionElement getRegion() { + if (mRegion == null) { + SMILDocument doc = (SMILDocument)this.getOwnerDocument(); + NodeList regions = doc.getLayout().getElementsByTagName("region"); + SMILRegionElement region = null; + for (int i = 0; i < regions.getLength(); i++) { + region = (SMILRegionElement)regions.item(i); + if (region.getId().equals(this.getAttribute("region"))) { + mRegion = region; + } + } + } + return mRegion; + } + + public void setRegion(SMILRegionElement region) { + this.setAttribute("region", region.getId()); + mRegion = region; + } + +} diff --git a/src/org/thoughtcrime/securesms/dom/smil/SmilRootLayoutElementImpl.java b/src/org/thoughtcrime/securesms/dom/smil/SmilRootLayoutElementImpl.java new file mode 100644 index 0000000000..4b59606c94 --- /dev/null +++ b/src/org/thoughtcrime/securesms/dom/smil/SmilRootLayoutElementImpl.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * 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.thoughtcrime.securesms.dom.smil; + +import org.w3c.dom.DOMException; +import org.w3c.dom.smil.SMILRootLayoutElement; + +public class SmilRootLayoutElementImpl extends SmilElementImpl implements + SMILRootLayoutElement { + + private static final String WIDTH_ATTRIBUTE_NAME = "width"; + private static final String HEIGHT_ATTRIBUTE_NAME = "height"; + private static final String BACKGROUND_COLOR_ATTRIBUTE_NAME = "backgroundColor"; + private static final String TITLE_ATTRIBUTE_NAME = "title"; + + SmilRootLayoutElementImpl(SmilDocumentImpl owner, String tagName) { + super(owner, tagName); + } + + public String getBackgroundColor() { + return this.getAttribute(BACKGROUND_COLOR_ATTRIBUTE_NAME); + } + + public int getHeight() { + String heightString = this.getAttribute(HEIGHT_ATTRIBUTE_NAME); + return parseAbsoluteLength(heightString); + } + + public String getTitle() { + return this.getAttribute(TITLE_ATTRIBUTE_NAME); + } + + public int getWidth() { + String widthString = this.getAttribute(WIDTH_ATTRIBUTE_NAME); + return parseAbsoluteLength(widthString); + } + + public void setBackgroundColor(String backgroundColor) throws DOMException { + this.setAttribute(BACKGROUND_COLOR_ATTRIBUTE_NAME, backgroundColor); + } + + public void setHeight(int height) throws DOMException { + this.setAttribute(HEIGHT_ATTRIBUTE_NAME, String.valueOf(height) + "px"); + + } + + public void setTitle(String title) throws DOMException { + this.setAttribute(TITLE_ATTRIBUTE_NAME, title); + } + + public void setWidth(int width) throws DOMException { + this.setAttribute(WIDTH_ATTRIBUTE_NAME, String.valueOf(width) + "px"); + } + + /* + * Internal Interface + */ + + private int parseAbsoluteLength(String length) { + if (length.endsWith("px")) { + length = length.substring(0, length.indexOf("px")); + } + try { + return Integer.parseInt(length); + } catch (NumberFormatException e) { + return 0; + } + } +} diff --git a/src/org/thoughtcrime/securesms/dom/smil/TimeImpl.java b/src/org/thoughtcrime/securesms/dom/smil/TimeImpl.java new file mode 100644 index 0000000000..7b525d847d --- /dev/null +++ b/src/org/thoughtcrime/securesms/dom/smil/TimeImpl.java @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2007-2008 Esmertec AG. + * Copyright (C) 2007-2008 The Android Open Source Project + * + * 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.thoughtcrime.securesms.dom.smil; + +import org.w3c.dom.DOMException; +import org.w3c.dom.Element; +import org.w3c.dom.smil.Time; + +public class TimeImpl implements Time { + static final int ALLOW_INDEFINITE_VALUE = (1 << 0); + static final int ALLOW_OFFSET_VALUE = (1 << 1); + static final int ALLOW_SYNCBASE_VALUE = (1 << 2); + static final int ALLOW_SYNCTOPREV_VALUE = (1 << 3); + static final int ALLOW_EVENT_VALUE = (1 << 4); + static final int ALLOW_MARKER_VALUE = (1 << 5); + static final int ALLOW_WALLCLOCK_VALUE = (1 << 6); + static final int ALLOW_NEGATIVE_VALUE = (1 << 7); + static final int ALLOW_ALL = 0xFF; + + short mTimeType; + boolean mResolved; + double mResolvedOffset; + + /** + * Creates a TimeImpl representation of a time-value represented as a String. + * Time-values have the following syntax: + * + * + * Time-val ::= ( smil-1.0-syncbase-value + * | "indefinite" + * | offset-value + * | syncbase-value + * | syncToPrev-value + * | event-value + * | media-marker-value + * | wallclock-sync-value ) + * Smil-1.0-syncbase-value ::= + * "id(" id-ref ")" ( "(" ( "begin" | "end" | clock-value ) ")" )? + * Offset-value ::= ( "+" | "-" )? clock-value + * Syncbase-value ::= ( id-ref "." ( "begin" | "end" ) ) ( ( "+" | "-" ) clock-value )? + * SyncToPrev-value ::= ( "prev.begin" | "prev.end" ) ( ( "+" | "-" ) clock-value )? + * Event-value ::= ( id-ref "." )? ( event-ref ) ( ( "+" | "-" ) clock-value )? + * Media-marker-value ::= id-ref ".marker(" marker-name ")" + * Wallclock-sync-value ::= "wallclock(" wallclock-value ")" + * + * + * @param timeValue A String in the representation specified above + * @param constraints Any combination of the #ALLOW_* flags + * @return A TimeImpl instance representing + * @exception java.lang.IllegalArgumentException if the timeValue input + * parameter does not comply with the defined syntax + * @exception java.lang.NullPointerException if the timekValue string is + * null + */ + TimeImpl(String timeValue, int constraints) { + /* + * We do not support yet: + * - smil-1.0-syncbase-value + * - syncbase-value + * - syncToPrev-value + * - event-value + * - Media-marker-value + * - Wallclock-sync-value + */ + // Will throw NullPointerException if timeValue is null + if (timeValue.equals("indefinite") + && ((constraints & ALLOW_INDEFINITE_VALUE) != 0) ) { + mTimeType = SMIL_TIME_INDEFINITE; + } else if ((constraints & ALLOW_OFFSET_VALUE) != 0) { + int sign = 1; + if (timeValue.startsWith("+")) { + timeValue = timeValue.substring(1); + } else if (timeValue.startsWith("-")) { + timeValue = timeValue.substring(1); + sign = -1; + } + mResolvedOffset = sign*parseClockValue(timeValue)/1000.0; + mResolved = true; + mTimeType = SMIL_TIME_OFFSET; + } else { + throw new IllegalArgumentException("Unsupported time value"); + } + } + + /** + * Converts a String representation of a clock value into the float + * representation used in this API. + * + * Clock values have the following syntax: + * + * + * + * Clock-val ::= ( Full-clock-val | Partial-clock-val | Timecount-val ) + * Full-clock-val ::= Hours ":" Minutes ":" Seconds ("." Fraction)? + * Partial-clock-val ::= Minutes ":" Seconds ("." Fraction)? + * Timecount-val ::= Timecount ("." Fraction)? (Metric)? + * Metric ::= "h" | "min" | "s" | "ms" + * Hours ::= DIGIT+; any positive number + * Minutes ::= 2DIGIT; range from 00 to 59 + * Seconds ::= 2DIGIT; range from 00 to 59 + * Fraction ::= DIGIT+ + * Timecount ::= DIGIT+ + * 2DIGIT ::= DIGIT DIGIT + * DIGIT ::= [0-9] + * + * + * @param clockValue A String in the representation specified above + * @return A float value in milliseconds that matches the string + * representation given as the parameter + * @exception java.lang.IllegalArgumentException if the clockValue input + * parameter does not comply with the defined syntax + * @exception java.lang.NullPointerException if the clockValue string is + * null + */ + public static float parseClockValue(String clockValue) { + try { + float result = 0; + + // Will throw NullPointerException if clockValue is null + clockValue = clockValue.trim(); + + // Handle first 'Timecount-val' cases with metric + if (clockValue.endsWith("ms")) { + result = parseFloat(clockValue, 2, true); + } else if (clockValue.endsWith("s")) { + result = 1000*parseFloat(clockValue, 1, true); + } else if (clockValue.endsWith("min")) { + result = 60000*parseFloat(clockValue, 3, true); + } else if (clockValue.endsWith("h")) { + result = 3600000*parseFloat(clockValue, 1, true); + } else { + // Handle Timecount-val without metric + try { + return parseFloat(clockValue, 0, true) * 1000; + } catch (NumberFormatException _) { + // Ignore + } + + // Split in {[Hours], Minutes, Seconds} + String[] timeValues = clockValue.split(":"); + + // Read Hours if present and remember location of Minutes + int indexOfMinutes; + if (timeValues.length == 2) { + indexOfMinutes = 0; + } else if (timeValues.length == 3) { + result = 3600000*(int)parseFloat(timeValues[0], 0, false); + indexOfMinutes = 1; + } else { + throw new IllegalArgumentException(); + } + + // Read Minutes + int minutes = (int)parseFloat(timeValues[indexOfMinutes], 0, false); + if ((minutes >= 00) && (minutes <= 59)) { + result += 60000*minutes; + } else { + throw new IllegalArgumentException(); + } + + // Read Seconds + float seconds = parseFloat(timeValues[indexOfMinutes + 1], 0, true); + if ((seconds >= 00) && (seconds < 60)) { + result += 60000*seconds; + } else { + throw new IllegalArgumentException(); + } + + } + return result; + } catch (NumberFormatException e) { + throw new IllegalArgumentException(); + } + } + + /** + * Parse a value formatted as follows: + * + * + * Value ::= Number ("." Decimal)? (Text)? + * Number ::= DIGIT+; any positive number + * Decimal ::= DIGIT+; any positive number + * Text ::= CHAR*; any sequence of chars + * DIGIT ::= [0-9] + * + * @param value The Value to parse + * @param ignoreLast The size of Text to ignore + * @param parseDecimal Whether Decimal is expected + * @return The float value without Text, rounded to 3 digits after '.' + * @throws IllegalArgumentException if Decimal was not expected but encountered + */ + private static float parseFloat(String value, int ignoreLast, boolean parseDecimal) { + // Ignore last characters + value = value.substring(0, value.length() - ignoreLast); + + float result; + int indexOfComma = value.indexOf('.'); + if (indexOfComma != -1) { + if (!parseDecimal) { + throw new IllegalArgumentException("int value contains decimal"); + } + // Ensure that there are at least 3 decimals + value = value + "000"; + // Read value up to 3 decimals and cut the rest + result = Float.parseFloat(value.substring(0, indexOfComma)); + result += Float.parseFloat( + value.substring(indexOfComma + 1, indexOfComma + 4))/1000; + } else { + result = Integer.parseInt(value); + } + + return result; + } + + /* + * Time Interface + */ + + public boolean getBaseBegin() { + // TODO Auto-generated method stub + return false; + } + + public Element getBaseElement() { + // TODO Auto-generated method stub + return null; + } + + public String getEvent() { + // TODO Auto-generated method stub + return null; + } + + public String getMarker() { + // TODO Auto-generated method stub + return null; + } + + public double getOffset() { + // TODO Auto-generated method stub + return 0; + } + + public boolean getResolved() { + return mResolved; + } + + public double getResolvedOffset() { + return mResolvedOffset; + } + + public short getTimeType() { + return mTimeType; + } + + public void setBaseBegin(boolean baseBegin) throws DOMException { + // TODO Auto-generated method stub + + } + + public void setBaseElement(Element baseElement) throws DOMException { + // TODO Auto-generated method stub + + } + + public void setEvent(String event) throws DOMException { + // TODO Auto-generated method stub + + } + + public void setMarker(String marker) throws DOMException { + // TODO Auto-generated method stub + + } + + public void setOffset(double offset) throws DOMException { + // TODO Auto-generated method stub + + } +} diff --git a/src/org/thoughtcrime/securesms/dom/smil/TimeListImpl.java b/src/org/thoughtcrime/securesms/dom/smil/TimeListImpl.java new file mode 100644 index 0000000000..7e809e00e0 --- /dev/null +++ b/src/org/thoughtcrime/securesms/dom/smil/TimeListImpl.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * 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.thoughtcrime.securesms.dom.smil; + +import java.util.ArrayList; + +import org.w3c.dom.smil.Time; +import org.w3c.dom.smil.TimeList; + +public class TimeListImpl implements TimeList { + private final ArrayList mTimes; + + /* + * Internal Interface + */ + TimeListImpl(ArrayList times) { + mTimes = times; + } + + /* + * TimeList Interface + */ + + public int getLength() { + return mTimes.size(); + } + + public Time item(int index) { + Time time = null; + try { + time = mTimes.get(index); + } catch (IndexOutOfBoundsException e) { + // Do nothing and return null + } + return time; + } + +} diff --git a/src/org/thoughtcrime/securesms/dom/smil/parser/SmilXmlSerializer.java b/src/org/thoughtcrime/securesms/dom/smil/parser/SmilXmlSerializer.java new file mode 100644 index 0000000000..52f6391c62 --- /dev/null +++ b/src/org/thoughtcrime/securesms/dom/smil/parser/SmilXmlSerializer.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * Copyright (C) 2007 The Android Open Source Project + * + * 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.thoughtcrime.securesms.dom.smil.parser; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; + +import org.w3c.dom.Attr; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.smil.SMILDocument; +import org.w3c.dom.smil.SMILElement; + +public class SmilXmlSerializer { + public static void serialize(SMILDocument smilDoc, OutputStream out) { + try { + Writer writer = new BufferedWriter(new OutputStreamWriter(out, "UTF-8"), 2048); + + writeElement(writer, smilDoc.getDocumentElement()); + writer.flush(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private static void writeElement(Writer writer, Element element) + throws IOException { + writer.write('<'); + writer.write(element.getTagName()); + + if (element.hasAttributes()) { + NamedNodeMap attributes = element.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) { + Attr attribute = (Attr)attributes.item(i); + writer.write(" " + attribute.getName()); + writer.write("=\"" + attribute.getValue() + "\""); + } + } + + // FIXME: Might throw ClassCastException + SMILElement childElement = (SMILElement) element.getFirstChild(); + + if (childElement != null) { + writer.write('>'); + + do { + writeElement(writer, childElement); + childElement = (SMILElement) childElement.getNextSibling(); + } while (childElement != null); + + writer.write(""); + writer.write(element.getTagName()); + writer.write('>'); + } else { + writer.write("/>"); + } + } +} + diff --git a/src/org/thoughtcrime/securesms/mms/AudioSlide.java b/src/org/thoughtcrime/securesms/mms/AudioSlide.java index f0df498fa8..0284d934f7 100644 --- a/src/org/thoughtcrime/securesms/mms/AudioSlide.java +++ b/src/org/thoughtcrime/securesms/mms/AudioSlide.java @@ -19,16 +19,18 @@ package org.thoughtcrime.securesms.mms; import java.io.IOException; import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.util.SmilUtil; +import org.w3c.dom.smil.SMILDocument; +import org.w3c.dom.smil.SMILMediaElement; +import org.w3c.dom.smil.SMILRegionElement; +import org.w3c.dom.smil.SMILRegionMediaElement; import ws.com.google.android.mms.pdu.PduPart; import android.content.Context; import android.database.Cursor; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.graphics.drawable.Drawable; import android.net.Uri; import android.provider.MediaStore.Audio; -import android.widget.ImageView; public class AudioSlide extends Slide { @@ -49,7 +51,17 @@ public class AudioSlide extends Slide { public boolean hasAudio() { return true; } - + + @Override + public SMILRegionElement getSmilRegion(SMILDocument document) { + return null; + } + + @Override + public SMILMediaElement getMediaElement(SMILDocument document) { + return SmilUtil.createMediaElement("audio", document, new String(getPart().getName())); + } + @Override public Drawable getThumbnail(int maxWidth, int maxHeight) { return context.getResources().getDrawable(R.drawable.ic_menu_add_sound); @@ -81,5 +93,4 @@ public class AudioSlide extends Slide { return part; } - } diff --git a/src/org/thoughtcrime/securesms/mms/ImageSlide.java b/src/org/thoughtcrime/securesms/mms/ImageSlide.java index 16054180fc..c58d2018db 100644 --- a/src/org/thoughtcrime/securesms/mms/ImageSlide.java +++ b/src/org/thoughtcrime/securesms/mms/ImageSlide.java @@ -33,6 +33,10 @@ import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.util.BitmapDecodingException; import org.thoughtcrime.securesms.util.BitmapUtil; import org.thoughtcrime.securesms.util.LRUCache; +import org.thoughtcrime.securesms.util.SmilUtil; +import org.w3c.dom.smil.SMILDocument; +import org.w3c.dom.smil.SMILMediaElement; +import org.w3c.dom.smil.SMILRegionElement; import org.whispersystems.textsecure.crypto.MasterSecret; import java.io.FileNotFoundException; @@ -59,7 +63,7 @@ public class ImageSlide extends Slide { public ImageSlide(Context context, Uri uri) throws IOException, BitmapDecodingException { super(context, constructPartFromUri(context, uri)); } - + @Override public Drawable getThumbnail(int maxWidth, int maxHeight) { Drawable thumbnail = getCachedThumbnail(); @@ -161,12 +165,29 @@ public class ImageSlide extends Slide { public boolean hasImage() { return true; } - + + @Override + public SMILRegionElement getSmilRegion(SMILDocument document) { + SMILRegionElement region = (SMILRegionElement) document.createElement("region"); + region.setId("Image"); + region.setLeft(0); + region.setTop(0); + region.setWidth(SmilUtil.ROOT_WIDTH); + region.setHeight(SmilUtil.ROOT_HEIGHT); + region.setFit("meet"); + return region; + } + + @Override + public SMILMediaElement getMediaElement(SMILDocument document) { + return SmilUtil.createMediaElement("img", document, new String(getPart().getName())); + } + private static PduPart constructPartFromUri(Context context, Uri uri) throws IOException, BitmapDecodingException { PduPart part = new PduPart(); - byte[] data = BitmapUtil.createScaledBytes(context, uri, 640, 480, (300 * 1024) - 5000); + byte[] data = BitmapUtil.createScaledBytes(context, uri, 1280, 1280, MAX_MESSAGE_SIZE); part.setData(data); part.setDataUri(uri); diff --git a/src/org/thoughtcrime/securesms/mms/Slide.java b/src/org/thoughtcrime/securesms/mms/Slide.java index 71467565b0..56c9c23718 100644 --- a/src/org/thoughtcrime/securesms/mms/Slide.java +++ b/src/org/thoughtcrime/securesms/mms/Slide.java @@ -20,6 +20,9 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import org.w3c.dom.smil.SMILDocument; +import org.w3c.dom.smil.SMILMediaElement; +import org.w3c.dom.smil.SMILRegionElement; import org.whispersystems.textsecure.crypto.MasterSecret; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.providers.PartProvider; @@ -36,61 +39,61 @@ import ws.com.google.android.mms.pdu.PduPart; public abstract class Slide { - protected static final int MAX_MESSAGE_SIZE = 1048576; - + protected static final int MAX_MESSAGE_SIZE = 280 * 1024; + protected final PduPart part; protected final Context context; protected MasterSecret masterSecret; - + public Slide(Context context, PduPart part) { this.part = part; this.context = context; } - + public Slide(Context context, MasterSecret masterSecret, PduPart part) { this(context, part); this.masterSecret = masterSecret; } - + public InputStream getPartDataInputStream() throws FileNotFoundException { Uri partUri = part.getDataUri(); - + Log.w("Slide", "Loading Part URI: " + partUri); - + if (PartProvider.isAuthority(partUri)) return DatabaseFactory.getEncryptingPartDatabase(context, masterSecret).getPartStream(ContentUris.parseId(partUri)); else return context.getContentResolver().openInputStream(partUri); } - + protected static long getMediaSize(Context context, Uri uri) throws IOException { InputStream in = context.getContentResolver().openInputStream(uri); - long size = 0; + long size = 0; byte[] buffer = new byte[512]; int read; - + while ((read = in.read(buffer)) != -1) size += read; - + return size; } - + protected byte[] getPartData() { if (part.getData() != null) return part.getData(); - + long partId = ContentUris.parseId(part.getDataUri()); return DatabaseFactory.getEncryptingPartDatabase(context, masterSecret).getPart(partId, true).getData(); } - + public String getContentType() { return new String(part.getContentType()); } - + public Uri getUri() { return part.getDataUri(); } - + public Drawable getThumbnail(int maxWidth, int maxHeight) { throw new AssertionError("getThumbnail() called on non-thumbnail producing slide!"); } @@ -102,28 +105,32 @@ public abstract class Slide { public boolean hasImage() { return false; } - + public boolean hasVideo() { return false; } - + public boolean hasAudio() { return false; } - + public Bitmap getImage() { throw new AssertionError("getImage() called on non-image slide!"); } - + public boolean hasText() { return false; } - + public String getText() { throw new AssertionError("getText() called on non-text slide!"); } - + public PduPart getPart() { return part; } + + public abstract SMILRegionElement getSmilRegion(SMILDocument document); + + public abstract SMILMediaElement getMediaElement(SMILDocument document); } diff --git a/src/org/thoughtcrime/securesms/mms/SlideDeck.java b/src/org/thoughtcrime/securesms/mms/SlideDeck.java index 929bc091a2..b2120a8d25 100644 --- a/src/org/thoughtcrime/securesms/mms/SlideDeck.java +++ b/src/org/thoughtcrime/securesms/mms/SlideDeck.java @@ -18,8 +18,11 @@ package org.thoughtcrime.securesms.mms; import android.content.Context; +import org.thoughtcrime.securesms.dom.smil.parser.SmilXmlSerializer; +import org.thoughtcrime.securesms.util.SmilUtil; import org.whispersystems.textsecure.crypto.MasterSecret; +import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; import java.util.LinkedList; import java.util.List; @@ -27,9 +30,9 @@ import java.util.List; import ws.com.google.android.mms.ContentType; import ws.com.google.android.mms.pdu.CharacterSets; import ws.com.google.android.mms.pdu.PduBody; +import ws.com.google.android.mms.pdu.PduPart; public class SlideDeck { - private final List slides = new LinkedList(); public SlideDeck(Context context, MasterSecret masterSecret, PduBody body) { @@ -44,7 +47,7 @@ public class SlideDeck { slides.add(new AudioSlide(context, body.getPart(i))); else if (ContentType.isTextType(contentType)) slides.add(new TextSlide(context, masterSecret, body.getPart(i))); - } + } } catch (UnsupportedEncodingException uee) { throw new AssertionError(uee); } @@ -52,7 +55,7 @@ public class SlideDeck { public SlideDeck() { } - + public void clear() { slides.clear(); } @@ -63,7 +66,16 @@ public class SlideDeck { for (Slide slide : slides) { body.addPart(slide.getPart()); } - + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + SmilXmlSerializer.serialize(SmilUtil.createSmilDocument(this), out); + PduPart smilPart = new PduPart(); + smilPart.setContentId("smil".getBytes()); + smilPart.setContentLocation("smil.xml".getBytes()); + smilPart.setContentType(ContentType.APP_SMIL.getBytes()); + smilPart.setData(out.toByteArray()); + body.addPart(0, smilPart); + return body; } diff --git a/src/org/thoughtcrime/securesms/mms/TextSlide.java b/src/org/thoughtcrime/securesms/mms/TextSlide.java index 2f2fb1b484..8b3721e4a6 100644 --- a/src/org/thoughtcrime/securesms/mms/TextSlide.java +++ b/src/org/thoughtcrime/securesms/mms/TextSlide.java @@ -20,6 +20,10 @@ import android.content.Context; import android.net.Uri; import android.util.Log; +import org.thoughtcrime.securesms.util.SmilUtil; +import org.w3c.dom.smil.SMILDocument; +import org.w3c.dom.smil.SMILMediaElement; +import org.w3c.dom.smil.SMILRegionElement; import org.whispersystems.textsecure.crypto.MasterSecret; import org.thoughtcrime.securesms.util.LRUCache; @@ -74,7 +78,24 @@ public class TextSlide extends Slide { return new String(getPartData()); } } - + + @Override + public SMILRegionElement getSmilRegion(SMILDocument document) { + SMILRegionElement region = (SMILRegionElement) document.createElement("region"); + region.setId("Text"); + region.setLeft(0); + region.setTop(SmilUtil.ROOT_HEIGHT); + region.setWidth(SmilUtil.ROOT_WIDTH); + region.setHeight(50); + region.setFit("meet"); + return region; + } + + @Override + public SMILMediaElement getMediaElement(SMILDocument document) { + return SmilUtil.createMediaElement("text", document, new String(getPart().getName())); + } + private static PduPart getPartForMessage(String message) { PduPart part = new PduPart(); diff --git a/src/org/thoughtcrime/securesms/mms/VideoSlide.java b/src/org/thoughtcrime/securesms/mms/VideoSlide.java index 5596cd90b5..290735220a 100644 --- a/src/org/thoughtcrime/securesms/mms/VideoSlide.java +++ b/src/org/thoughtcrime/securesms/mms/VideoSlide.java @@ -19,18 +19,19 @@ package org.thoughtcrime.securesms.mms; import java.io.IOException; import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.util.SmilUtil; +import org.w3c.dom.smil.SMILDocument; +import org.w3c.dom.smil.SMILMediaElement; +import org.w3c.dom.smil.SMILRegionElement; import ws.com.google.android.mms.pdu.PduPart; import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.graphics.drawable.Drawable; import android.net.Uri; import android.provider.MediaStore; import android.util.Log; -import android.widget.ImageView; public class VideoSlide extends Slide { @@ -41,7 +42,7 @@ public class VideoSlide extends Slide { public VideoSlide(Context context, Uri uri) throws IOException, MediaTooLargeException { super(context, constructPartFromUri(context, uri)); } - + @Override public Drawable getThumbnail(int width, int height) { return context.getResources().getDrawable(R.drawable.ic_launcher_video_player); @@ -51,12 +52,29 @@ public class VideoSlide extends Slide { public boolean hasImage() { return true; } - + @Override public boolean hasVideo() { return true; } - + + @Override + public SMILRegionElement getSmilRegion(SMILDocument document) { + SMILRegionElement region = (SMILRegionElement) document.createElement("region"); + region.setId("Image"); + region.setLeft(0); + region.setTop(0); + region.setWidth(SmilUtil.ROOT_WIDTH); + region.setHeight(SmilUtil.ROOT_HEIGHT); + region.setFit("meet"); + return region; + } + + @Override + public SMILMediaElement getMediaElement(SMILDocument document) { + return SmilUtil.createMediaElement("video", document, new String(getPart().getName())); + } + private static PduPart constructPartFromUri(Context context, Uri uri) throws IOException, MediaTooLargeException { PduPart part = new PduPart(); ContentResolver resolver = context.getContentResolver(); diff --git a/src/org/thoughtcrime/securesms/util/SmilUtil.java b/src/org/thoughtcrime/securesms/util/SmilUtil.java new file mode 100644 index 0000000000..7e1b1051ed --- /dev/null +++ b/src/org/thoughtcrime/securesms/util/SmilUtil.java @@ -0,0 +1,75 @@ +package org.thoughtcrime.securesms.util; + +import android.util.Log; + +import org.thoughtcrime.securesms.dom.smil.SmilDocumentImpl; +import org.thoughtcrime.securesms.mms.Slide; +import org.thoughtcrime.securesms.mms.SlideDeck; +import org.w3c.dom.smil.SMILDocument; +import org.w3c.dom.smil.SMILElement; +import org.w3c.dom.smil.SMILLayoutElement; +import org.w3c.dom.smil.SMILMediaElement; +import org.w3c.dom.smil.SMILParElement; +import org.w3c.dom.smil.SMILRegionElement; +import org.w3c.dom.smil.SMILRegionMediaElement; +import org.w3c.dom.smil.SMILRootLayoutElement; + +public class SmilUtil { + private static final String TAG = SmilUtil.class.getSimpleName(); + + public static final int ROOT_HEIGHT = 1024; + public static final int ROOT_WIDTH = 1024; + + public static SMILDocument createSmilDocument(SlideDeck deck) { + Log.w(TAG, "Creating SMIL document from SlideDeck."); + + SMILDocument document = new SmilDocumentImpl(); + + SMILElement smilElement = (SMILElement) document.createElement("smil"); + document.appendChild(smilElement); + + SMILElement headElement = (SMILElement) document.createElement("head"); + smilElement.appendChild(headElement); + + SMILLayoutElement layoutElement = (SMILLayoutElement) document.createElement("layout"); + headElement.appendChild(layoutElement); + + SMILRootLayoutElement rootLayoutElement = (SMILRootLayoutElement) document.createElement("root-layout"); + rootLayoutElement.setWidth(ROOT_WIDTH); + rootLayoutElement.setHeight(ROOT_HEIGHT); + layoutElement.appendChild(rootLayoutElement); + + SMILElement bodyElement = (SMILElement) document.createElement("body"); + smilElement.appendChild(bodyElement); + + SMILParElement par = (SMILParElement) document.createElement("par"); + bodyElement.appendChild(par); + + for (Slide slide : deck.getSlides()) { + SMILRegionElement regionElement = slide.getSmilRegion(document); + SMILMediaElement mediaElement = slide.getMediaElement(document); + + if (regionElement != null) { + ((SMILRegionMediaElement)mediaElement).setRegion(regionElement); + layoutElement.appendChild(regionElement); + } + par.appendChild(mediaElement); + } + + return document; + } + + public static SMILMediaElement createMediaElement(String tag, SMILDocument document, String src) { + SMILMediaElement mediaElement = (SMILMediaElement) document.createElement(tag); + mediaElement.setSrc(escapeXML(src)); + return mediaElement; + } + + private static String escapeXML(String str) { + return str.replaceAll("&","&") + .replaceAll("<", "<") + .replaceAll(">", ">") + .replaceAll("\"", """) + .replaceAll("'", "'"); + } +}
Text>
Node
null
HEAD
+ *
+ * Time-val ::= ( smil-1.0-syncbase-value + * | "indefinite" + * | offset-value + * | syncbase-value + * | syncToPrev-value + * | event-value + * | media-marker-value + * | wallclock-sync-value ) + * Smil-1.0-syncbase-value ::= + * "id(" id-ref ")" ( "(" ( "begin" | "end" | clock-value ) ")" )? + * Offset-value ::= ( "+" | "-" )? clock-value + * Syncbase-value ::= ( id-ref "." ( "begin" | "end" ) ) ( ( "+" | "-" ) clock-value )? + * SyncToPrev-value ::= ( "prev.begin" | "prev.end" ) ( ( "+" | "-" ) clock-value )? + * Event-value ::= ( id-ref "." )? ( event-ref ) ( ( "+" | "-" ) clock-value )? + * Media-marker-value ::= id-ref ".marker(" marker-name ")" + * Wallclock-sync-value ::= "wallclock(" wallclock-value ")" + *
+ * Clock values have the following syntax: + *
+ * Clock-val ::= ( Full-clock-val | Partial-clock-val | Timecount-val ) + * Full-clock-val ::= Hours ":" Minutes ":" Seconds ("." Fraction)? + * Partial-clock-val ::= Minutes ":" Seconds ("." Fraction)? + * Timecount-val ::= Timecount ("." Fraction)? (Metric)? + * Metric ::= "h" | "min" | "s" | "ms" + * Hours ::= DIGIT+; any positive number + * Minutes ::= 2DIGIT; range from 00 to 59 + * Seconds ::= 2DIGIT; range from 00 to 59 + * Fraction ::= DIGIT+ + * Timecount ::= DIGIT+ + * 2DIGIT ::= DIGIT DIGIT + * DIGIT ::= [0-9] + *
+ * Value ::= Number ("." Decimal)? (Text)? + * Number ::= DIGIT+; any positive number + * Decimal ::= DIGIT+; any positive number + * Text ::= CHAR*; any sequence of chars + * DIGIT ::= [0-9] + *