Significant MMS changes

1) Remove all our PDU code and switch to the PDU code from the
   klinker library

2) Switch to using the system Lollipop MMS library by default,
   and falling back to our own custom library if that fails.

3) Format SMIL differently, using code from klinker instead of
   what we've pieced together.

4) Pull per-carrier MMS media constraints from the XML config
   files in the klinker library, instead of hardcoding it at 280kb.

Hopefully this is an improvement, but given that MMS is involved,
it will probably make things worse instead.
This commit is contained in:
Moxie Marlinspike
2017-05-08 15:32:59 -07:00
parent 165fae5734
commit 51d6144591
99 changed files with 530 additions and 11696 deletions

View File

@@ -157,8 +157,6 @@ import java.security.SecureRandom;
import java.util.List;
import java.util.concurrent.ExecutionException;
import ws.com.google.android.mms.ContentType;
import static org.thoughtcrime.securesms.TransportOption.Type;
import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
import static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
@@ -1407,8 +1405,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private MediaConstraints getCurrentMediaConstraints() {
return sendButton.getSelectedTransport().getType() == Type.TEXTSECURE
? MediaConstraints.PUSH_CONSTRAINTS
: MediaConstraints.MMS_CONSTRAINTS;
? MediaConstraints.getPushMediaConstraints()
: MediaConstraints.getMmsMediaConstraints(sendButton.getSelectedTransport().getSimSubscriptionId().or(-1));
}
private void markThreadAsRead() {
@@ -1607,7 +1605,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override
public void onImageCapture(@NonNull final byte[] imageBytes) {
setMedia(PersistentBlobProvider.getInstance(this)
.create(masterSecret, imageBytes, ContentType.IMAGE_JPEG),
.create(masterSecret, imageBytes, MediaUtil.IMAGE_JPEG),
MediaType.IMAGE);
quickAttachmentDrawer.hide(false);
}
@@ -1648,7 +1646,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
boolean forceSms = sendButton.isManualSelection() && sendButton.getSelectedTransport().isSms();
int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().or(-1);
long expiresIn = recipients.getExpireMessages() * 1000;
AudioSlide audioSlide = new AudioSlide(ConversationActivity.this, result.first, result.second, ContentType.AUDIO_AAC);
AudioSlide audioSlide = new AudioSlide(ConversationActivity.this, result.first, result.second, MediaUtil.AUDIO_AAC);
SlideDeck slideDeck = new SlideDeck();
slideDeck.addSlide(audioSlide);
@@ -1719,11 +1717,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
public void onMediaSelected(@NonNull Uri uri, String contentType) {
if (!TextUtils.isEmpty(contentType) && contentType.trim().equals("image/gif")) {
setMedia(uri, MediaType.GIF);
} else if (ContentType.isImageType(contentType)) {
} else if (MediaUtil.isImageType(contentType)) {
setMedia(uri, MediaType.IMAGE);
} else if (ContentType.isVideoType(contentType)) {
} else if (MediaUtil.isVideoType(contentType)) {
setMedia(uri, MediaType.VIDEO);
} else if (ContentType.isAudioType(contentType)) {
} else if (MediaUtil.isAudioType(contentType)) {
setMedia(uri, MediaType.AUDIO);
}
}

View File

@@ -20,8 +20,6 @@ import org.thoughtcrime.securesms.util.concurrent.SettableFuture;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import ws.com.google.android.mms.ContentType;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public class AudioRecorder {
@@ -58,7 +56,7 @@ public class AudioRecorder {
captureUri = blobProvider.create(masterSecret,
new ParcelFileDescriptor.AutoCloseInputStream(fds[0]),
ContentType.AUDIO_AAC);
MediaUtil.AUDIO_AAC);
audioCodec = new AudioCodec();
audioCodec.start(new ParcelFileDescriptor.AutoCloseOutputStream(fds[1]));

View File

@@ -36,13 +36,13 @@ import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.AttachmentId;
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
import org.thoughtcrime.securesms.crypto.AsymmetricMasterCipher;
import org.thoughtcrime.securesms.crypto.DecryptingPartInputStream;
import org.thoughtcrime.securesms.crypto.EncryptingPartOutputStream;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecretUnion;
import org.thoughtcrime.securesms.mms.MediaStream;
import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.MediaUtil.ThumbnailData;
@@ -50,8 +50,6 @@ import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.video.EncryptedMediaDataSource;
import org.whispersystems.libsignal.InvalidMessageException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -63,9 +61,6 @@ import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import ws.com.google.android.mms.ContentType;
import ws.com.google.android.mms.MmsException;
public class AttachmentDatabase extends Database {
private static final String TAG = AttachmentDatabase.class.getSimpleName();
@@ -610,7 +605,7 @@ public class AttachmentDatabase extends Database {
ThumbnailData data;
if (ContentType.isVideoType(attachment.getContentType())) {
if (MediaUtil.isVideoType(attachment.getContentType())) {
data = generateVideoThumbnail(masterSecret, attachmentId);
} else{
data = MediaUtil.generateThumbnail(context, masterSecret, attachment.getContentType(), attachment.getDataUri());

View File

@@ -17,15 +17,11 @@
package org.thoughtcrime.securesms.database;
import android.content.ContentValues;
import android.util.Log;
import com.google.android.mms.pdu_alt.EncodedStringValue;
import org.thoughtcrime.securesms.util.Util;
import ws.com.google.android.mms.pdu.CharacterSets;
import ws.com.google.android.mms.pdu.EncodedStringValue;
import java.io.UnsupportedEncodingException;
public class ContentValuesBuilder {
private final ContentValues contentValues;

View File

@@ -33,6 +33,7 @@ import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.InvalidMessageException;
@@ -42,8 +43,6 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import ws.com.google.android.mms.ContentType;
public class DatabaseFactory {
private static final int INTRODUCED_IDENTITIES_VERSION = 2;
@@ -381,7 +380,7 @@ public class DatabaseFactory {
while (partCursor != null && partCursor.moveToNext()) {
String contentType = partCursor.getString(partCursor.getColumnIndexOrThrow("ct"));
if (ContentType.isTextType(contentType)) {
if (MediaUtil.isTextType(contentType)) {
try {
long partId = partCursor.getLong(partCursor.getColumnIndexOrThrow("_id"));
String dataLocation = partCursor.getString(partCursor.getColumnIndexOrThrow("_data"));
@@ -401,9 +400,9 @@ public class DatabaseFactory {
} catch (IOException e) {
Log.w("DatabaseFactory", e);
}
} else if (ContentType.isAudioType(contentType) ||
ContentType.isImageType(contentType) ||
ContentType.isVideoType(contentType))
} else if (MediaUtil.isAudioType(contentType) ||
MediaUtil.isImageType(contentType) ||
MediaUtil.isVideoType(contentType))
{
partCount++;
}

View File

@@ -23,6 +23,8 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.support.annotation.NonNull;
import com.google.android.mms.pdu_alt.PduHeaders;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
@@ -30,8 +32,6 @@ import org.thoughtcrime.securesms.recipients.Recipients;
import java.util.LinkedList;
import java.util.List;
import ws.com.google.android.mms.pdu.PduHeaders;
public class MmsAddressDatabase extends Database {
private static final String TAG = MmsAddressDatabase.class.getSimpleName();

View File

@@ -19,7 +19,6 @@ package org.thoughtcrime.securesms.database;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
@@ -29,6 +28,8 @@ import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import com.google.android.mms.pdu_alt.NotificationInd;
import com.google.android.mms.pdu_alt.PduHeaders;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import org.thoughtcrime.securesms.ApplicationContext;
@@ -77,9 +78,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import ws.com.google.android.mms.MmsException;
import ws.com.google.android.mms.pdu.NotificationInd;
import ws.com.google.android.mms.pdu.PduHeaders;
import org.thoughtcrime.securesms.mms.MmsException;
import static org.thoughtcrime.securesms.util.Util.canonicalizeNumber;
import static org.thoughtcrime.securesms.util.Util.canonicalizeNumberOrGroup;
@@ -593,23 +592,16 @@ public class MmsDatabase extends MessagingDatabase {
return new Pair<>(messageId, threadId);
}
public Optional<Pair<NotificationInd, Integer>> getNotification(long messageId) {
public Optional<MmsNotificationInfo> getNotification(long messageId) {
Cursor cursor = null;
try {
cursor = rawQuery(RAW_ID_WHERE, new String[] {String.valueOf(messageId)});
if (cursor != null && cursor.moveToNext()) {
PduHeaders headers = new PduHeaders();
PduHeadersBuilder builder = new PduHeadersBuilder(headers, cursor);
builder.addText(CONTENT_LOCATION, PduHeaders.CONTENT_LOCATION);
builder.addLong(NORMALIZED_DATE_SENT, PduHeaders.DATE);
builder.addLong(EXPIRY, PduHeaders.EXPIRY);
builder.addLong(MESSAGE_SIZE, PduHeaders.MESSAGE_SIZE);
builder.addText(TRANSACTION_ID, PduHeaders.TRANSACTION_ID);
return Optional.of(new Pair<>(new NotificationInd(headers),
cursor.getInt(cursor.getColumnIndexOrThrow(SUBSCRIPTION_ID))));
return Optional.of(new MmsNotificationInfo(cursor.getString(cursor.getColumnIndexOrThrow(CONTENT_LOCATION)),
cursor.getString(cursor.getColumnIndexOrThrow(TRANSACTION_ID)),
cursor.getInt(cursor.getColumnIndexOrThrow(SUBSCRIPTION_ID))));
} else {
return Optional.absent();
}
@@ -820,22 +812,21 @@ public class MmsDatabase extends MessagingDatabase {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
MmsAddressDatabase addressDatabase = DatabaseFactory.getMmsAddressDatabase(context);
long threadId = getThreadIdFor(notification);
PduHeaders headers = notification.getPduHeaders();
ContentValues contentValues = new ContentValues();
ContentValuesBuilder contentBuilder = new ContentValuesBuilder(contentValues);
Log.w(TAG, "Message received type: " + notification.getMessageType());
Log.w(TAG, "Message received type: " + headers.getOctet(PduHeaders.MESSAGE_TYPE));
contentBuilder.add(CONTENT_LOCATION, headers.getTextString(PduHeaders.CONTENT_LOCATION));
contentBuilder.add(DATE_SENT, headers.getLongInteger(PduHeaders.DATE) * 1000L);
contentBuilder.add(EXPIRY, headers.getLongInteger(PduHeaders.EXPIRY));
contentBuilder.add(MESSAGE_SIZE, headers.getLongInteger(PduHeaders.MESSAGE_SIZE));
contentBuilder.add(TRANSACTION_ID, headers.getTextString(PduHeaders.TRANSACTION_ID));
contentBuilder.add(MESSAGE_TYPE, headers.getOctet(PduHeaders.MESSAGE_TYPE));
contentBuilder.add(CONTENT_LOCATION, notification.getContentLocation());
contentBuilder.add(DATE_SENT, System.currentTimeMillis());
contentBuilder.add(EXPIRY, notification.getExpiry());
contentBuilder.add(MESSAGE_SIZE, notification.getMessageSize());
contentBuilder.add(TRANSACTION_ID, notification.getTransactionId());
contentBuilder.add(MESSAGE_TYPE, notification.getMessageType());
if (headers.getEncodedStringValue(PduHeaders.FROM) != null) {
contentBuilder.add(ADDRESS, headers.getEncodedStringValue(PduHeaders.FROM).getTextString());
if (notification.getFrom() != null) {
contentBuilder.add(ADDRESS, notification.getFrom().getTextString());
} else {
contentBuilder.add(ADDRESS, null);
}
@@ -852,7 +843,7 @@ public class MmsDatabase extends MessagingDatabase {
long messageId = db.insert(TABLE_NAME, null, contentValues);
if (headers.getEncodedStringValue(PduHeaders.FROM) != null) {
if (notification.getFrom() != null) {
addressDatabase.insertAddressesForId(messageId, MmsAddresses.forFrom(Util.toIsoString(notification.getFrom().getTextString())));
}
@@ -1129,6 +1120,30 @@ public class MmsDatabase extends MessagingDatabase {
public static final int DOWNLOAD_APN_UNAVAILABLE = 6;
}
public static class MmsNotificationInfo {
private final String contentLocation;
private final String transactionId;
private final int subscriptionId;
public MmsNotificationInfo(String contentLocation, String transactionId, int subscriptionId) {
this.contentLocation = contentLocation;
this.transactionId = transactionId;
this.subscriptionId = subscriptionId;
}
public String getContentLocation() {
return contentLocation;
}
public String getTransactionId() {
return transactionId;
}
public int getSubscriptionId() {
return subscriptionId;
}
}
public class OutgoingMessageReader {
private final OutgoingMediaMessage message;

View File

@@ -1,80 +0,0 @@
/**
* Copyright (C) 2011 Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.database;
import android.database.Cursor;
import android.util.Log;
import ws.com.google.android.mms.InvalidHeaderValueException;
import ws.com.google.android.mms.pdu.CharacterSets;
import ws.com.google.android.mms.pdu.EncodedStringValue;
import ws.com.google.android.mms.pdu.PduHeaders;
import java.io.UnsupportedEncodingException;
public class PduHeadersBuilder {
private final PduHeaders headers;
private final Cursor cursor;
public PduHeadersBuilder(PduHeaders headers, Cursor cursor) {
this.headers = headers;
this.cursor = cursor;
}
public PduHeaders getHeaders() {
return headers;
}
public void addLong(String key, int headersKey) {
int columnIndex = cursor.getColumnIndexOrThrow(key);
if (!cursor.isNull(columnIndex))
headers.setLongInteger(cursor.getLong(columnIndex), headersKey);
}
public void addOctet(String key, int headersKey) throws InvalidHeaderValueException {
int columnIndex = cursor.getColumnIndexOrThrow(key);
if (!cursor.isNull(columnIndex))
headers.setOctet(cursor.getInt(columnIndex), headersKey);
}
public void addText(String key, int headersKey) {
String value = cursor.getString(cursor.getColumnIndexOrThrow(key));
if (value != null && value.trim().length() > 0)
headers.setTextString(getBytes(value), headersKey);
}
public void add(String key, String charsetKey, int headersKey) {
String value = cursor.getString(cursor.getColumnIndexOrThrow(key));
if (value != null && value.trim().length() > 0) {
int charsetValue = cursor.getInt(cursor.getColumnIndexOrThrow(charsetKey));
EncodedStringValue encodedValue = new EncodedStringValue(charsetValue, getBytes(value));
headers.setEncodedStringValue(encodedValue, headersKey);
}
}
private byte[] getBytes(String data) {
try {
return data.getBytes(CharacterSets.MIMENAME_ISO_8859_1);
} catch (UnsupportedEncodingException e) {
Log.e("PduHeadersBuilder", "ISO_8859_1 must be supported!", e);
return new byte[0];
}
}
}

View File

@@ -1,108 +0,0 @@
/*
* 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 <code>Text></code> 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;
}
}

View File

@@ -1,194 +0,0 @@
/*
* 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);
}
}

View File

@@ -1,172 +0,0 @@
/*
* 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);
}
}

View File

@@ -1,87 +0,0 @@
/*
* 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<Node> mNodes = new Vector<Node>();
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;
}
}

View File

@@ -1,273 +0,0 @@
/*
* 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<Node> mChildNodes = new Vector<Node>();
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<Node> 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<Node> 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;
}
}

View File

@@ -1,128 +0,0 @@
/*
* 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<Node> mSearchNodes;
private ArrayList<Node> 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 <code>Node</code> 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<Node> 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:
* <ul>
* <li> Visit root.
* <li> Traverse children from left to right in preorder.
* </ul>
* 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<Node>();
} 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();
}
}
}

View File

@@ -1,127 +0,0 @@
/*
* 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;
}
}

View File

@@ -1,131 +0,0 @@
/*
* 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<EventListenerEntry> 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<EventListenerEntry>();
}
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;
}
}
}
}

View File

@@ -1,155 +0,0 @@
/*
* 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<Node> activeChildren = new ArrayList<Node>();
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);
}
}

View File

@@ -1,73 +0,0 @@
/*
* 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<Node> nodes = new ArrayList<Node>();
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;
}
}

View File

@@ -1,33 +0,0 @@
/*
* 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);
}
}

View File

@@ -1,348 +0,0 @@
/*
* 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 <code>null</code> 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<Time> beginTimeList = new ArrayList<Time>();
// 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<Time> endTimeList = new ArrayList<Time>();
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");
}
}
}

View File

@@ -1,291 +0,0 @@
/*
* 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 <code>HEAD</code>
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");
}
}
}

View File

@@ -1,47 +0,0 @@
/*
* 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
}
}

View File

@@ -1,63 +0,0 @@
/*
* 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;
}
}

View File

@@ -1,343 +0,0 @@
/*
* 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);
}
}

View File

@@ -1,217 +0,0 @@
/*
* 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<Time> singleTimeContainer = new ArrayList<Time>();
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);
}
}

View File

@@ -1,29 +0,0 @@
/*
* 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);
}
}

View File

@@ -1,283 +0,0 @@
/*
* 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();
}
}

View File

@@ -1,53 +0,0 @@
/*
* 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;
}
}

View File

@@ -1,84 +0,0 @@
/*
* 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;
}
}
}

View File

@@ -1,295 +0,0 @@
/*
* 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:
* <p>
* <pre>
* 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 ")"
* </pre>
*
* @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
* <code>null</code>
*/
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.
* <p>
* Clock values have the following syntax:
* </p>
* <p>
* <pre>
* 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]
* </pre>
*
* @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
* <code>null</code>
*/
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:
* <p>
* <pre>
* Value ::= Number ("." Decimal)? (Text)?
* Number ::= DIGIT+; any positive number
* Decimal ::= DIGIT+; any positive number
* Text ::= CHAR*; any sequence of chars
* DIGIT ::= [0-9]
* </pre>
* @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
}
}

View File

@@ -1,53 +0,0 @@
/*
* 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<Time> mTimes;
/*
* Internal Interface
*/
TimeListImpl(ArrayList<Time> 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;
}
}

View File

@@ -1,80 +0,0 @@
/*
* 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("/>");
}
}
}

View File

@@ -22,6 +22,7 @@ import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
@@ -32,8 +33,6 @@ import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;
import ws.com.google.android.mms.ContentType;
public class GroupManager {
public static @NonNull GroupActionResult createGroup(@NonNull Context context,
@NonNull MasterSecret masterSecret,
@@ -104,7 +103,7 @@ public class GroupManager {
if (avatar != null) {
Uri avatarUri = SingleUseBlobProvider.getInstance().createUri(avatar);
avatarAttachment = new UriAttachment(avatarUri, ContentType.IMAGE_PNG, AttachmentDatabase.TRANSFER_PROGRESS_DONE, avatar.length, null);
avatarAttachment = new UriAttachment(avatarUri, MediaUtil.IMAGE_PNG, AttachmentDatabase.TRANSFER_PROGRESS_DONE, avatar.length, null);
}
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(groupRecipient, groupContext, avatarAttachment, System.currentTimeMillis(), 0);

View File

@@ -37,7 +37,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import ws.com.google.android.mms.MmsException;
import org.thoughtcrime.securesms.mms.MmsException;
import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
import static org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer;

View File

@@ -36,7 +36,7 @@ import java.io.InputStream;
import javax.inject.Inject;
import ws.com.google.android.mms.MmsException;
import org.thoughtcrime.securesms.mms.MmsException;
public class AttachmentDownloadJob extends MasterSecretJob implements InjectableType {
private static final long serialVersionUID = 1L;

View File

@@ -3,7 +3,12 @@ package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.net.Uri;
import android.util.Log;
import android.util.Pair;
import com.google.android.mms.pdu_alt.CharacterSets;
import com.google.android.mms.pdu_alt.EncodedStringValue;
import com.google.android.mms.pdu_alt.PduBody;
import com.google.android.mms.pdu_alt.PduPart;
import com.google.android.mms.pdu_alt.RetrieveConf;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.UriAttachment;
@@ -32,16 +37,12 @@ import org.whispersystems.libsignal.NoSessionException;
import org.whispersystems.libsignal.util.guava.Optional;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import ws.com.google.android.mms.MmsException;
import ws.com.google.android.mms.pdu.EncodedStringValue;
import ws.com.google.android.mms.pdu.NotificationInd;
import ws.com.google.android.mms.pdu.PduBody;
import ws.com.google.android.mms.pdu.PduPart;
import ws.com.google.android.mms.pdu.RetrieveConf;
import org.thoughtcrime.securesms.mms.MmsException;
public class MmsDownloadJob extends MasterSecretJob {
@@ -75,8 +76,8 @@ public class MmsDownloadJob extends MasterSecretJob {
@Override
public void onRun(MasterSecret masterSecret) {
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
Optional<Pair<NotificationInd, Integer>> notification = database.getNotification(messageId);
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
Optional<MmsDatabase.MmsNotificationInfo> notification = database.getNotification(messageId);
if (!notification.isPresent()) {
Log.w(TAG, "No notification for ID: " + messageId);
@@ -84,24 +85,30 @@ public class MmsDownloadJob extends MasterSecretJob {
}
try {
if (notification.get().first.getContentLocation() == null) {
if (notification.get().getContentLocation() == null) {
throw new MmsException("Notification content location was null.");
}
database.markDownloadState(messageId, MmsDatabase.Status.DOWNLOAD_CONNECTING);
String contentLocation = new String(notification.get().first.getContentLocation());
byte[] transactionId = notification.get().first.getTransactionId();
String contentLocation = notification.get().getContentLocation();
byte[] transactionId = new byte[0];
try {
transactionId = notification.get().getTransactionId().getBytes(CharacterSets.MIMENAME_ISO_8859_1);
} catch (UnsupportedEncodingException e) {
Log.w(TAG, e);
}
Log.w(TAG, "Downloading mms at " + Uri.parse(contentLocation).getHost());
RetrieveConf retrieveConf = new CompatMmsConnection(context).retrieve(contentLocation, transactionId, notification.get().second);
RetrieveConf retrieveConf = new CompatMmsConnection(context).retrieve(contentLocation, transactionId, notification.get().getSubscriptionId());
if (retrieveConf == null) {
throw new MmsException("RetrieveConf was null");
}
storeRetrievedMms(masterSecret, contentLocation, messageId, threadId, retrieveConf, notification.get().second);
storeRetrievedMms(masterSecret, contentLocation, messageId, threadId, retrieveConf, notification.get().getSubscriptionId());
} catch (ApnUnavailableException e) {
Log.w(TAG, e);
handleDownloadError(masterSecret, messageId, threadId, MmsDatabase.Status.DOWNLOAD_APN_UNAVAILABLE,

View File

@@ -4,6 +4,11 @@ import android.content.Context;
import android.util.Log;
import android.util.Pair;
import com.google.android.mms.pdu_alt.GenericPdu;
import com.google.android.mms.pdu_alt.NotificationInd;
import com.google.android.mms.pdu_alt.PduHeaders;
import com.google.android.mms.pdu_alt.PduParser;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase;
@@ -12,11 +17,6 @@ import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.jobqueue.JobParameters;
import ws.com.google.android.mms.pdu.GenericPdu;
import ws.com.google.android.mms.pdu.NotificationInd;
import ws.com.google.android.mms.pdu.PduHeaders;
import ws.com.google.android.mms.pdu.PduParser;
public class MmsReceiveJob extends ContextJob {
private static final long serialVersionUID = 1L;

View File

@@ -3,13 +3,27 @@ package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
import android.webkit.MimeTypeMap;
import com.android.mms.dom.smil.parser.SmilXmlSerializer;
import com.google.android.mms.ContentType;
import com.google.android.mms.InvalidHeaderValueException;
import com.google.android.mms.pdu_alt.CharacterSets;
import com.google.android.mms.pdu_alt.EncodedStringValue;
import com.google.android.mms.pdu_alt.PduBody;
import com.google.android.mms.pdu_alt.PduComposer;
import com.google.android.mms.pdu_alt.PduHeaders;
import com.google.android.mms.pdu_alt.PduPart;
import com.google.android.mms.pdu_alt.SendConf;
import com.google.android.mms.pdu_alt.SendReq;
import com.google.android.mms.smil.SmilHelper;
import com.klinker.android.send_message.Utils;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.database.ThreadDatabase.DistributionTypes;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.mms.CompatMmsConnection;
import org.thoughtcrime.securesms.mms.MediaConstraints;
@@ -22,26 +36,16 @@ import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.thoughtcrime.securesms.util.Hex;
import org.thoughtcrime.securesms.util.NumberUtil;
import org.thoughtcrime.securesms.util.SmilUtil;
import org.thoughtcrime.securesms.util.TelephonyUtil;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.NetworkRequirement;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import ws.com.google.android.mms.ContentType;
import ws.com.google.android.mms.MmsException;
import ws.com.google.android.mms.pdu.CharacterSets;
import ws.com.google.android.mms.pdu.EncodedStringValue;
import ws.com.google.android.mms.pdu.PduBody;
import ws.com.google.android.mms.pdu.PduComposer;
import ws.com.google.android.mms.pdu.PduHeaders;
import ws.com.google.android.mms.pdu.PduPart;
import ws.com.google.android.mms.pdu.SendConf;
import ws.com.google.android.mms.pdu.SendReq;
import org.thoughtcrime.securesms.mms.MmsException;
public class MmsSendJob extends SendJob {
@@ -64,8 +68,7 @@ public class MmsSendJob extends SendJob {
@Override
public void onAdded() {
// MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
// database.markAsSending(messageId);
}
@Override
@@ -109,14 +112,6 @@ public class MmsSendJob extends SendJob {
private byte[] getPduBytes(SendReq message)
throws IOException, UndeliverableMessageException, InsecureFallbackApprovalException
{
String number = TelephonyUtil.getManager(context).getLine1Number();
message.setBody(SmilUtil.getSmilBody(message.getBody()));
if (!TextUtils.isEmpty(number)) {
message.setFrom(new EncodedStringValue(number));
}
byte[] pduBytes = new PduComposer(context, message).make();
if (pduBytes == null) {
@@ -174,51 +169,100 @@ public class MmsSendJob extends SendJob {
private SendReq constructSendPdu(MasterSecret masterSecret, OutgoingMediaMessage message)
throws UndeliverableMessageException
{
SendReq sendReq = new SendReq();
PduBody body = new PduBody();
List<String> numbers = message.getRecipients().toNumberStringList(true);
SendReq req = new SendReq();
String lineNumber = Utils.getMyPhoneNumber(context);
List<String> numbers = message.getRecipients().toNumberStringList(true);
MediaConstraints mediaConstraints = MediaConstraints.getMmsMediaConstraints(message.getSubscriptionId());
List<Attachment> scaledAttachments = scaleAttachments(masterSecret, mediaConstraints, message.getAttachments());
for (String number : numbers) {
if (message.getDistributionType() == DistributionTypes.CONVERSATION) {
sendReq.addTo(new EncodedStringValue(Util.toIsoBytes(number)));
} else {
sendReq.addBcc(new EncodedStringValue(Util.toIsoBytes(number)));
}
if (!TextUtils.isEmpty(lineNumber)) {
req.setFrom(new EncodedStringValue(lineNumber));
}
sendReq.setDate(message.getSentTimeMillis() / 1000L);
for (String recipient : numbers) {
req.addTo(new EncodedStringValue(recipient));
}
req.setDate(System.currentTimeMillis() / 1000);
PduBody body = new PduBody();
int size = 0;
if (!TextUtils.isEmpty(message.getBody())) {
PduPart part = new PduPart();
String name = String.valueOf(System.currentTimeMillis());
part.setData(Util.toUtf8Bytes(message.getBody()));
part.setCharset(CharacterSets.UTF_8);
part.setContentType(ContentType.TEXT_PLAIN.getBytes());
part.setContentId((System.currentTimeMillis()+"").getBytes());
part.setName(("Text"+System.currentTimeMillis()).getBytes());
part.setContentId(name.getBytes());
part.setContentLocation((name + ".txt").getBytes());
part.setName((name + ".txt").getBytes());
body.addPart(part);
size += getPartSize(part);
}
List<Attachment> scaledAttachments = scaleAttachments(masterSecret, MediaConstraints.MMS_CONSTRAINTS, message.getAttachments());
for (Attachment attachment : scaledAttachments) {
try {
if (attachment.getDataUri() == null) throw new IOException("Assertion failed, attachment for outgoing MMS has no data!");
PduPart part = new PduPart();
String fileName = attachment.getFileName();
PduPart part = new PduPart();
if (fileName == null) {
fileName = String.valueOf(Math.abs(Util.getSecureRandom().nextLong()));
String fileExtension = MimeTypeMap.getSingleton().getExtensionFromMimeType(attachment.getContentType());
if (fileExtension != null) fileName = fileName + "." + fileExtension;
}
if (attachment.getContentType().startsWith("text")) {
part.setCharset(CharacterSets.UTF_8);
}
part.setContentType(attachment.getContentType().getBytes());
part.setContentLocation(fileName.getBytes());
part.setName(fileName.getBytes());
int index = fileName.lastIndexOf(".");
String contentId = (index == -1) ? fileName : fileName.substring(0, index);
part.setContentId(contentId.getBytes());
part.setData(Util.readFully(PartAuthority.getAttachmentStream(context, masterSecret, attachment.getDataUri())));
part.setContentType(Util.toIsoBytes(attachment.getContentType()));
part.setContentId((System.currentTimeMillis() + "").getBytes());
part.setName((System.currentTimeMillis() + "").getBytes());
body.addPart(part);
size += getPartSize(part);
} catch (IOException e) {
Log.w(TAG, e);
}
}
sendReq.setBody(body);
return sendReq;
ByteArrayOutputStream out = new ByteArrayOutputStream();
SmilXmlSerializer.serialize(SmilHelper.createSmilDocument(body), 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);
req.setBody(body);
req.setMessageSize(size);
req.setMessageClass(PduHeaders.MESSAGE_CLASS_PERSONAL_STR.getBytes());
req.setExpiry(7 * 24 * 60 * 60);
try {
req.setPriority(PduHeaders.PRIORITY_NORMAL);
req.setDeliveryReport(PduHeaders.VALUE_NO);
req.setReadReport(PduHeaders.VALUE_NO);
} catch (InvalidHeaderValueException e) {}
return req;
}
private long getPartSize(PduPart part) {
return part.getName().length + part.getContentLocation().length +
part.getContentType().length + part.getData().length +
part.getContentId().length;
}
private void notifyMediaMessageDeliveryFailed(Context context, long messageId) {

View File

@@ -7,7 +7,6 @@ import android.util.Log;
import android.util.Pair;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
import org.thoughtcrime.securesms.attachments.PointerAttachment;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
@@ -77,11 +76,10 @@ import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptM
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import ws.com.google.android.mms.MmsException;
import org.thoughtcrime.securesms.mms.MmsException;
public class PushDecryptJob extends ContextJob {

View File

@@ -13,6 +13,7 @@ import org.thoughtcrime.securesms.database.documents.NetworkFailure;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.mms.MediaConstraints;
import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.thoughtcrime.securesms.recipients.Recipient;
@@ -40,8 +41,6 @@ import java.util.List;
import javax.inject.Inject;
import ws.com.google.android.mms.MmsException;
import static org.thoughtcrime.securesms.dependencies.SignalCommunicationModule.SignalMessageSenderFactory;
public class PushGroupSendJob extends PushSendJob implements InjectableType {
@@ -139,7 +138,8 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
SignalServiceMessageSender messageSender = messageSenderFactory.create();
byte[] groupId = GroupUtil.getDecodedId(message.getRecipients().getPrimaryRecipient().getNumber());
Recipients recipients = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupId, false);
List<Attachment> scaledAttachments = scaleAttachments(masterSecret, MediaConstraints.PUSH_CONSTRAINTS, message.getAttachments());
MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints();
List<Attachment> scaledAttachments = scaleAttachments(masterSecret, mediaConstraints, message.getAttachments());
List<SignalServiceAttachment> attachmentStreams = getAttachmentsFor(masterSecret, scaledAttachments);
List<SignalServiceAddress> addresses;

View File

@@ -32,7 +32,7 @@ import java.util.List;
import javax.inject.Inject;
import ws.com.google.android.mms.MmsException;
import org.thoughtcrime.securesms.mms.MmsException;
import static org.thoughtcrime.securesms.dependencies.SignalCommunicationModule.SignalMessageSenderFactory;
@@ -119,7 +119,8 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
try {
SignalServiceAddress address = getPushAddress(message.getRecipients().getPrimaryRecipient().getNumber());
List<Attachment> scaledAttachments = scaleAttachments(masterSecret, MediaConstraints.PUSH_CONSTRAINTS, message.getAttachments());
MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints();
List<Attachment> scaledAttachments = scaleAttachments(masterSecret, mediaConstraints, message.getAttachments());
List<SignalServiceAttachment> attachmentStreams = getAttachmentsFor(masterSecret, scaledAttachments);
SignalServiceDataMessage mediaMessage = SignalServiceDataMessage.newBuilder()
.withBody(message.getBody())

View File

@@ -30,8 +30,6 @@ import java.io.InputStream;
import java.util.LinkedList;
import java.util.List;
import ws.com.google.android.mms.ContentType;
public abstract class PushSendJob extends SendJob {
private static final String TAG = PushSendJob.class.getSimpleName();

View File

@@ -7,23 +7,19 @@ import org.thoughtcrime.securesms.BuildConfig;
import org.thoughtcrime.securesms.TextSecureExpiredException;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.mms.MediaConstraints;
import org.thoughtcrime.securesms.mms.MediaStream;
import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.jobqueue.JobParameters;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedList;
import java.util.List;
import ws.com.google.android.mms.ContentType;
import ws.com.google.android.mms.MmsException;
public abstract class SendJob extends MasterSecretJob {
private final static String TAG = SendJob.class.getSimpleName();

View File

@@ -19,8 +19,6 @@ import org.whispersystems.jobqueue.requirements.Requirement;
import java.util.Collections;
import java.util.Set;
import ws.com.google.android.mms.ContentType;
public class MediaNetworkRequirement implements Requirement, ContextDependent {
private static final long serialVersionUID = 0L;
private static final String TAG = MediaNetworkRequirement.class.getSimpleName();
@@ -112,8 +110,8 @@ public class MediaNetworkRequirement implements Requirement, ContextDependent {
private boolean isNonDocumentType(String contentType) {
return
ContentType.isImageType(contentType) ||
ContentType.isVideoType(contentType) ||
ContentType.isAudioType(contentType);
MediaUtil.isImageType(contentType) ||
MediaUtil.isVideoType(contentType) ||
MediaUtil.isAudioType(contentType);
}
}

View File

@@ -35,7 +35,6 @@ import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.bumptech.glide.Glide;
import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
import com.google.android.gms.common.GooglePlayServicesRepairableException;
import com.google.android.gms.location.places.ui.PlacePicker;
@@ -67,7 +66,6 @@ import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import ws.com.google.android.mms.ContentType;
public class AttachmentManager {
@@ -192,7 +190,7 @@ public class AttachmentManager {
public void onSuccess(@NonNull Bitmap result) {
byte[] blob = BitmapUtil.toByteArray(result);
Uri uri = PersistentBlobProvider.getInstance(context)
.create(masterSecret, blob, ContentType.IMAGE_PNG);
.create(masterSecret, blob, MediaUtil.IMAGE_PNG);
LocationSlide locationSlide = new LocationSlide(context, uri, blob.length, place);
setSlide(locationSlide);
@@ -204,7 +202,8 @@ public class AttachmentManager {
public void setMedia(@NonNull final MasterSecret masterSecret,
@NonNull final Uri uri,
@NonNull final MediaType mediaType,
@NonNull final MediaConstraints constraints) {
@NonNull final MediaConstraints constraints)
{
inflateStub();
new AsyncTask<Void, Void, Slide>() {
@@ -350,7 +349,7 @@ public class AttachmentManager {
if (captureIntent.resolveActivity(activity.getPackageManager()) != null) {
if (captureUri == null) {
captureUri = PersistentBlobProvider.getInstance(context)
.createForExternal(ContentType.IMAGE_JPEG);
.createForExternal(MediaUtil.IMAGE_JPEG);
}
Log.w(TAG, "captureUri path is " + captureUri.getPath());
captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, captureUri);
@@ -462,11 +461,11 @@ public class AttachmentManager {
}
public static @Nullable MediaType from(final @Nullable String mimeType) {
if (TextUtils.isEmpty(mimeType)) return null;
if (MediaUtil.isGif(mimeType)) return GIF;
if (ContentType.isImageType(mimeType)) return IMAGE;
if (ContentType.isAudioType(mimeType)) return AUDIO;
if (ContentType.isVideoType(mimeType)) return VIDEO;
if (TextUtils.isEmpty(mimeType)) return null;
if (MediaUtil.isGif(mimeType)) return GIF;
if (MediaUtil.isImageType(mimeType)) return IMAGE;
if (MediaUtil.isAudioType(mimeType)) return AUDIO;
if (MediaUtil.isVideoType(mimeType)) return VIDEO;
return null;
}

View File

@@ -27,17 +27,14 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.UriAttachment;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.ResUtil;
import java.io.IOException;
import ws.com.google.android.mms.ContentType;
import ws.com.google.android.mms.pdu.PduPart;
public class AudioSlide extends Slide {
public AudioSlide(Context context, Uri uri, long dataSize) {
super(context, constructAttachmentFromUri(context, uri, ContentType.AUDIO_UNSPECIFIED, dataSize, false, null));
super(context, constructAttachmentFromUri(context, uri, MediaUtil.AUDIO_UNSPECIFIED, dataSize, false, null));
}
public AudioSlide(Context context, Uri uri, long dataSize, String contentType) {

View File

@@ -1,22 +1,20 @@
package org.thoughtcrime.securesms.mms;
import android.content.Context;
import android.os.Build;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import com.google.android.mms.pdu_alt.PduHeaders;
import com.google.android.mms.pdu_alt.RetrieveConf;
import com.google.android.mms.pdu_alt.SendConf;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import java.io.IOException;
import ws.com.google.android.mms.MmsException;
import ws.com.google.android.mms.pdu.PduHeaders;
import ws.com.google.android.mms.pdu.RetrieveConf;
import ws.com.google.android.mms.pdu.SendConf;
public class CompatMmsConnection implements OutgoingMmsConnection, IncomingMmsConnection {
private static final String TAG = CompatMmsConnection.class.getSimpleName();
@@ -31,7 +29,18 @@ public class CompatMmsConnection implements OutgoingMmsConnection, IncomingMmsCo
public SendConf send(@NonNull byte[] pduBytes, int subscriptionId)
throws UndeliverableMessageException
{
if (subscriptionId == -1 || VERSION.SDK_INT < 22) {
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
try {
Log.w(TAG, "Sending via Lollipop API");
return new OutgoingLollipopMmsConnection(context).send(pduBytes, subscriptionId);
} catch (UndeliverableMessageException e) {
Log.w(TAG, e);
}
}
Log.w(TAG, "Falling back to legacy connection...");
if (subscriptionId == -1) {
Log.w(TAG, "Sending via legacy connection");
try {
SendConf result = new OutgoingLegacyMmsConnection(context).send(pduBytes, subscriptionId);
@@ -46,11 +55,7 @@ public class CompatMmsConnection implements OutgoingMmsConnection, IncomingMmsCo
}
}
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
return new OutgoingLollipopMmsConnection(context).send(pduBytes, subscriptionId);
} else {
throw new UndeliverableMessageException("Lollipop API not available to try...");
}
throw new UndeliverableMessageException("Both lollipop and legacy connections failed...");
}
@Nullable
@@ -60,8 +65,13 @@ public class CompatMmsConnection implements OutgoingMmsConnection, IncomingMmsCo
int subscriptionId)
throws MmsException, MmsRadioException, ApnUnavailableException, IOException
{
if (VERSION.SDK_INT < 22 || subscriptionId == -1) {
Log.w(TAG, "Receiving via legacy connection");
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
Log.w(TAG, "Receiving via Lollipop API");
return new IncomingLollipopMmsConnection(context).retrieve(contentLocation, transactionId, subscriptionId);
}
if (subscriptionId == -1) {
Log.w(TAG, "Falling back to receiving via legacy connection");
try {
return new IncomingLegacyMmsConnection(context).retrieve(contentLocation, transactionId, subscriptionId);
} catch (MmsRadioException | ApnUnavailableException | IOException e) {
@@ -69,11 +79,6 @@ public class CompatMmsConnection implements OutgoingMmsConnection, IncomingMmsCo
}
}
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
Log.w(TAG, "Falling back to try receiving via Lollipop API");
return new IncomingLollipopMmsConnection(context).retrieve(contentLocation, transactionId, subscriptionId);
} else {
throw new IOException("Not able to use Lollipop APIs, giving up...");
}
throw new IOException("Both lollipop and fallback APIs failed...");
}
}

View File

@@ -5,13 +5,7 @@ import android.net.Uri;
import android.support.annotation.Nullable;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import java.io.IOException;
import java.io.InputStream;
import ws.com.google.android.mms.ContentType;
import ws.com.google.android.mms.pdu.PduPart;
import org.thoughtcrime.securesms.util.MediaUtil;
public class GifSlide extends ImageSlide {
@@ -20,7 +14,7 @@ public class GifSlide extends ImageSlide {
}
public GifSlide(Context context, Uri uri, long size) {
super(context, constructAttachmentFromUri(context, uri, ContentType.IMAGE_GIF, size, true, null));
super(context, constructAttachmentFromUri(context, uri, MediaUtil.IMAGE_GIF, size, true, null));
}
@Override

View File

@@ -24,8 +24,7 @@ import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.attachments.Attachment;
import ws.com.google.android.mms.ContentType;
import org.thoughtcrime.securesms.util.MediaUtil;
public class ImageSlide extends Slide {
@@ -36,7 +35,7 @@ public class ImageSlide extends Slide {
}
public ImageSlide(Context context, Uri uri, long size) {
super(context, constructAttachmentFromUri(context, uri, ContentType.IMAGE_JPEG, size, true, null));
super(context, constructAttachmentFromUri(context, uri, MediaUtil.IMAGE_JPEG, size, true, null));
}
@Override

View File

@@ -22,6 +22,13 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import com.google.android.mms.InvalidHeaderValueException;
import com.google.android.mms.pdu_alt.NotifyRespInd;
import com.google.android.mms.pdu_alt.PduComposer;
import com.google.android.mms.pdu_alt.PduHeaders;
import com.google.android.mms.pdu_alt.PduParser;
import com.google.android.mms.pdu_alt.RetrieveConf;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
@@ -31,12 +38,6 @@ import org.apache.http.client.methods.HttpUriRequest;
import java.io.IOException;
import java.util.Arrays;
import ws.com.google.android.mms.InvalidHeaderValueException;
import ws.com.google.android.mms.pdu.NotifyRespInd;
import ws.com.google.android.mms.pdu.PduComposer;
import ws.com.google.android.mms.pdu.PduHeaders;
import ws.com.google.android.mms.pdu.PduParser;
import ws.com.google.android.mms.pdu.RetrieveConf;
@SuppressWarnings("deprecation")
public class IncomingLegacyMmsConnection extends LegacyMmsConnection implements IncomingMmsConnection {

View File

@@ -26,18 +26,16 @@ import android.support.annotation.Nullable;
import android.telephony.SmsManager;
import android.util.Log;
import com.google.android.mms.pdu_alt.PduParser;
import com.google.android.mms.pdu_alt.RetrieveConf;
import org.thoughtcrime.securesms.providers.MmsBodyProvider;
import org.thoughtcrime.securesms.util.Hex;
import org.thoughtcrime.securesms.util.Util;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import ws.com.google.android.mms.MmsException;
import ws.com.google.android.mms.pdu.PduParser;
import ws.com.google.android.mms.pdu.RetrieveConf;
public class IncomingLollipopMmsConnection extends LollipopMmsConnection implements IncomingMmsConnection {
public static final String ACTION = IncomingLollipopMmsConnection.class.getCanonicalName() + "MMS_DOWNLOADED_ACTION";
@@ -89,7 +87,7 @@ public class IncomingLollipopMmsConnection extends LollipopMmsConnection impleme
Util.copy(pointer.getInputStream(), baos);
pointer.close();
Log.w(TAG, baos.size() + "-byte response: " + Hex.dump(baos.toByteArray()));
Log.w(TAG, baos.size() + "-byte response: ");// + Hex.dump(baos.toByteArray()));
return (RetrieveConf) new PduParser(baos.toByteArray()).parse();
} catch (IOException | TimeoutException e) {

View File

@@ -3,11 +3,11 @@ package org.thoughtcrime.securesms.mms;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.google.android.mms.pdu_alt.RetrieveConf;
import java.io.IOException;
import ws.com.google.android.mms.MmsException;
import ws.com.google.android.mms.pdu.RetrieveConf;
public interface IncomingMmsConnection {
@Nullable RetrieveConf retrieve(@NonNull String contentLocation, byte[] transactionId, int subscriptionId) throws MmsException, MmsRadioException, ApnUnavailableException, IOException;
@Nullable
RetrieveConf retrieve(@NonNull String contentLocation, byte[] transactionId, int subscriptionId) throws MmsException, MmsRadioException, ApnUnavailableException, IOException;
}

View File

@@ -18,33 +18,33 @@ import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import ws.com.google.android.mms.ContentType;
public abstract class MediaConstraints {
private static final String TAG = MediaConstraints.class.getSimpleName();
public static MediaConstraints MMS_CONSTRAINTS = new MmsMediaConstraints();
public static MediaConstraints PUSH_CONSTRAINTS = new PushMediaConstraints();
public static MediaConstraints getPushMediaConstraints() {
return new PushMediaConstraints();
}
public static MediaConstraints getMmsMediaConstraints(int subscriptionId) {
return new MmsMediaConstraints(subscriptionId);
}
public abstract int getImageMaxWidth(Context context);
public abstract int getImageMaxHeight(Context context);
public abstract int getImageMaxSize();
public abstract int getImageMaxSize(Context context);
public abstract int getGifMaxSize();
public abstract int getVideoMaxSize();
public abstract int getAudioMaxSize();
public abstract int getDocumentMaxSize();
public abstract int getGifMaxSize(Context context);
public abstract int getVideoMaxSize(Context context);
public abstract int getAudioMaxSize(Context context);
public abstract int getDocumentMaxSize(Context context);
public boolean isSatisfied(@NonNull Context context, @NonNull MasterSecret masterSecret, @NonNull Attachment attachment) {
try {
return (MediaUtil.isGif(attachment) && attachment.getSize() <= getGifMaxSize() && isWithinBounds(context, masterSecret, attachment.getDataUri())) ||
(MediaUtil.isImage(attachment) && attachment.getSize() <= getImageMaxSize() && isWithinBounds(context, masterSecret, attachment.getDataUri())) ||
(MediaUtil.isAudio(attachment) && attachment.getSize() <= getAudioMaxSize()) ||
(MediaUtil.isVideo(attachment) && attachment.getSize() <= getVideoMaxSize()) ||
(MediaUtil.isFile(attachment) && attachment.getSize() <= getDocumentMaxSize());
return (MediaUtil.isGif(attachment) && attachment.getSize() <= getGifMaxSize(context) && isWithinBounds(context, masterSecret, attachment.getDataUri())) ||
(MediaUtil.isImage(attachment) && attachment.getSize() <= getImageMaxSize(context) && isWithinBounds(context, masterSecret, attachment.getDataUri())) ||
(MediaUtil.isAudio(attachment) && attachment.getSize() <= getAudioMaxSize(context)) ||
(MediaUtil.isVideo(attachment) && attachment.getSize() <= getVideoMaxSize(context)) ||
(MediaUtil.isFile(attachment) && attachment.getSize() <= getDocumentMaxSize(context));
} catch (IOException ioe) {
Log.w(TAG, "Failed to determine if media's constraints are satisfied.", ioe);
return false;
@@ -78,7 +78,7 @@ public abstract class MediaConstraints {
try {
// XXX - This is loading everything into memory! We want the send path to be stream-like.
return new MediaStream(new ByteArrayInputStream(BitmapUtil.createScaledBytes(context, new DecryptableUri(masterSecret, attachment.getDataUri()), this)),
ContentType.IMAGE_JPEG);
MediaUtil.IMAGE_JPEG);
} catch (BitmapDecodingException e) {
throw new IOException(e);
}

View File

@@ -0,0 +1,53 @@
package org.thoughtcrime.securesms.mms;
import android.content.Context;
import android.content.res.Configuration;
import android.os.Build;
import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
import com.android.mms.service_alt.MmsConfig;
import org.thoughtcrime.securesms.util.dualsim.SubscriptionInfoCompat;
import org.thoughtcrime.securesms.util.dualsim.SubscriptionManagerCompat;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.HashMap;
import java.util.Map;
public class MmsConfigManager {
private static Map<Integer, MmsConfig> mmsConfigMap = new HashMap<>();
@WorkerThread
public synchronized static @Nullable MmsConfig getMmsConfig(Context context, int subscriptionId) {
if (mmsConfigMap.containsKey(subscriptionId)) {
return mmsConfigMap.get(subscriptionId);
}
MmsConfig loadedConfig = loadMmsConfig(context, subscriptionId);
if (loadedConfig != null) mmsConfigMap.put(subscriptionId, loadedConfig);
return loadedConfig;
}
private static MmsConfig loadMmsConfig(Context context, int subscriptionId) {
if (subscriptionId != -1 && Build.VERSION.SDK_INT >= 24) {
Optional<SubscriptionInfoCompat> subscriptionInfo = new SubscriptionManagerCompat(context).getActiveSubscriptionInfo(subscriptionId);
if (subscriptionInfo.isPresent()) {
Configuration configuration = context.getResources().getConfiguration();
configuration.mcc = subscriptionInfo.get().getMcc();
configuration.mnc = subscriptionInfo.get().getMnc();
Context subcontext = context.createConfigurationContext(configuration);
return new MmsConfig(subcontext, subscriptionId);
}
}
return new MmsConfig(context, subscriptionId);
}
}

View File

@@ -0,0 +1,60 @@
/*
* 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.mms;
/**
* A generic exception that is thrown by the Mms client.
*/
public class MmsException extends Exception {
private static final long serialVersionUID = -7323249827281485390L;
/**
* Creates a new MmsException.
*/
public MmsException() {
super();
}
/**
* Creates a new MmsException with the specified detail message.
*
* @param message the detail message.
*/
public MmsException(String message) {
super(message);
}
/**
* Creates a new MmsException with the specified cause.
*
* @param cause the cause.
*/
public MmsException(Throwable cause) {
super(cause);
}
/**
* Creates a new MmsException with the specified detail message and cause.
*
* @param message the detail message.
* @param cause the cause.
*/
public MmsException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -1,46 +1,78 @@
package org.thoughtcrime.securesms.mms;
import android.content.Context;
import android.os.Bundle;
import org.thoughtcrime.securesms.util.Util;
import com.android.mms.service_alt.MmsConfig;
public class MmsMediaConstraints extends MediaConstraints {
private static final int MAX_IMAGE_DIMEN_LOWMEM = 768;
private static final int MAX_IMAGE_DIMEN = 1024;
public static final int MAX_MESSAGE_SIZE = 280 * 1024;
class MmsMediaConstraints extends MediaConstraints {
private static final int DEFAULT_MAX_IMAGE_DIMEN = 1024;
private static final int DEFAULT_MAX_MESSAGE_SIZE = 280 * 1024;
private final int subscriptionId;
MmsMediaConstraints(int subscriptionId) {
this.subscriptionId = subscriptionId;
}
@Override
public int getImageMaxWidth(Context context) {
return Util.isLowMemory(context) ? MAX_IMAGE_DIMEN_LOWMEM : MAX_IMAGE_DIMEN;
MmsConfig mmsConfig = MmsConfigManager.getMmsConfig(context, subscriptionId);
if (mmsConfig != null) {
MmsConfig.Overridden overridden = new MmsConfig.Overridden(mmsConfig, new Bundle());
return overridden.getMaxImageWidth();
}
return DEFAULT_MAX_IMAGE_DIMEN;
}
@Override
public int getImageMaxHeight(Context context) {
return getImageMaxWidth(context);
MmsConfig mmsConfig = MmsConfigManager.getMmsConfig(context, subscriptionId);
if (mmsConfig != null) {
MmsConfig.Overridden overridden = new MmsConfig.Overridden(mmsConfig, new Bundle());
return overridden.getMaxImageHeight();
}
return DEFAULT_MAX_IMAGE_DIMEN;
}
@Override
public int getImageMaxSize() {
return MAX_MESSAGE_SIZE;
public int getImageMaxSize(Context context) {
return getMaxMessageSize(context);
}
@Override
public int getGifMaxSize() {
return MAX_MESSAGE_SIZE;
public int getGifMaxSize(Context context) {
return getMaxMessageSize(context);
}
@Override
public int getVideoMaxSize() {
return MAX_MESSAGE_SIZE;
public int getVideoMaxSize(Context context) {
return getMaxMessageSize(context);
}
@Override
public int getAudioMaxSize() {
return MAX_MESSAGE_SIZE;
public int getAudioMaxSize(Context context) {
return getMaxMessageSize(context);
}
@Override
public int getDocumentMaxSize() {
return MAX_MESSAGE_SIZE;
public int getDocumentMaxSize(Context context) {
return getMaxMessageSize(context);
}
private int getMaxMessageSize(Context context) {
MmsConfig mmsConfig = MmsConfigManager.getMmsConfig(context, subscriptionId);
if (mmsConfig != null) {
MmsConfig.Overridden overridden = new MmsConfig.Overridden(mmsConfig, new Bundle());
return overridden.getMaxMessageSize();
}
return DEFAULT_MAX_MESSAGE_SIZE;
}
}

View File

@@ -24,6 +24,9 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import com.google.android.mms.pdu_alt.PduParser;
import com.google.android.mms.pdu_alt.SendConf;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
@@ -34,8 +37,6 @@ import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import java.io.IOException;
import ws.com.google.android.mms.pdu.PduParser;
import ws.com.google.android.mms.pdu.SendConf;
@SuppressWarnings("deprecation")
public class OutgoingLegacyMmsConnection extends LegacyMmsConnection implements OutgoingMmsConnection {

View File

@@ -21,11 +21,16 @@ import android.content.Context;
import android.content.Intent;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.telephony.SmsManager;
import android.util.Log;
import com.android.mms.service_alt.MmsConfig;
import com.google.android.mms.pdu_alt.PduParser;
import com.google.android.mms.pdu_alt.SendConf;
import org.thoughtcrime.securesms.providers.MmsBodyProvider;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.thoughtcrime.securesms.util.Util;
@@ -34,9 +39,6 @@ import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import ws.com.google.android.mms.pdu.PduParser;
import ws.com.google.android.mms.pdu.SendConf;
public class OutgoingLollipopMmsConnection extends LollipopMmsConnection implements OutgoingMmsConnection {
private static final String TAG = OutgoingLollipopMmsConnection.class.getSimpleName();
private static final String ACTION = OutgoingLollipopMmsConnection.class.getCanonicalName() + "MMS_SENT_ACTION";
@@ -75,10 +77,21 @@ public class OutgoingLollipopMmsConnection extends LollipopMmsConnection impleme
smsManager = SmsManager.getDefault();
}
Bundle configOverrides = new Bundle();
configOverrides.putBoolean(SmsManager.MMS_CONFIG_GROUP_MMS_ENABLED, true);
MmsConfig mmsConfig = MmsConfigManager.getMmsConfig(getContext(), subscriptionId);
if (mmsConfig != null) {
MmsConfig.Overridden overridden = new MmsConfig.Overridden(mmsConfig, new Bundle());
configOverrides.putString(SmsManager.MMS_CONFIG_HTTP_PARAMS, overridden.getHttpParams());
configOverrides.putInt(SmsManager.MMS_CONFIG_MAX_MESSAGE_SIZE, overridden.getMaxMessageSize());
}
smsManager.sendMultimediaMessage(getContext(),
pointer.getUri(),
null,
null,
configOverrides,
getPendingIntent());
waitForResult();

View File

@@ -3,10 +3,12 @@ package org.thoughtcrime.securesms.mms;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.google.android.mms.pdu_alt.SendConf;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import ws.com.google.android.mms.pdu.SendConf;
public interface OutgoingMmsConnection {
@Nullable SendConf send(@NonNull byte[] pduBytes, int subscriptionId) throws UndeliverableMessageException;
@Nullable
SendConf send(@NonNull byte[] pduBytes, int subscriptionId) throws UndeliverableMessageException;
}

View File

@@ -1,14 +1,10 @@
package org.thoughtcrime.securesms.mms;
import android.content.Context;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.recipients.Recipients;
import java.util.List;
import ws.com.google.android.mms.pdu.PduBody;
public class OutgoingSecureMediaMessage extends OutgoingMediaMessage {
public OutgoingSecureMediaMessage(Recipients recipients, String body,

View File

@@ -2,14 +2,15 @@ package org.thoughtcrime.securesms.mms;
import android.util.Log;
import com.google.android.mms.ContentType;
import com.google.android.mms.pdu_alt.CharacterSets;
import com.google.android.mms.pdu_alt.PduBody;
import com.google.android.mms.pdu_alt.PduPart;
import org.thoughtcrime.securesms.util.Util;
import java.io.UnsupportedEncodingException;
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 PartParser {
public static String getMessageText(PduBody body) {

View File

@@ -5,6 +5,7 @@ import android.content.Context;
import org.thoughtcrime.securesms.util.Util;
public class PushMediaConstraints extends MediaConstraints {
private static final int MAX_IMAGE_DIMEN_LOWMEM = 768;
private static final int MAX_IMAGE_DIMEN = 4096;
private static final int KB = 1024;
@@ -21,27 +22,27 @@ public class PushMediaConstraints extends MediaConstraints {
}
@Override
public int getImageMaxSize() {
public int getImageMaxSize(Context context) {
return 6 * MB;
}
@Override
public int getGifMaxSize() {
public int getGifMaxSize(Context context) {
return 25 * MB;
}
@Override
public int getVideoMaxSize() {
public int getVideoMaxSize(Context context) {
return 100 * MB;
}
@Override
public int getAudioMaxSize() {
public int getAudioMaxSize(Context context) {
return 100 * MB;
}
@Override
public int getDocumentMaxSize() {
public int getDocumentMaxSize(Context context) {
return 100 * MB;
}
}

View File

@@ -27,12 +27,10 @@ import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.ResUtil;
import ws.com.google.android.mms.ContentType;
public class VideoSlide extends Slide {
public VideoSlide(Context context, Uri uri, long dataSize) {
super(context, constructAttachmentFromUri(context, uri, ContentType.VIDEO_UNSPECIFIED, dataSize, MediaUtil.hasVideoThumbnail(uri), null));
super(context, constructAttachmentFromUri(context, uri, MediaUtil.VIDEO_UNSPECIFIED, dataSize, MediaUtil.hasVideoThumbnail(uri), null));
}
public VideoSlide(Context context, Attachment attachment) {

View File

@@ -28,14 +28,13 @@ import org.thoughtcrime.securesms.scribbles.widget.VerticalSlideColorPicker;
import org.thoughtcrime.securesms.scribbles.widget.entity.ImageEntity;
import org.thoughtcrime.securesms.scribbles.widget.entity.MotionEntity;
import org.thoughtcrime.securesms.scribbles.widget.entity.TextEntity;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import ws.com.google.android.mms.ContentType;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public class ScribbleActivity extends PassphraseRequiredActionBarActivity implements ScribbleToolbar.ScribbleToolbarListener, VerticalSlideColorPicker.OnColorChangeListener {
@@ -230,7 +229,7 @@ public class ScribbleActivity extends PassphraseRequiredActionBarActivity implem
baos = null;
result = null;
Uri uri = provider.create(masterSecret, data, ContentType.IMAGE_JPEG);
Uri uri = provider.create(masterSecret, data, MediaUtil.IMAGE_JPEG);
Intent intent = new Intent();
intent.setData(uri);
setResult(RESULT_OK, intent);

View File

@@ -52,7 +52,7 @@ import org.whispersystems.signalservice.api.util.InvalidNumberException;
import java.io.IOException;
import ws.com.google.android.mms.MmsException;
import org.thoughtcrime.securesms.mms.MmsException;
public class MessageSender {

View File

@@ -72,14 +72,14 @@ public class BitmapUtil {
Log.w(TAG, "iteration with quality " + quality + " size " + (bytes.length / 1024) + "kb");
if (quality == MIN_COMPRESSION_QUALITY) break;
int nextQuality = (int)Math.floor(quality * Math.sqrt((double)constraints.getImageMaxSize() / bytes.length));
int nextQuality = (int)Math.floor(quality * Math.sqrt((double)constraints.getImageMaxSize(context) / bytes.length));
if (quality - nextQuality < MIN_COMPRESSION_QUALITY_DECREASE) {
nextQuality = quality - MIN_COMPRESSION_QUALITY_DECREASE;
}
quality = Math.max(nextQuality, MIN_COMPRESSION_QUALITY);
}
while (bytes.length > constraints.getImageMaxSize() && attempts++ < MAX_COMPRESSION_ATTEMPTS);
if (bytes.length > constraints.getImageMaxSize()) {
while (bytes.length > constraints.getImageMaxSize(context) && attempts++ < MAX_COMPRESSION_ATTEMPTS);
if (bytes.length > constraints.getImageMaxSize(context)) {
throw new BitmapDecodingException("Unable to scale image below: " + bytes.length);
}
Log.w(TAG, "createScaledBytes(" + model.toString() + ") -> quality " + Math.min(quality, MAX_COMPRESSION_QUALITY) + ", " + attempts + " attempt(s)");

View File

@@ -31,19 +31,25 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.ExecutionException;
import ws.com.google.android.mms.ContentType;
public class MediaUtil {
private static final String TAG = MediaUtil.class.getSimpleName();
public static final String IMAGE_PNG = "image/png";
public static final String IMAGE_JPEG = "image/jpeg";
public static final String IMAGE_GIF = "image/gif";
public static final String AUDIO_AAC = "audio/aac";
public static final String AUDIO_UNSPECIFIED = "audio/*";
public static final String VIDEO_UNSPECIFIED = "video/*";
public static @Nullable ThumbnailData generateThumbnail(Context context, MasterSecret masterSecret, String contentType, Uri uri)
throws BitmapDecodingException
{
long startMillis = System.currentTimeMillis();
ThumbnailData data = null;
if (ContentType.isImageType(contentType)) {
if (isImageType(contentType)) {
data = new ThumbnailData(generateImageThumbnail(context, masterSecret, uri));
}
@@ -77,11 +83,11 @@ public class MediaUtil {
Slide slide = null;
if (isGif(attachment.getContentType())) {
slide = new GifSlide(context, attachment);
} else if (ContentType.isImageType(attachment.getContentType())) {
} else if (isImageType(attachment.getContentType())) {
slide = new ImageSlide(context, attachment);
} else if (ContentType.isVideoType(attachment.getContentType())) {
} else if (isVideoType(attachment.getContentType())) {
slide = new VideoSlide(context, attachment);
} else if (ContentType.isAudioType(attachment.getContentType())) {
} else if (isAudioType(attachment.getContentType())) {
slide = new AudioSlide(context, attachment);
} else if (isMms(attachment.getContentType())) {
slide = new MmsSlide(context, attachment);
@@ -112,8 +118,8 @@ public class MediaUtil {
switch(mimeType) {
case "image/jpg":
return MimeTypeMap.getSingleton().hasMimeType(ContentType.IMAGE_JPEG)
? ContentType.IMAGE_JPEG
return MimeTypeMap.getSingleton().hasMimeType(IMAGE_JPEG)
? IMAGE_JPEG
: mimeType;
default:
return mimeType;
@@ -140,34 +146,50 @@ public class MediaUtil {
return !TextUtils.isEmpty(contentType) && contentType.trim().equals("application/mms");
}
public static boolean isGif(String contentType) {
return !TextUtils.isEmpty(contentType) && contentType.trim().equals("image/gif");
}
public static boolean isGif(Attachment attachment) {
return isGif(attachment.getContentType());
}
public static boolean isImage(Attachment attachment) {
return ContentType.isImageType(attachment.getContentType());
return isImageType(attachment.getContentType());
}
public static boolean isAudio(Attachment attachment) {
return ContentType.isAudioType(attachment.getContentType());
return isAudioType(attachment.getContentType());
}
public static boolean isVideo(Attachment attachment) {
return ContentType.isVideoType(attachment.getContentType());
return isVideoType(attachment.getContentType());
}
public static boolean isVideo(String contentType) {
return !TextUtils.isEmpty(contentType) && contentType.trim().startsWith("video/");
}
public static boolean isGif(String contentType) {
return !TextUtils.isEmpty(contentType) && contentType.trim().equals("image/gif");
}
public static boolean isFile(Attachment attachment) {
return !isGif(attachment) && !isImage(attachment) && !isAudio(attachment) && !isVideo(attachment);
}
public static boolean isTextType(String contentType) {
return (null != contentType) && contentType.startsWith("text/");
}
public static boolean isImageType(String contentType) {
return (null != contentType) && contentType.startsWith("image/");
}
public static boolean isAudioType(String contentType) {
return (null != contentType) && contentType.startsWith("audio/");
}
public static boolean isVideoType(String contentType) {
return (null != contentType) && contentType.startsWith("video/");
}
public static boolean hasVideoThumbnail(Uri uri) {
Log.w(TAG, "Checking: " + uri);

View File

@@ -1,132 +0,0 @@
package org.thoughtcrime.securesms.util;
import android.util.Log;
import org.thoughtcrime.securesms.dom.smil.SmilDocumentImpl;
import org.thoughtcrime.securesms.dom.smil.parser.SmilXmlSerializer;
import org.thoughtcrime.securesms.mms.PartParser;
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;
import java.io.ByteArrayOutputStream;
import ws.com.google.android.mms.ContentType;
import ws.com.google.android.mms.pdu.PduBody;
import ws.com.google.android.mms.pdu.PduPart;
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 PduBody getSmilBody(PduBody body) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
SmilXmlSerializer.serialize(SmilUtil.createSmilDocument(body), 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;
}
private static SMILDocument createSmilDocument(PduBody body) {
Log.w(TAG, "Creating SMIL document from PduBody.");
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 (int i=0; i<body.getPartsNum(); i++) {
PduPart part = body.getPart(i);
SMILRegionElement regionElement = getRegion(document, part);
SMILMediaElement mediaElement = getMediaElement(document, part);
if (regionElement != null) {
((SMILRegionMediaElement)mediaElement).setRegion(regionElement);
layoutElement.appendChild(regionElement);
}
par.appendChild(mediaElement);
}
return document;
}
private static SMILRegionElement getRegion(SMILDocument document, PduPart part) {
if (PartParser.isAudio(part)) return null;
SMILRegionElement region = (SMILRegionElement) document.createElement("region");
if (PartParser.isText(part)) {
region.setId("Text");
region.setTop(SmilUtil.ROOT_HEIGHT);
region.setHeight(50);
} else {
region.setId("Image");
region.setTop(0);
region.setHeight(SmilUtil.ROOT_HEIGHT);
}
region.setLeft(0);
region.setWidth(SmilUtil.ROOT_WIDTH);
region.setFit("meet");
return region;
}
private static SMILMediaElement getMediaElement(SMILDocument document, PduPart part) {
final String tag;
if (PartParser.isImage(part)) {
tag = "img";
} else if (PartParser.isAudio(part)) {
tag = "audio";
} else if (PartParser.isVideo(part)) {
tag = "video";
} else if (PartParser.isText(part)) {
tag = "text";
} else {
tag = "ref";
}
return createMediaElement(tag, document, new String(part.getName() == null
? new byte[]{}
: part.getName()));
}
private 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("&","&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll("\"", "&quot;")
.replaceAll("'", "&apos;");
}
}

View File

@@ -40,6 +40,8 @@ import android.text.style.StyleSpan;
import android.util.Log;
import android.widget.EditText;
import com.google.android.mms.pdu_alt.CharacterSets;
import com.google.android.mms.pdu_alt.EncodedStringValue;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import org.thoughtcrime.securesms.BuildConfig;
@@ -65,9 +67,6 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import ws.com.google.android.mms.pdu.CharacterSets;
import ws.com.google.android.mms.pdu.EncodedStringValue;
public class Util {
private static final String TAG = Util.class.getSimpleName();

View File

@@ -6,11 +6,15 @@ import android.support.annotation.Nullable;
public class SubscriptionInfoCompat {
private final int subscriptionId;
private final int mcc;
private final int mnc;
private final @Nullable CharSequence displayName;
public SubscriptionInfoCompat(int subscriptionId, @Nullable CharSequence displayName) {
public SubscriptionInfoCompat(int subscriptionId, @Nullable CharSequence displayName, int mcc, int mnc) {
this.subscriptionId = subscriptionId;
this.displayName = displayName;
this.mcc = mcc;
this.mnc = mnc;
}
public @NonNull CharSequence getDisplayName() {
@@ -20,4 +24,12 @@ public class SubscriptionInfoCompat {
public int getSubscriptionId() {
return subscriptionId;
}
public int getMnc() {
return mnc;
}
public int getMcc() {
return mcc;
}
}

View File

@@ -35,7 +35,8 @@ public class SubscriptionManagerCompat {
SubscriptionInfo subscriptionInfo = SubscriptionManager.from(context).getActiveSubscriptionInfo(subscriptionId);
if (subscriptionInfo != null) {
return Optional.of(new SubscriptionInfoCompat(subscriptionId, subscriptionInfo.getDisplayName()));
return Optional.of(new SubscriptionInfoCompat(subscriptionId, subscriptionInfo.getDisplayName(),
subscriptionInfo.getMcc(), subscriptionInfo.getMnc()));
} else {
return Optional.absent();
}
@@ -56,7 +57,9 @@ public class SubscriptionManagerCompat {
for (SubscriptionInfo subscriptionInfo : subscriptionInfos) {
compatList.add(new SubscriptionInfoCompat(subscriptionInfo.getSubscriptionId(),
subscriptionInfo.getDisplayName()));
subscriptionInfo.getDisplayName(),
subscriptionInfo.getMcc(),
subscriptionInfo.getMnc()));
}
return compatList;