basic arbitrary file transfer

This commit is contained in:
iNPUTmice 2014-11-13 21:04:05 +01:00
parent 4c504dea7a
commit 7a90ca429b
13 changed files with 334 additions and 138 deletions

View file

@ -2,7 +2,7 @@ package eu.siacs.conversations.entities;
public interface Downloadable { public interface Downloadable {
public final String[] VALID_EXTENSIONS = {"webp", "jpeg", "jpg", "png", "jpe"}; public final String[] VALID_IMAGE_EXTENSIONS = {"webp", "jpeg", "jpg", "png", "jpe"};
public final String[] VALID_CRYPTO_EXTENSIONS = {"pgp", "gpg", "otr"}; public final String[] VALID_CRYPTO_EXTENSIONS = {"pgp", "gpg", "otr"};
public static final int STATUS_UNKNOWN = 0x200; public static final int STATUS_UNKNOWN = 0x200;
@ -18,4 +18,8 @@ public interface Downloadable {
public int getStatus(); public int getStatus();
public long getFileSize(); public long getFileSize();
public int getProgress();
public String getMimeType();
} }

View file

@ -6,6 +6,7 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.URLConnection;
import java.security.InvalidAlgorithmParameterException; import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.security.Key; import java.security.Key;
@ -28,6 +29,7 @@ public class DownloadableFile extends File {
private long expectedSize = 0; private long expectedSize = 0;
private String sha1sum; private String sha1sum;
private Key aeskey; private Key aeskey;
private String mime;
private byte[] iv = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, private byte[] iv = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0xf }; 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0xf };
@ -52,6 +54,16 @@ public class DownloadableFile extends File {
} }
} }
public String getMimeType() {
if (mime==null) {
mime = URLConnection.guessContentTypeFromName(this.getAbsolutePath());
if (mime == null) {
mime = "";
}
}
return mime;
}
public void setExpectedSize(long size) { public void setExpectedSize(long size) {
this.expectedSize = size; this.expectedSize = size;
} }

View file

@ -32,7 +32,7 @@ public class Message extends AbstractEntity {
public static final int TYPE_TEXT = 0; public static final int TYPE_TEXT = 0;
public static final int TYPE_IMAGE = 1; public static final int TYPE_IMAGE = 1;
public static final int TYPE_AUDIO = 2; public static final int TYPE_FILE = 2;
public static final int TYPE_STATUS = 3; public static final int TYPE_STATUS = 3;
public static final int TYPE_PRIVATE = 4; public static final int TYPE_PRIVATE = 4;
@ -45,6 +45,7 @@ public class Message extends AbstractEntity {
public static String STATUS = "status"; public static String STATUS = "status";
public static String TYPE = "type"; public static String TYPE = "type";
public static String REMOTE_MSG_ID = "remoteMsgId"; public static String REMOTE_MSG_ID = "remoteMsgId";
public static String RELATIVE_FILE_PATH = "relativeFilePath";
public boolean markable = false; public boolean markable = false;
protected String conversationUuid; protected String conversationUuid;
protected Jid counterpart; protected Jid counterpart;
@ -55,6 +56,7 @@ public class Message extends AbstractEntity {
protected int encryption; protected int encryption;
protected int status; protected int status;
protected int type; protected int type;
protected String relativeFilePath;
protected boolean read = true; protected boolean read = true;
protected String remoteMsgId = null; protected String remoteMsgId = null;
protected Conversation conversation = null; protected Conversation conversation = null;
@ -74,13 +76,13 @@ public class Message extends AbstractEntity {
this(java.util.UUID.randomUUID().toString(), conversation.getUuid(), this(java.util.UUID.randomUUID().toString(), conversation.getUuid(),
conversation.getContactJid().toBareJid(), null, body, System conversation.getContactJid().toBareJid(), null, body, System
.currentTimeMillis(), encryption, .currentTimeMillis(), encryption,
status, TYPE_TEXT, null); status, TYPE_TEXT, null,null);
this.conversation = conversation; this.conversation = conversation;
} }
public Message(final String uuid, final String conversationUUid, final Jid counterpart, public Message(final String uuid, final String conversationUUid, final Jid counterpart,
final String trueCounterpart, final String body, final long timeSent, final String trueCounterpart, final String body, final long timeSent,
final int encryption, final int status, final int type, final String remoteMsgId) { final int encryption, final int status, final int type, final String remoteMsgId, final String relativeFilePath) {
this.uuid = uuid; this.uuid = uuid;
this.conversationUuid = conversationUUid; this.conversationUuid = conversationUUid;
this.counterpart = counterpart; this.counterpart = counterpart;
@ -91,6 +93,7 @@ public class Message extends AbstractEntity {
this.status = status; this.status = status;
this.type = type; this.type = type;
this.remoteMsgId = remoteMsgId; this.remoteMsgId = remoteMsgId;
this.relativeFilePath = relativeFilePath;
} }
public static Message fromCursor(Cursor cursor) { public static Message fromCursor(Cursor cursor) {
@ -114,7 +117,8 @@ public class Message extends AbstractEntity {
cursor.getInt(cursor.getColumnIndex(ENCRYPTION)), cursor.getInt(cursor.getColumnIndex(ENCRYPTION)),
cursor.getInt(cursor.getColumnIndex(STATUS)), cursor.getInt(cursor.getColumnIndex(STATUS)),
cursor.getInt(cursor.getColumnIndex(TYPE)), cursor.getInt(cursor.getColumnIndex(TYPE)),
cursor.getString(cursor.getColumnIndex(REMOTE_MSG_ID))); cursor.getString(cursor.getColumnIndex(REMOTE_MSG_ID)),
cursor.getString(cursor.getColumnIndex(RELATIVE_FILE_PATH)));
} }
public static Message createStatusMessage(Conversation conversation) { public static Message createStatusMessage(Conversation conversation) {
@ -141,6 +145,7 @@ public class Message extends AbstractEntity {
values.put(STATUS, status); values.put(STATUS, status);
values.put(TYPE, type); values.put(TYPE, type);
values.put(REMOTE_MSG_ID, remoteMsgId); values.put(REMOTE_MSG_ID, remoteMsgId);
values.put(RELATIVE_FILE_PATH, relativeFilePath);
return values; return values;
} }
@ -205,6 +210,14 @@ public class Message extends AbstractEntity {
this.status = status; this.status = status;
} }
public void setRelativeFilePath(String path) {
this.relativeFilePath = path;
}
public String getRelativeFilePath() {
return this.relativeFilePath;
}
public String getRemoteMsgId() { public String getRemoteMsgId() {
return this.remoteMsgId; return this.remoteMsgId;
} }
@ -376,14 +389,14 @@ public class Message extends AbstractEntity {
} }
String[] extensionParts = filename.split("\\."); String[] extensionParts = filename.split("\\.");
if (extensionParts.length == 2 if (extensionParts.length == 2
&& Arrays.asList(Downloadable.VALID_EXTENSIONS).contains( && Arrays.asList(Downloadable.VALID_IMAGE_EXTENSIONS).contains(
extensionParts[extensionParts.length - 1])) { extensionParts[extensionParts.length - 1])) {
return true; return true;
} else if (extensionParts.length == 3 } else if (extensionParts.length == 3
&& Arrays && Arrays
.asList(Downloadable.VALID_CRYPTO_EXTENSIONS) .asList(Downloadable.VALID_CRYPTO_EXTENSIONS)
.contains(extensionParts[extensionParts.length - 1]) .contains(extensionParts[extensionParts.length - 1])
&& Arrays.asList(Downloadable.VALID_EXTENSIONS).contains( && Arrays.asList(Downloadable.VALID_IMAGE_EXTENSIONS).contains(
extensionParts[extensionParts.length - 2])) { extensionParts[extensionParts.length - 2])) {
return true; return true;
} else { } else {

View file

@ -37,6 +37,7 @@ public class HttpConnection implements Downloadable {
private DownloadableFile file; private DownloadableFile file;
private int mStatus = Downloadable.STATUS_UNKNOWN; private int mStatus = Downloadable.STATUS_UNKNOWN;
private boolean acceptedAutomatically = false; private boolean acceptedAutomatically = false;
private int mProgress = 0;
public HttpConnection(HttpConnectionManager manager) { public HttpConnection(HttpConnectionManager manager) {
this.mHttpConnectionManager = manager; this.mHttpConnectionManager = manager;
@ -235,10 +236,14 @@ public class HttpConnection implements Downloadable {
if (os == null) { if (os == null) {
throw new IOException(); throw new IOException();
} }
long transmitted = 0;
long expected = file.getExpectedSize();
int count = -1; int count = -1;
byte[] buffer = new byte[1024]; byte[] buffer = new byte[1024];
while ((count = is.read(buffer)) != -1) { while ((count = is.read(buffer)) != -1) {
transmitted += count;
os.write(buffer, 0, count); os.write(buffer, 0, count);
mProgress = (int) (expected * 100 / transmitted);
} }
os.flush(); os.flush();
os.close(); os.close();
@ -272,4 +277,14 @@ public class HttpConnection implements Downloadable {
return 0; return 0;
} }
} }
@Override
public int getProgress() {
return this.mProgress;
}
@Override
public String getMimeType() {
return "";
}
} }

View file

@ -22,7 +22,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
private static DatabaseBackend instance = null; private static DatabaseBackend instance = null;
private static final String DATABASE_NAME = "history"; private static final String DATABASE_NAME = "history";
private static final int DATABASE_VERSION = 9; private static final int DATABASE_VERSION = 10;
private static String CREATE_CONTATCS_STATEMENT = "create table " private static String CREATE_CONTATCS_STATEMENT = "create table "
+ Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, " + Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, "
@ -64,6 +64,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
+ " TEXT, " + Message.TRUE_COUNTERPART + " TEXT," + " TEXT, " + Message.TRUE_COUNTERPART + " TEXT,"
+ Message.BODY + " TEXT, " + Message.ENCRYPTION + " NUMBER, " + Message.BODY + " TEXT, " + Message.ENCRYPTION + " NUMBER, "
+ Message.STATUS + " NUMBER," + Message.TYPE + " NUMBER, " + Message.STATUS + " NUMBER," + Message.TYPE + " NUMBER, "
+ Message.RELATIVE_FILE_PATH + " TEXT, "
+ Message.REMOTE_MSG_ID + " TEXT, FOREIGN KEY(" + Message.REMOTE_MSG_ID + " TEXT, FOREIGN KEY("
+ Message.CONVERSATION + ") REFERENCES " + Message.CONVERSATION + ") REFERENCES "
+ Conversation.TABLENAME + "(" + Conversation.UUID + Conversation.TABLENAME + "(" + Conversation.UUID
@ -110,6 +111,10 @@ public class DatabaseBackend extends SQLiteOpenHelper {
db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN " db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
+ Contact.LAST_PRESENCE + " TEXT"); + Contact.LAST_PRESENCE + " TEXT");
} }
if (oldVersion < 10 && newVersion >= 10) {
db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
+ Message.RELATIVE_FILE_PATH + " TEXT");
}
} }
public static synchronized DatabaseBackend getInstance(Context context) { public static synchronized DatabaseBackend getInstance(Context context) {

View file

@ -2,11 +2,13 @@ package eu.siacs.conversations.persistance;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.URLConnection;
import java.security.DigestOutputStream; import java.security.DigestOutputStream;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
@ -14,6 +16,7 @@ import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.Locale; import java.util.Locale;
import android.content.ContentResolver;
import android.database.Cursor; import android.database.Cursor;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
@ -53,25 +56,34 @@ public class FileBackend {
} }
public DownloadableFile getFile(Message message, boolean decrypted) { public DownloadableFile getFile(Message message, boolean decrypted) {
StringBuilder filename = new StringBuilder(); String path = message.getRelativeFilePath();
filename.append(getConversationsDirectory()); if (path != null && !path.isEmpty()) {
filename.append(message.getUuid()); if (path.startsWith("/")) {
if ((decrypted) || (message.getEncryption() == Message.ENCRYPTION_NONE)) { return new DownloadableFile(path);
filename.append(".webp"); } else {
return new DownloadableFile(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)+"/"+path);
}
} else { } else {
if (message.getEncryption() == Message.ENCRYPTION_OTR) { StringBuilder filename = new StringBuilder();
filename.append(getConversationsDirectory());
filename.append(message.getUuid());
if ((decrypted) || (message.getEncryption() == Message.ENCRYPTION_NONE)) {
filename.append(".webp"); filename.append(".webp");
} else { } else {
filename.append(".webp.pgp"); if (message.getEncryption() == Message.ENCRYPTION_OTR) {
filename.append(".webp");
} else {
filename.append(".webp.pgp");
}
} }
return new DownloadableFile(filename.toString());
} }
return new DownloadableFile(filename.toString());
} }
public static String getConversationsDirectory() { public static String getConversationsDirectory() {
return Environment.getExternalStoragePublicDirectory( return Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES).getAbsolutePath() Environment.DIRECTORY_PICTURES).getAbsolutePath()
+ "/Conversations/"; + "/Conversations/";
} }
public Bitmap resize(Bitmap originalBitmap, int size) { public Bitmap resize(Bitmap originalBitmap, int size) {
@ -103,13 +115,34 @@ public class FileBackend {
return Bitmap.createBitmap(bitmap, 0, 0, w, h, mtx, true); return Bitmap.createBitmap(bitmap, 0, 0, w, h, mtx, true);
} }
public String getOriginalPath(Uri uri) {
String path = null;
if (uri.getScheme().equals("file")) {
path = uri.getPath();
} else {
String[] projection = {MediaStore.MediaColumns.DATA};
Cursor metaCursor = mXmppConnectionService.getContentResolver().query(uri,
projection, null, null, null);
if (metaCursor != null) {
try {
if (metaCursor.moveToFirst()) {
path = metaCursor.getString(0);
}
} finally {
metaCursor.close();
}
}
}
return path;
}
public DownloadableFile copyImageToPrivateStorage(Message message, Uri image) public DownloadableFile copyImageToPrivateStorage(Message message, Uri image)
throws ImageCopyException { throws FileCopyException {
return this.copyImageToPrivateStorage(message, image, 0); return this.copyImageToPrivateStorage(message, image, 0);
} }
private DownloadableFile copyImageToPrivateStorage(Message message, private DownloadableFile copyImageToPrivateStorage(Message message,
Uri image, int sampleSize) throws ImageCopyException { Uri image, int sampleSize) throws FileCopyException {
try { try {
InputStream is = mXmppConnectionService.getContentResolver() InputStream is = mXmppConnectionService.getContentResolver()
.openInputStream(image); .openInputStream(image);
@ -125,7 +158,7 @@ public class FileBackend {
originalBitmap = BitmapFactory.decodeStream(is, null, options); originalBitmap = BitmapFactory.decodeStream(is, null, options);
is.close(); is.close();
if (originalBitmap == null) { if (originalBitmap == null) {
throw new ImageCopyException(R.string.error_not_an_image_file); throw new FileCopyException(R.string.error_not_an_image_file);
} }
Bitmap scalledBitmap = resize(originalBitmap, IMAGE_SIZE); Bitmap scalledBitmap = resize(originalBitmap, IMAGE_SIZE);
originalBitmap = null; originalBitmap = null;
@ -137,7 +170,7 @@ public class FileBackend {
boolean success = scalledBitmap.compress( boolean success = scalledBitmap.compress(
Bitmap.CompressFormat.WEBP, 75, os); Bitmap.CompressFormat.WEBP, 75, os);
if (!success) { if (!success) {
throw new ImageCopyException(R.string.error_compressing_image); throw new FileCopyException(R.string.error_compressing_image);
} }
os.flush(); os.flush();
os.close(); os.close();
@ -147,18 +180,18 @@ public class FileBackend {
message.setBody(Long.toString(size) + ',' + width + ',' + height); message.setBody(Long.toString(size) + ',' + width + ',' + height);
return file; return file;
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
throw new ImageCopyException(R.string.error_file_not_found); throw new FileCopyException(R.string.error_file_not_found);
} catch (IOException e) { } catch (IOException e) {
throw new ImageCopyException(R.string.error_io_exception); throw new FileCopyException(R.string.error_io_exception);
} catch (SecurityException e) { } catch (SecurityException e) {
throw new ImageCopyException( throw new FileCopyException(
R.string.error_security_exception_during_image_copy); R.string.error_security_exception_during_image_copy);
} catch (OutOfMemoryError e) { } catch (OutOfMemoryError e) {
++sampleSize; ++sampleSize;
if (sampleSize <= 3) { if (sampleSize <= 3) {
return copyImageToPrivateStorage(message, image, sampleSize); return copyImageToPrivateStorage(message, image, sampleSize);
} else { } else {
throw new ImageCopyException(R.string.error_out_of_memory); throw new FileCopyException(R.string.error_out_of_memory);
} }
} }
} }
@ -400,11 +433,11 @@ public class FileBackend {
return Uri.parse("file://" + file.getAbsolutePath()); return Uri.parse("file://" + file.getAbsolutePath());
} }
public class ImageCopyException extends Exception { public class FileCopyException extends Exception {
private static final long serialVersionUID = -1010013599132881427L; private static final long serialVersionUID = -1010013599132881427L;
private int resId; private int resId;
public ImageCopyException(int resId) { public FileCopyException(int resId) {
this.resId = resId; this.resId = resId;
} }

View file

@ -56,6 +56,7 @@ import eu.siacs.conversations.entities.Bookmark;
import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Downloadable; import eu.siacs.conversations.entities.Downloadable;
import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.MucOptions; import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.entities.MucOptions.OnRenameListener; import eu.siacs.conversations.entities.MucOptions.OnRenameListener;
@ -294,6 +295,27 @@ public class XmppConnectionService extends Service {
return this.mAvatarService; return this.mAvatarService;
} }
public Message attachFileToConversation(Conversation conversation, final Uri uri) {
String path = getFileBackend().getOriginalPath(uri);
if (path!=null) {
Log.d(Config.LOGTAG,"file path : "+path);
Message message;
if (conversation.getNextEncryption(forceEncryption()) == Message.ENCRYPTION_PGP) {
message = new Message(conversation, "",
Message.ENCRYPTION_DECRYPTED);
} else {
message = new Message(conversation, "",
conversation.getNextEncryption(forceEncryption()));
}
message.setCounterpart(conversation.getNextCounterpart());
message.setType(Message.TYPE_FILE);
message.setStatus(Message.STATUS_OFFERED);
message.setRelativeFilePath(path);
return message;
}
return null;
}
public Message attachImageToConversation(final Conversation conversation, public Message attachImageToConversation(final Conversation conversation,
final Uri uri, final UiCallback<Message> callback) { final Uri uri, final UiCallback<Message> callback) {
final Message message; final Message message;
@ -312,13 +334,14 @@ public class XmppConnectionService extends Service {
@Override @Override
public void run() { public void run() {
try { try {
getFileBackend().copyImageToPrivateStorage(message, uri); DownloadableFile file = getFileBackend().copyImageToPrivateStorage(message, uri);
message.setRelativeFilePath(file.getName());
if (conversation.getNextEncryption(forceEncryption()) == Message.ENCRYPTION_PGP) { if (conversation.getNextEncryption(forceEncryption()) == Message.ENCRYPTION_PGP) {
getPgpEngine().encrypt(message, callback); getPgpEngine().encrypt(message, callback);
} else { } else {
callback.success(message); callback.success(message);
} }
} catch (FileBackend.ImageCopyException e) { } catch (FileBackend.FileCopyException e) {
callback.error(e.getResId(), message); callback.error(e.getResId(), message);
} }
} }
@ -552,7 +575,7 @@ public class XmppConnectionService extends Service {
boolean send = false; boolean send = false;
if (account.getStatus() == Account.STATUS_ONLINE if (account.getStatus() == Account.STATUS_ONLINE
&& account.getXmppConnection() != null) { && account.getXmppConnection() != null) {
if (message.getType() == Message.TYPE_IMAGE) { if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) {
if (message.getCounterpart() != null) { if (message.getCounterpart() != null) {
if (message.getEncryption() == Message.ENCRYPTION_OTR) { if (message.getEncryption() == Message.ENCRYPTION_OTR) {
if (!conv.hasValidOtrSession()) { if (!conv.hasValidOtrSession()) {
@ -1988,5 +2011,15 @@ public class XmppConnectionService extends Service {
return 0; return 0;
} }
@Override
public int getProgress() {
return 0;
}
@Override
public String getMimeType() {
return "";
}
} }
} }

View file

@ -15,6 +15,7 @@ import android.os.SystemClock;
import android.provider.MediaStore; import android.provider.MediaStore;
import android.support.v4.widget.SlidingPaneLayout; import android.support.v4.widget.SlidingPaneLayout;
import android.support.v4.widget.SlidingPaneLayout.PanelSlideListener; import android.support.v4.widget.SlidingPaneLayout.PanelSlideListener;
import android.util.Log;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
@ -31,6 +32,7 @@ import android.widget.Toast;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversation;
@ -52,13 +54,14 @@ public class ConversationActivity extends XmppActivity implements
public static final int REQUEST_SEND_MESSAGE = 0x0201; public static final int REQUEST_SEND_MESSAGE = 0x0201;
public static final int REQUEST_DECRYPT_PGP = 0x0202; public static final int REQUEST_DECRYPT_PGP = 0x0202;
public static final int REQUEST_ENCRYPT_MESSAGE = 0x0207; public static final int REQUEST_ENCRYPT_MESSAGE = 0x0207;
private static final int REQUEST_ATTACH_FILE_DIALOG = 0x0203; private static final int REQUEST_ATTACH_IMAGE_DIALOG = 0x0203;
private static final int REQUEST_IMAGE_CAPTURE = 0x0204; private static final int REQUEST_IMAGE_CAPTURE = 0x0204;
private static final int REQUEST_RECORD_AUDIO = 0x0205; private static final int REQUEST_RECORD_AUDIO = 0x0205;
private static final int REQUEST_SEND_PGP_IMAGE = 0x0206; private static final int REQUEST_SEND_PGP_IMAGE = 0x0206;
private static final int REQUEST_ATTACH_FILE_DIALOG = 0x0208;
private static final int ATTACHMENT_CHOICE_CHOOSE_IMAGE = 0x0301; private static final int ATTACHMENT_CHOICE_CHOOSE_IMAGE = 0x0301;
private static final int ATTACHMENT_CHOICE_TAKE_PHOTO = 0x0302; private static final int ATTACHMENT_CHOICE_TAKE_PHOTO = 0x0302;
private static final int ATTACHMENT_CHOICE_RECORD_VOICE = 0x0303; private static final int ATTACHMENT_CHOICE_CHOOSE_FILE = 0x0303;
private static final String STATE_OPEN_CONVERSATION = "state_open_conversation"; private static final String STATE_OPEN_CONVERSATION = "state_open_conversation";
private static final String STATE_PANEL_OPEN = "state_panel_open"; private static final String STATE_PANEL_OPEN = "state_panel_open";
private static final String STATE_PENDING_URI = "state_pending_uri"; private static final String STATE_PENDING_URI = "state_pending_uri";
@ -66,6 +69,7 @@ public class ConversationActivity extends XmppActivity implements
private String mOpenConverstaion = null; private String mOpenConverstaion = null;
private boolean mPanelOpen = true; private boolean mPanelOpen = true;
private Uri mPendingImageUri = null; private Uri mPendingImageUri = null;
private Uri mPendingFileUri = null;
private View mContentView; private View mContentView;
@ -306,13 +310,16 @@ public class ConversationActivity extends XmppActivity implements
Intent attachFileIntent = new Intent(); Intent attachFileIntent = new Intent();
attachFileIntent.setType("image/*"); attachFileIntent.setType("image/*");
attachFileIntent.setAction(Intent.ACTION_GET_CONTENT); attachFileIntent.setAction(Intent.ACTION_GET_CONTENT);
Intent chooser = Intent.createChooser(attachFileIntent,
getString(R.string.attach_file));
startActivityForResult(chooser, REQUEST_ATTACH_IMAGE_DIALOG);
} else if (attachmentChoice == ATTACHMENT_CHOICE_CHOOSE_FILE) {
Intent attachFileIntent = new Intent();
attachFileIntent.setType("file/*");
attachFileIntent.setAction(Intent.ACTION_GET_CONTENT);
Intent chooser = Intent.createChooser(attachFileIntent, Intent chooser = Intent.createChooser(attachFileIntent,
getString(R.string.attach_file)); getString(R.string.attach_file));
startActivityForResult(chooser, REQUEST_ATTACH_FILE_DIALOG); startActivityForResult(chooser, REQUEST_ATTACH_FILE_DIALOG);
} else if (attachmentChoice == ATTACHMENT_CHOICE_RECORD_VOICE) {
Intent intent = new Intent(
MediaStore.Audio.Media.RECORD_SOUND_ACTION);
startActivityForResult(intent, REQUEST_RECORD_AUDIO);
} }
} }
}); });
@ -483,7 +490,7 @@ public class ConversationActivity extends XmppActivity implements
attachFile(ATTACHMENT_CHOICE_TAKE_PHOTO); attachFile(ATTACHMENT_CHOICE_TAKE_PHOTO);
break; break;
case R.id.attach_record_voice: case R.id.attach_record_voice:
attachFile(ATTACHMENT_CHOICE_RECORD_VOICE); attachFile(ATTACHMENT_CHOICE_CHOOSE_FILE);
break; break;
} }
return false; return false;
@ -675,14 +682,17 @@ public class ConversationActivity extends XmppActivity implements
} else { } else {
showConversationsOverview(); showConversationsOverview();
mPendingImageUri = null; mPendingImageUri = null;
mPendingFileUri = null;
setSelectedConversation(conversationList.get(0)); setSelectedConversation(conversationList.get(0));
this.mConversationFragment.reInit(getSelectedConversation()); this.mConversationFragment.reInit(getSelectedConversation());
} }
if (mPendingImageUri != null) { if (mPendingImageUri != null) {
attachImageToConversation(getSelectedConversation(), attachImageToConversation(getSelectedConversation(),mPendingImageUri);
mPendingImageUri);
mPendingImageUri = null; mPendingImageUri = null;
} else if (mPendingFileUri != null) {
attachFileToConversation(getSelectedConversation(),mPendingFileUri);
mPendingFileUri = null;
} }
ExceptionHelper.checkForCrash(this, this.xmppConnectionService); ExceptionHelper.checkForCrash(this, this.xmppConnectionService);
setIntent(new Intent()); setIntent(new Intent());
@ -726,13 +736,20 @@ public class ConversationActivity extends XmppActivity implements
selectedFragment.hideSnackbar(); selectedFragment.hideSnackbar();
selectedFragment.updateMessages(); selectedFragment.updateMessages();
} }
} else if (requestCode == REQUEST_ATTACH_FILE_DIALOG) { } else if (requestCode == REQUEST_ATTACH_IMAGE_DIALOG) {
mPendingImageUri = data.getData(); mPendingImageUri = data.getData();
if (xmppConnectionServiceBound) { if (xmppConnectionServiceBound) {
attachImageToConversation(getSelectedConversation(), attachImageToConversation(getSelectedConversation(),
mPendingImageUri); mPendingImageUri);
mPendingImageUri = null; mPendingImageUri = null;
} }
} else if (requestCode == REQUEST_ATTACH_FILE_DIALOG) {
mPendingFileUri = data.getData();
if (xmppConnectionServiceBound) {
attachFileToConversation(getSelectedConversation(),
mPendingFileUri);
mPendingFileUri = null;
}
} else if (requestCode == REQUEST_SEND_PGP_IMAGE) { } else if (requestCode == REQUEST_SEND_PGP_IMAGE) {
} else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_IMAGE) { } else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_IMAGE) {
@ -754,9 +771,6 @@ public class ConversationActivity extends XmppActivity implements
Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intent.setData(mPendingImageUri); intent.setData(mPendingImageUri);
sendBroadcast(intent); sendBroadcast(intent);
} else if (requestCode == REQUEST_RECORD_AUDIO) {
attachAudioToConversation(getSelectedConversation(),
data.getData());
} }
} else { } else {
if (requestCode == REQUEST_IMAGE_CAPTURE) { if (requestCode == REQUEST_IMAGE_CAPTURE) {
@ -765,8 +779,10 @@ public class ConversationActivity extends XmppActivity implements
} }
} }
private void attachAudioToConversation(Conversation conversation, Uri uri) { private void attachFileToConversation(Conversation conversation, Uri uri) {
Log.d(Config.LOGTAG, "attachFileToConversation");
Message message = xmppConnectionService.attachFileToConversation(conversation,uri);
xmppConnectionService.sendMessage(message);
} }
private void attachImageToConversation(Conversation conversation, Uri uri) { private void attachImageToConversation(Conversation conversation, Uri uri) {

View file

@ -2,15 +2,18 @@ package eu.siacs.conversations.ui.adapter;
import android.content.Intent; import android.content.Intent;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.net.Uri;
import android.text.Spannable; import android.text.Spannable;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.style.ForegroundColorSpan; import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan; import android.text.style.StyleSpan;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener; import android.view.View.OnLongClickListener;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.webkit.MimeTypeMap;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.Button; import android.widget.Button;
import android.widget.ImageView; import android.widget.ImageView;
@ -18,6 +21,9 @@ import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.List; import java.util.List;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
@ -25,6 +31,7 @@ import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Downloadable; import eu.siacs.conversations.entities.Downloadable;
import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.Message.ImageParams; import eu.siacs.conversations.entities.Message.ImageParams;
import eu.siacs.conversations.ui.ConversationActivity; import eu.siacs.conversations.ui.ConversationActivity;
@ -181,13 +188,13 @@ public class MessageAdapter extends ArrayAdapter<Message> {
} }
} }
private void displayInfoMessage(ViewHolder viewHolder, int r) { private void displayInfoMessage(ViewHolder viewHolder, String text) {
if (viewHolder.download_button != null) { if (viewHolder.download_button != null) {
viewHolder.download_button.setVisibility(View.GONE); viewHolder.download_button.setVisibility(View.GONE);
} }
viewHolder.image.setVisibility(View.GONE); viewHolder.image.setVisibility(View.GONE);
viewHolder.messageBody.setVisibility(View.VISIBLE); viewHolder.messageBody.setVisibility(View.VISIBLE);
viewHolder.messageBody.setText(getContext().getString(r)); viewHolder.messageBody.setText(text);
viewHolder.messageBody.setTextColor(activity.getSecondaryTextColor()); viewHolder.messageBody.setTextColor(activity.getSecondaryTextColor());
viewHolder.messageBody.setTypeface(null, Typeface.ITALIC); viewHolder.messageBody.setTypeface(null, Typeface.ITALIC);
viewHolder.messageBody.setTextIsSelectable(false); viewHolder.messageBody.setTextIsSelectable(false);
@ -252,11 +259,11 @@ public class MessageAdapter extends ArrayAdapter<Message> {
} }
private void displayDownloadableMessage(ViewHolder viewHolder, private void displayDownloadableMessage(ViewHolder viewHolder,
final Message message, int resid) { final Message message, String text) {
viewHolder.image.setVisibility(View.GONE); viewHolder.image.setVisibility(View.GONE);
viewHolder.messageBody.setVisibility(View.GONE); viewHolder.messageBody.setVisibility(View.GONE);
viewHolder.download_button.setVisibility(View.VISIBLE); viewHolder.download_button.setVisibility(View.VISIBLE);
viewHolder.download_button.setText(resid); viewHolder.download_button.setText(text);
viewHolder.download_button.setOnClickListener(new OnClickListener() { viewHolder.download_button.setOnClickListener(new OnClickListener() {
@Override @Override
@ -267,6 +274,21 @@ public class MessageAdapter extends ArrayAdapter<Message> {
viewHolder.download_button.setOnLongClickListener(openContextMenu); viewHolder.download_button.setOnLongClickListener(openContextMenu);
} }
private void displayOpenableMessage(ViewHolder viewHolder,final Message message) {
viewHolder.image.setVisibility(View.GONE);
viewHolder.messageBody.setVisibility(View.GONE);
viewHolder.download_button.setVisibility(View.VISIBLE);
viewHolder.download_button.setText(activity.getString(R.string.open_file,activity.xmppConnectionService.getFileBackend().getFile(message).getMimeType()));
viewHolder.download_button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
openDonwloadable(message);
}
});
viewHolder.download_button.setOnLongClickListener(openContextMenu);
}
private void displayImageMessage(ViewHolder viewHolder, private void displayImageMessage(ViewHolder viewHolder,
final Message message) { final Message message) {
if (viewHolder.download_button != null) { if (viewHolder.download_button != null) {
@ -455,42 +477,46 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}); });
} }
if (item.getType() == Message.TYPE_IMAGE if (item.getDownloadable() != null) {
|| item.getDownloadable() != null) {
Downloadable d = item.getDownloadable(); Downloadable d = item.getDownloadable();
if (d != null && d.getStatus() == Downloadable.STATUS_DOWNLOADING) { if (d.getStatus() == Downloadable.STATUS_DOWNLOADING) {
displayInfoMessage(viewHolder, R.string.receiving_image); if (item.getType() == Message.TYPE_FILE) {
} else if (d != null displayInfoMessage(viewHolder,activity.getString(R.string.receiving_file,d.getMimeType(),d.getProgress()));
&& d.getStatus() == Downloadable.STATUS_CHECKING) { } else {
displayInfoMessage(viewHolder, R.string.checking_image); displayInfoMessage(viewHolder,activity.getString(R.string.receiving_image,d.getProgress()));
} else if (d != null }
&& d.getStatus() == Downloadable.STATUS_DELETED) { } else if (d.getStatus() == Downloadable.STATUS_CHECKING) {
displayInfoMessage(viewHolder, R.string.image_file_deleted); displayInfoMessage(viewHolder,activity.getString(R.string.checking_image));
} else if (d != null && d.getStatus() == Downloadable.STATUS_OFFER) { } else if (d.getStatus() == Downloadable.STATUS_DELETED) {
displayDownloadableMessage(viewHolder, item, displayInfoMessage(viewHolder,activity.getString(R.string.image_file_deleted));
R.string.download_image); } else if (d.getStatus() == Downloadable.STATUS_OFFER) {
} else if (d != null if (item.getType() == Message.TYPE_FILE) {
&& d.getStatus() == Downloadable.STATUS_OFFER_CHECK_FILESIZE) { displayDownloadableMessage(viewHolder,item,activity.getString(R.string.download_file,d.getMimeType()));
displayDownloadableMessage(viewHolder, item, } else {
R.string.check_image_filesize); displayDownloadableMessage(viewHolder, item,activity.getString(R.string.download_image));
} else if (d != null && d.getStatus() == Downloadable.STATUS_FAILED) { }
displayInfoMessage(viewHolder, R.string.image_transmission_failed); } else if (d.getStatus() == Downloadable.STATUS_OFFER_CHECK_FILESIZE) {
} else if ((item.getEncryption() == Message.ENCRYPTION_DECRYPTED) displayDownloadableMessage(viewHolder, item,activity.getString(R.string.check_image_filesize));
|| (item.getEncryption() == Message.ENCRYPTION_NONE) } else if (d.getStatus() == Downloadable.STATUS_FAILED) {
|| (item.getEncryption() == Message.ENCRYPTION_OTR)) { displayInfoMessage(viewHolder, activity.getString(R.string.image_transmission_failed));
displayImageMessage(viewHolder, item);
} else if (item.getEncryption() == Message.ENCRYPTION_PGP) {
displayInfoMessage(viewHolder, R.string.encrypted_message);
} else {
displayDecryptionFailed(viewHolder);
} }
} else if (item.getType() == Message.TYPE_IMAGE) {
if (item.getEncryption() == Message.ENCRYPTION_PGP) {
displayInfoMessage(viewHolder,activity.getString(R.string.encrypted_message));
} else if (item.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
displayDecryptionFailed(viewHolder);
} else {
displayImageMessage(viewHolder, item);
}
} else if (item.getType() == Message.TYPE_FILE) {
displayOpenableMessage(viewHolder,item);
} else { } else {
if (item.getEncryption() == Message.ENCRYPTION_PGP) { if (item.getEncryption() == Message.ENCRYPTION_PGP) {
if (activity.hasPgp()) { if (activity.hasPgp()) {
displayInfoMessage(viewHolder, R.string.encrypted_message); displayInfoMessage(viewHolder,activity.getString(R.string.encrypted_message));
} else { } else {
displayInfoMessage(viewHolder, displayInfoMessage(viewHolder,
R.string.install_openkeychain); activity.getString(R.string.install_openkeychain));
if (viewHolder != null) { if (viewHolder != null) {
viewHolder.message_box viewHolder.message_box
.setOnClickListener(new OnClickListener() { .setOnClickListener(new OnClickListener() {
@ -524,6 +550,13 @@ public class MessageAdapter extends ArrayAdapter<Message> {
} }
} }
public void openDonwloadable(Message message) {
DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), file.getMimeType());
getContext().startActivity(intent);
}
public interface OnContactPictureClicked { public interface OnContactPictureClicked {
public void onContactPictureClicked(Message message); public void onContactPictureClicked(Message message);
} }

View file

@ -11,6 +11,7 @@ import java.util.concurrent.ConcurrentHashMap;
import android.content.Intent; import android.content.Intent;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.net.Uri; import android.net.Uri;
import android.os.SystemClock;
import android.util.Log; import android.util.Log;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
@ -29,9 +30,6 @@ import eu.siacs.conversations.xmpp.stanzas.IqPacket;
public class JingleConnection implements Downloadable { public class JingleConnection implements Downloadable {
private final String[] extensions = { "webp", "jpeg", "jpg", "png" };
private final String[] cryptoExtensions = { "pgp", "gpg", "otr" };
private JingleConnectionManager mJingleConnectionManager; private JingleConnectionManager mJingleConnectionManager;
private XmppConnectionService mXmppConnectionService; private XmppConnectionService mXmppConnectionService;
@ -62,6 +60,9 @@ public class JingleConnection implements Downloadable {
private String contentName; private String contentName;
private String contentCreator; private String contentCreator;
private int mProgress = 0;
private long mLastGuiRefresh = 0;
private boolean receivedCandidate = false; private boolean receivedCandidate = false;
private boolean sentCandidate = false; private boolean sentCandidate = false;
@ -258,7 +259,6 @@ public class JingleConnection implements Downloadable {
packet.getFrom().toBareJid(), false); packet.getFrom().toBareJid(), false);
this.message = new Message(conversation, "", Message.ENCRYPTION_NONE); this.message = new Message(conversation, "", Message.ENCRYPTION_NONE);
this.message.setStatus(Message.STATUS_RECEIVED); this.message.setStatus(Message.STATUS_RECEIVED);
this.message.setType(Message.TYPE_IMAGE);
this.mStatus = Downloadable.STATUS_OFFER; this.mStatus = Downloadable.STATUS_OFFER;
this.message.setDownloadable(this); this.message.setDownloadable(this);
final Jid from = packet.getFrom(); final Jid from = packet.getFrom();
@ -278,68 +278,71 @@ public class JingleConnection implements Downloadable {
Element fileSize = fileOffer.findChild("size"); Element fileSize = fileOffer.findChild("size");
Element fileNameElement = fileOffer.findChild("name"); Element fileNameElement = fileOffer.findChild("name");
if (fileNameElement != null) { if (fileNameElement != null) {
boolean supportedFile = false;
String[] filename = fileNameElement.getContent() String[] filename = fileNameElement.getContent()
.toLowerCase(Locale.US).split("\\."); .toLowerCase(Locale.US).split("\\.");
if (Arrays.asList(this.extensions).contains( if (Arrays.asList(VALID_IMAGE_EXTENSIONS).contains(
filename[filename.length - 1])) { filename[filename.length - 1])) {
supportedFile = true; message.setType(Message.TYPE_IMAGE);
} else if (Arrays.asList(this.cryptoExtensions).contains( } else if (Arrays.asList(VALID_CRYPTO_EXTENSIONS).contains(
filename[filename.length - 1])) { filename[filename.length - 1])) {
if (filename.length == 3) { if (filename.length == 3) {
if (Arrays.asList(this.extensions).contains( if (Arrays.asList(VALID_IMAGE_EXTENSIONS).contains(
filename[filename.length - 2])) { filename[filename.length - 2])) {
supportedFile = true; message.setType(Message.TYPE_IMAGE);
if (filename[filename.length - 1].equals("otr")) {
Log.d(Config.LOGTAG, "receiving otr file");
this.message
.setEncryption(Message.ENCRYPTION_OTR);
} else {
this.message
.setEncryption(Message.ENCRYPTION_PGP);
}
}
}
}
if (supportedFile) {
long size = Long.parseLong(fileSize.getContent());
message.setBody(Long.toString(size));
conversation.add(message);
mXmppConnectionService.updateConversationUi();
if (size <= this.mJingleConnectionManager
.getAutoAcceptFileSize()) {
Log.d(Config.LOGTAG, "auto accepting file from "
+ packet.getFrom());
this.acceptedAutomatically = true;
this.sendAccept();
} else {
message.markUnread();
Log.d(Config.LOGTAG,
"not auto accepting new file offer with size: "
+ size
+ " allowed size:"
+ this.mJingleConnectionManager
.getAutoAcceptFileSize());
this.mXmppConnectionService.getNotificationService()
.push(message);
}
this.file = this.mXmppConnectionService.getFileBackend()
.getFile(message, false);
if (message.getEncryption() == Message.ENCRYPTION_OTR) {
byte[] key = conversation.getSymmetricKey();
if (key == null) {
this.sendCancel();
this.cancel();
return;
} else { } else {
this.file.setKey(key); message.setType(Message.TYPE_FILE);
}
if (filename[filename.length - 1].equals("otr")) {
message.setEncryption(Message.ENCRYPTION_OTR);
} else {
message.setEncryption(Message.ENCRYPTION_PGP);
} }
} }
this.file.setExpectedSize(size);
} else { } else {
this.sendCancel(); message.setType(Message.TYPE_FILE);
this.cancel();
} }
if (message.getType() == Message.TYPE_FILE) {
String suffix = "";
if (!fileNameElement.getContent().isEmpty()) {
String parts[] = fileNameElement.getContent().split("/");
suffix = parts[parts.length - 1];
}
message.setRelativeFilePath(message.getUuid()+"_"+suffix);
}
long size = Long.parseLong(fileSize.getContent());
message.setBody(Long.toString(size));
conversation.add(message);
mXmppConnectionService.updateConversationUi();
if (size <= this.mJingleConnectionManager
.getAutoAcceptFileSize()) {
Log.d(Config.LOGTAG, "auto accepting file from "
+ packet.getFrom());
this.acceptedAutomatically = true;
this.sendAccept();
} else {
message.markUnread();
Log.d(Config.LOGTAG,
"not auto accepting new file offer with size: "
+ size
+ " allowed size:"
+ this.mJingleConnectionManager
.getAutoAcceptFileSize());
this.mXmppConnectionService.getNotificationService()
.push(message);
}
this.file = this.mXmppConnectionService.getFileBackend()
.getFile(message, false);
if (message.getEncryption() == Message.ENCRYPTION_OTR) {
byte[] key = conversation.getSymmetricKey();
if (key == null) {
this.sendCancel();
this.cancel();
return;
} else {
this.file.setKey(key);
}
}
this.file.setExpectedSize(size);
} else { } else {
this.sendCancel(); this.sendCancel();
this.cancel(); this.cancel();
@ -354,7 +357,7 @@ public class JingleConnection implements Downloadable {
this.mXmppConnectionService.markMessage(this.message, Message.STATUS_OFFERED); this.mXmppConnectionService.markMessage(this.message, Message.STATUS_OFFERED);
JinglePacket packet = this.bootstrapPacket("session-initiate"); JinglePacket packet = this.bootstrapPacket("session-initiate");
Content content = new Content(this.contentCreator, this.contentName); Content content = new Content(this.contentCreator, this.contentName);
if (message.getType() == Message.TYPE_IMAGE) { if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) {
content.setTransportId(this.transportId); content.setTransportId(this.transportId);
this.file = this.mXmppConnectionService.getFileBackend().getFile( this.file = this.mXmppConnectionService.getFileBackend().getFile(
message, false); message, false);
@ -856,6 +859,14 @@ public class JingleConnection implements Downloadable {
return null; return null;
} }
public void updateProgress(int i) {
this.mProgress = i;
if (SystemClock.elapsedRealtime() - this.mLastGuiRefresh > 1000) {
this.mLastGuiRefresh = SystemClock.elapsedRealtime();
mXmppConnectionService.updateConversationUi();
}
}
interface OnProxyActivated { interface OnProxyActivated {
public void success(); public void success();
@ -900,4 +911,14 @@ public class JingleConnection implements Downloadable {
return 0; return 0;
} }
} }
@Override
public int getProgress() {
return this.mProgress;
}
@Override
public String getMimeType() {
return this.file.getMimeType();
}
} }

View file

@ -15,6 +15,7 @@ import eu.siacs.conversations.utils.CryptoHelper;
public class JingleSocks5Transport extends JingleTransport { public class JingleSocks5Transport extends JingleTransport {
private JingleCandidate candidate; private JingleCandidate candidate;
private JingleConnection connection;
private String destination; private String destination;
private OutputStream outputStream; private OutputStream outputStream;
private InputStream inputStream; private InputStream inputStream;
@ -25,6 +26,7 @@ public class JingleSocks5Transport extends JingleTransport {
public JingleSocks5Transport(JingleConnection jingleConnection, public JingleSocks5Transport(JingleConnection jingleConnection,
JingleCandidate candidate) { JingleCandidate candidate) {
this.candidate = candidate; this.candidate = candidate;
this.connection = jingleConnection;
try { try {
MessageDigest mDigest = MessageDigest.getInstance("SHA-1"); MessageDigest mDigest = MessageDigest.getInstance("SHA-1");
StringBuilder destBuilder = new StringBuilder(); StringBuilder destBuilder = new StringBuilder();
@ -102,11 +104,15 @@ public class JingleSocks5Transport extends JingleTransport {
callback.onFileTransferAborted(); callback.onFileTransferAborted();
return; return;
} }
long size = file.getSize();
double transmitted = 0;
int count; int count;
byte[] buffer = new byte[8192]; byte[] buffer = new byte[8192];
while ((count = fileInputStream.read(buffer)) > 0) { while ((count = fileInputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, count); outputStream.write(buffer, 0, count);
digest.update(buffer, 0, count); digest.update(buffer, 0, count);
transmitted += count;
connection.updateProgress((int) (((transmitted) / size) * 100));
} }
outputStream.flush(); outputStream.flush();
file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest())); file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
@ -151,6 +157,7 @@ public class JingleSocks5Transport extends JingleTransport {
callback.onFileTransferAborted(); callback.onFileTransferAborted();
return; return;
} }
double size = file.getExpectedSize();
long remainingSize = file.getExpectedSize(); long remainingSize = file.getExpectedSize();
byte[] buffer = new byte[8192]; byte[] buffer = new byte[8192];
int count = buffer.length; int count = buffer.length;
@ -164,6 +171,7 @@ public class JingleSocks5Transport extends JingleTransport {
digest.update(buffer, 0, count); digest.update(buffer, 0, count);
remainingSize -= count; remainingSize -= count;
} }
connection.updateProgress((int) (((size - remainingSize) / size) * 100));
} }
fileOutputStream.flush(); fileOutputStream.flush();
fileOutputStream.close(); fileOutputStream.close();

View file

@ -9,7 +9,6 @@
android:title="@string/attach_take_picture"/> android:title="@string/attach_take_picture"/>
<item <item
android:id="@+id/attach_record_voice" android:id="@+id/attach_record_voice"
android:title="@string/attach_record_voice" android:title="@string/choose_file"/>
android:visible="false"/>
</menu> </menu>

View file

@ -58,7 +58,7 @@
<string name="add_contact">Add contact</string> <string name="add_contact">Add contact</string>
<string name="send_failed">delivery failed</string> <string name="send_failed">delivery failed</string>
<string name="send_rejected">rejected</string> <string name="send_rejected">rejected</string>
<string name="receiving_image">Receiving image file. Please wait…</string> <string name="receiving_image">Receiving image file (%1$d%%)</string>
<string name="preparing_image">Preparing image for transmission</string> <string name="preparing_image">Preparing image for transmission</string>
<string name="action_clear_history">Clear history</string> <string name="action_clear_history">Clear history</string>
<string name="clear_conversation_history">Clear Conversation History</string> <string name="clear_conversation_history">Clear Conversation History</string>
@ -311,4 +311,8 @@
<string name="scan_qr_code">Scan QR code</string> <string name="scan_qr_code">Scan QR code</string>
<string name="show_qr_code">Show QR code</string> <string name="show_qr_code">Show QR code</string>
<string name="account_details">Account details</string> <string name="account_details">Account details</string>
<string name="choose_file">Choose file</string>
<string name="receiving_file">Receiving %1$s file (%2$d%%)</string>
<string name="download_file">Download %s file</string>
<string name="open_file">Open %s file</string>
</resources> </resources>