Add progress listener for attachments.

// FREEBIE
This commit is contained in:
Moxie Marlinspike 2015-06-25 16:04:16 -07:00
parent 64833318da
commit e7f1c52eb2
9 changed files with 76 additions and 43 deletions

View File

@ -18,6 +18,8 @@ package org.whispersystems.textsecure.api;
import org.whispersystems.libaxolotl.InvalidMessageException;
import org.whispersystems.textsecure.api.crypto.AttachmentCipherInputStream;
import org.whispersystems.textsecure.api.messages.TextSecureAttachment;
import org.whispersystems.textsecure.api.messages.TextSecureAttachment.ProgressListener;
import org.whispersystems.textsecure.api.messages.TextSecureAttachmentPointer;
import org.whispersystems.textsecure.api.messages.TextSecureDataMessage;
import org.whispersystems.textsecure.api.messages.TextSecureEnvelope;
@ -88,10 +90,10 @@ public class TextSecureMessageReceiver {
* @throws IOException
* @throws InvalidMessageException
*/
public InputStream retrieveAttachment(TextSecureAttachmentPointer pointer, File destination)
public InputStream retrieveAttachment(TextSecureAttachmentPointer pointer, File destination, ProgressListener listener)
throws IOException, InvalidMessageException
{
socket.retrieveAttachment(pointer.getRelay().orNull(), pointer.getId(), destination);
socket.retrieveAttachment(pointer.getRelay().orNull(), pointer.getId(), destination, listener);
return new AttachmentCipherInputStream(destination, pointer.getKey());
}

View File

@ -335,6 +335,7 @@ public class TextSecureMessageSender {
PushAttachmentData attachmentData = new PushAttachmentData(attachment.getContentType(),
attachment.getInputStream(),
attachment.getLength(),
attachment.getListener(),
attachmentKey);
long attachmentId = socket.sendAttachment(attachmentData);

View File

@ -47,9 +47,10 @@ public abstract class TextSecureAttachment {
public static class Builder {
private InputStream inputStream;
private String contentType;
private long length;
private InputStream inputStream;
private String contentType;
private long length;
private ProgressListener listener;
private Builder() {}
@ -68,12 +69,21 @@ public abstract class TextSecureAttachment {
return this;
}
public Builder withListener(ProgressListener listener) {
this.listener = listener;
return this;
}
public TextSecureAttachmentStream build() {
if (inputStream == null) throw new IllegalArgumentException("Must specify stream!");
if (contentType == null) throw new IllegalArgumentException("No content type specified!");
if (length == 0) throw new IllegalArgumentException("No length specified!");
return new TextSecureAttachmentStream(inputStream, contentType, length);
return new TextSecureAttachmentStream(inputStream, contentType, length, listener);
}
}
public interface ProgressListener {
public void onAttachmentProgress(long total, long progress);
}
}

View File

@ -23,13 +23,15 @@ import java.io.InputStream;
*/
public class TextSecureAttachmentStream extends TextSecureAttachment {
private final InputStream inputStream;
private final long length;
private final InputStream inputStream;
private final long length;
private final ProgressListener listener;
public TextSecureAttachmentStream(InputStream inputStream, String contentType, long length) {
public TextSecureAttachmentStream(InputStream inputStream, String contentType, long length, ProgressListener listener) {
super(contentType);
this.inputStream = inputStream;
this.length = length;
this.listener = listener;
}
@Override
@ -49,4 +51,8 @@ public class TextSecureAttachmentStream extends TextSecureAttachment {
public long getLength() {
return length;
}
public ProgressListener getListener() {
return listener;
}
}

View File

@ -29,7 +29,7 @@ public class DeviceContactsInputStream extends ChunkedInputStream {
InputStream avatarStream = new LimitedInputStream(in, avatarLength);
String avatarContentType = details.getAvatar().getContentType();
avatar = Optional.of(new TextSecureAttachmentStream(avatarStream, avatarContentType, avatarLength));
avatar = Optional.of(new TextSecureAttachmentStream(avatarStream, avatarContentType, avatarLength, null));
}
return new DeviceContact(number, name, avatar);

View File

@ -36,7 +36,7 @@ public class DeviceGroupsInputStream extends ChunkedInputStream{
InputStream avatarStream = new ChunkedInputStream.LimitedInputStream(in, avatarLength);
String avatarContentType = details.getAvatar().getContentType();
avatar = Optional.of(new TextSecureAttachmentStream(avatarStream, avatarContentType, avatarLength));
avatar = Optional.of(new TextSecureAttachmentStream(avatarStream, avatarContentType, avatarLength, null));
}
return new DeviceGroup(id, name, members, avatar);

View File

@ -16,20 +16,26 @@
*/
package org.whispersystems.textsecure.internal.push;
import org.whispersystems.textsecure.api.messages.TextSecureAttachment.ProgressListener;
import java.io.InputStream;
public class PushAttachmentData {
private final String contentType;
private final InputStream data;
private final long dataSize;
private final byte[] key;
private final String contentType;
private final InputStream data;
private final long dataSize;
private final byte[] key;
private final ProgressListener listener;
public PushAttachmentData(String contentType, InputStream data, long dataSize, byte[] key) {
public PushAttachmentData(String contentType, InputStream data, long dataSize,
ProgressListener listener, byte[] key)
{
this.contentType = contentType;
this.data = data;
this.dataSize = dataSize;
this.key = key;
this.listener = listener;
}
public String getContentType() {
@ -47,4 +53,8 @@ public class PushAttachmentData {
public byte[] getKey() {
return key;
}
public ProgressListener getListener() {
return listener;
}
}

View File

@ -27,10 +27,11 @@ import org.whispersystems.libaxolotl.state.PreKeyRecord;
import org.whispersystems.libaxolotl.state.SignedPreKeyRecord;
import org.whispersystems.libaxolotl.util.guava.Optional;
import org.whispersystems.textsecure.api.crypto.AttachmentCipherOutputStream;
import org.whispersystems.textsecure.api.messages.TextSecureAttachment.ProgressListener;
import org.whispersystems.textsecure.api.messages.multidevice.DeviceInfo;
import org.whispersystems.textsecure.api.push.ContactTokenDetails;
import org.whispersystems.textsecure.api.push.TextSecureAddress;
import org.whispersystems.textsecure.api.push.SignedPreKeyEntity;
import org.whispersystems.textsecure.api.push.TextSecureAddress;
import org.whispersystems.textsecure.api.push.TrustStore;
import org.whispersystems.textsecure.api.push.exceptions.AuthorizationFailedException;
import org.whispersystems.textsecure.api.push.exceptions.ExpectationFailedException;
@ -335,12 +336,12 @@ public class PushServiceSocket {
Log.w(TAG, "Got attachment content location: " + attachmentKey.getLocation());
uploadAttachment("PUT", attachmentKey.getLocation(), attachment.getData(),
attachment.getDataSize(), attachment.getKey());
attachment.getDataSize(), attachment.getKey(), attachment.getListener());
return attachmentKey.getId();
}
public void retrieveAttachment(String relay, long attachmentId, File destination) throws IOException {
public void retrieveAttachment(String relay, long attachmentId, File destination, ProgressListener listener) throws IOException {
String path = String.format(ATTACHMENT_PATH, String.valueOf(attachmentId));
if (!Util.isEmpty(relay)) {
@ -352,7 +353,7 @@ public class PushServiceSocket {
Log.w(TAG, "Attachment: " + attachmentId + " is at: " + descriptor.getLocation());
downloadExternalFile(descriptor.getLocation(), destination);
downloadExternalFile(descriptor.getLocation(), destination, listener);
}
public List<ContactTokenDetails> retrieveDirectory(Set<String> contactTokens)
@ -374,7 +375,7 @@ public class PushServiceSocket {
}
}
private void downloadExternalFile(String url, File localDestination)
private void downloadExternalFile(String url, File localDestination, ProgressListener listener)
throws IOException
{
URL downloadUrl = new URL(url);
@ -388,13 +389,19 @@ public class PushServiceSocket {
throw new NonSuccessfulResponseCodeException("Bad response: " + connection.getResponseCode());
}
OutputStream output = new FileOutputStream(localDestination);
InputStream input = connection.getInputStream();
byte[] buffer = new byte[4096];
int read;
OutputStream output = new FileOutputStream(localDestination);
InputStream input = connection.getInputStream();
byte[] buffer = new byte[4096];
int contentLength = connection.getContentLength();
int read,totalRead = 0;
while ((read = input.read(buffer)) != -1) {
output.write(buffer, 0, read);
totalRead += read;
if (listener != null) {
listener.onAttachmentProgress(contentLength, totalRead);
}
}
output.close();
@ -406,7 +413,8 @@ public class PushServiceSocket {
}
}
private void uploadAttachment(String method, String url, InputStream data, long dataSize, byte[] key)
private void uploadAttachment(String method, String url, InputStream data,
long dataSize, byte[] key, ProgressListener listener)
throws IOException
{
URL uploadUrl = new URL(url);
@ -427,9 +435,21 @@ public class PushServiceSocket {
try {
OutputStream stream = connection.getOutputStream();
AttachmentCipherOutputStream out = new AttachmentCipherOutputStream(key, stream);
byte[] buffer = new byte[4096];
int read, written = 0;
Util.copy(data, out);
while ((read = data.read(buffer)) != -1) {
out.write(buffer, 0, read);
written += read;
if (listener != null) {
listener.onAttachmentProgress(dataSize, written);
}
}
data.close();
out.flush();
out.close();
if (connection.getResponseCode() != 200) {
throw new IOException("Bad response: " + connection.getResponseCode() + " " + connection.getResponseMessage());

View File

@ -96,7 +96,6 @@ public class Util {
}
}
public static void copy(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[4096];
int read;
@ -124,19 +123,4 @@ public class Util {
throw new AssertionError(e);
}
}
public static byte[] toVarint64(long value) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
while (true) {
if ((value & ~0x7FL) == 0) {
out.write((int) value);
return out.toByteArray();
} else {
out.write(((int) value & 0x7F) | 0x80);
value >>>= 7;
}
}
}
}