Merge branch 'feature/file_transfer' into development

Conflicts:
	src/main/res/values/strings.xml
This commit is contained in:
iNPUTmice 2014-11-15 15:52:15 +01:00
commit 35bf13f5ef
23 changed files with 775 additions and 278 deletions

View file

@ -19,6 +19,9 @@ public final class Config {
public static final int MESSAGE_MERGE_WINDOW = 20;
public static final boolean PARSE_EMOTICONS = false;
public static final int PROGRESS_UI_UPDATE_INTERVAL = 750;
public static final boolean NO_PROXY_LOOKUP = false; //useful to debug ibb
private Config() {

View file

@ -3,7 +3,6 @@ package eu.siacs.conversations.crypto;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@ -24,7 +23,6 @@ import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.ui.UiCallback;
import android.app.PendingIntent;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.net.Uri;
public class PgpEngine {
@ -80,12 +78,13 @@ public class PgpEngine {
}
}
});
} else if (message.getType() == Message.TYPE_IMAGE) {
} else if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) {
try {
final DownloadableFile inputFile = this.mXmppConnectionService
.getFileBackend().getFile(message, false);
final DownloadableFile outputFile = this.mXmppConnectionService
.getFileBackend().getFile(message, true);
outputFile.getParentFile().mkdirs();
outputFile.createNewFile();
InputStream is = new FileInputStream(inputFile);
OutputStream os = new FileOutputStream(outputFile);
@ -97,24 +96,7 @@ public class PgpEngine {
OpenPgpApi.RESULT_CODE_ERROR)) {
case OpenPgpApi.RESULT_CODE_SUCCESS:
URL url = message.getImageParams().url;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(
outputFile.getAbsolutePath(), options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
if (url == null) {
message.setBody(Long.toString(outputFile
.getSize())
+ '|'
+ imageWidth
+ '|'
+ imageHeight);
} else {
message.setBody(url.toString() + "|"
+ Long.toString(outputFile.getSize())
+ '|' + imageWidth + '|' + imageHeight);
}
mXmppConnectionService.getFileBackend().updateFileParams(message,url);
message.setEncryption(Message.ENCRYPTION_DECRYPTED);
PgpEngine.this.mXmppConnectionService
.updateMessage(message);
@ -199,12 +181,13 @@ public class PgpEngine {
}
}
});
} else if (message.getType() == Message.TYPE_IMAGE) {
} else if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) {
try {
DownloadableFile inputFile = this.mXmppConnectionService
.getFileBackend().getFile(message, true);
DownloadableFile outputFile = this.mXmppConnectionService
.getFileBackend().getFile(message, false);
outputFile.getParentFile().mkdirs();
outputFile.createNewFile();
InputStream is = new FileInputStream(inputFile);
OutputStream os = new FileOutputStream(outputFile);

View file

@ -2,7 +2,7 @@ package eu.siacs.conversations.entities;
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 static final int STATUS_UNKNOWN = 0x200;
@ -12,10 +12,17 @@ public interface Downloadable {
public static final int STATUS_DOWNLOADING = 0x204;
public static final int STATUS_DELETED = 0x205;
public static final int STATUS_OFFER_CHECK_FILESIZE = 0x206;
public static final int STATUS_UPLOADING = 0x207;
public boolean start();
public int getStatus();
public long getFileSize();
public int getProgress();
public String getMimeType();
public void cancel();
}

View file

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

View file

@ -0,0 +1,39 @@
package eu.siacs.conversations.entities;
public class DownloadablePlaceholder implements Downloadable {
private int status;
public DownloadablePlaceholder(int status) {
this.status = status;
}
@Override
public boolean start() {
return false;
}
@Override
public int getStatus() {
return status;
}
@Override
public long getFileSize() {
return 0;
}
@Override
public int getProgress() {
return 0;
}
@Override
public String getMimeType() {
return "";
}
@Override
public void cancel() {
}
}

View file

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

View file

@ -3,6 +3,7 @@ package eu.siacs.conversations.http;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.SystemClock;
import org.apache.http.conn.ssl.StrictHostnameVerifier;
@ -21,6 +22,7 @@ import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.X509TrustManager;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Downloadable;
import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.entities.Message;
@ -37,6 +39,8 @@ public class HttpConnection implements Downloadable {
private DownloadableFile file;
private int mStatus = Downloadable.STATUS_UNKNOWN;
private boolean acceptedAutomatically = false;
private int mProgress = 0;
private long mLastGuiRefresh = 0;
public HttpConnection(HttpConnectionManager manager) {
this.mHttpConnectionManager = manager;
@ -235,10 +239,14 @@ public class HttpConnection implements Downloadable {
if (os == null) {
throw new IOException();
}
long transmitted = 0;
long expected = file.getExpectedSize();
int count = -1;
byte[] buffer = new byte[1024];
while ((count = is.read(buffer)) != -1) {
transmitted += count;
os.write(buffer, 0, count);
updateProgress((int) ((((double) transmitted) / expected) * 100));
}
os.flush();
os.close();
@ -246,19 +254,21 @@ public class HttpConnection implements Downloadable {
}
private void updateImageBounds() {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file.getAbsolutePath(), options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
message.setBody(mUrl.toString() + "|" + file.getSize() + '|'
+ imageWidth + '|' + imageHeight);
message.setType(Message.TYPE_IMAGE);
mXmppConnectionService.getFileBackend().updateFileParams(message,mUrl);
mXmppConnectionService.updateMessage(message);
}
}
public void updateProgress(int i) {
this.mProgress = i;
if (SystemClock.elapsedRealtime() - this.mLastGuiRefresh > Config.PROGRESS_UI_UPDATE_INTERVAL) {
this.mLastGuiRefresh = SystemClock.elapsedRealtime();
mXmppConnectionService.updateConversationUi();
}
}
@Override
public int getStatus() {
return this.mStatus;
@ -272,4 +282,14 @@ public class HttpConnection implements Downloadable {
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 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 "
+ Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, "
@ -64,6 +64,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
+ " TEXT, " + Message.TRUE_COUNTERPART + " TEXT,"
+ Message.BODY + " TEXT, " + Message.ENCRYPTION + " NUMBER, "
+ Message.STATUS + " NUMBER," + Message.TYPE + " NUMBER, "
+ Message.RELATIVE_FILE_PATH + " TEXT, "
+ Message.REMOTE_MSG_ID + " TEXT, FOREIGN KEY("
+ Message.CONVERSATION + ") REFERENCES "
+ Conversation.TABLENAME + "(" + Conversation.UUID
@ -110,6 +111,10 @@ public class DatabaseBackend extends SQLiteOpenHelper {
db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
+ 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) {

View file

@ -7,6 +7,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@ -26,6 +27,8 @@ import android.provider.MediaStore;
import android.util.Base64;
import android.util.Base64OutputStream;
import android.util.Log;
import android.webkit.MimeTypeMap;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.DownloadableFile;
@ -53,22 +56,37 @@ public class FileBackend {
}
public DownloadableFile getFile(Message message, boolean decrypted) {
String path = message.getRelativeFilePath();
if (!decrypted && (message.getEncryption() == Message.ENCRYPTION_PGP || message.getEncryption() == Message.ENCRYPTION_DECRYPTED)) {
String extension;
if (path != null && !path.isEmpty()) {
String[] parts = path.split("\\.");
extension = "."+parts[parts.length - 1];
} else if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_TEXT) {
extension = ".webp";
} else {
extension = "";
}
return new DownloadableFile(getConversationsFileDirectory()+message.getUuid()+extension+".pgp");
} else if (path != null && !path.isEmpty()) {
if (path.startsWith("/")) {
return new DownloadableFile(path);
} else {
return new DownloadableFile(getConversationsFileDirectory()+path);
}
} else {
StringBuilder filename = new StringBuilder();
filename.append(getConversationsDirectory());
filename.append(message.getUuid());
if ((decrypted) || (message.getEncryption() == Message.ENCRYPTION_NONE)) {
filename.append(".webp");
} else {
if (message.getEncryption() == Message.ENCRYPTION_OTR) {
filename.append(".webp");
} else {
filename.append(".webp.pgp");
}
}
filename.append(getConversationsImageDirectory());
filename.append(message.getUuid()+".webp");
return new DownloadableFile(filename.toString());
}
}
public static String getConversationsDirectory() {
public static String getConversationsFileDirectory() {
return Environment.getExternalStorageDirectory().getAbsolutePath()+"/Conversations/";
}
public static String getConversationsImageDirectory() {
return Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES).getAbsolutePath()
+ "/Conversations/";
@ -103,13 +121,60 @@ public class FileBackend {
return Bitmap.createBitmap(bitmap, 0, 0, w, h, mtx, true);
}
public String getOriginalPath(Uri uri) {
String path = null;
if (uri.getScheme().equals("file")) {
return uri.getPath();
} else if (uri.toString().startsWith("content://media/")) {
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 copyFileToPrivateStorage(Message message, Uri uri) throws FileCopyException {
try {
Log.d(Config.LOGTAG, "copy " + uri.toString() + " to private storage");
String mime = mXmppConnectionService.getContentResolver().getType(uri);
String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mime);
message.setRelativeFilePath(message.getUuid() + "." + extension);
DownloadableFile file = mXmppConnectionService.getFileBackend().getFile(message);
OutputStream os = new FileOutputStream(file);
InputStream is = mXmppConnectionService.getContentResolver().openInputStream(uri);
byte[] buffer = new byte[1024];
int length;
while ((length = is.read(buffer)) > 0) {
os.write(buffer, 0, length);
}
os.flush();
os.close();
is.close();
Log.d(Config.LOGTAG, "output file name " + mXmppConnectionService.getFileBackend().getFile(message));
return file;
} catch (FileNotFoundException e) {
throw new FileCopyException(R.string.error_file_not_found);
} catch (IOException e) {
throw new FileCopyException(R.string.error_io_exception);
}
}
public DownloadableFile copyImageToPrivateStorage(Message message, Uri image)
throws ImageCopyException {
throws FileCopyException {
return this.copyImageToPrivateStorage(message, image, 0);
}
private DownloadableFile copyImageToPrivateStorage(Message message,
Uri image, int sampleSize) throws ImageCopyException {
Uri image, int sampleSize) throws FileCopyException {
try {
InputStream is = mXmppConnectionService.getContentResolver()
.openInputStream(image);
@ -125,7 +190,7 @@ public class FileBackend {
originalBitmap = BitmapFactory.decodeStream(is, null, options);
is.close();
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);
originalBitmap = null;
@ -137,7 +202,7 @@ public class FileBackend {
boolean success = scalledBitmap.compress(
Bitmap.CompressFormat.WEBP, 75, os);
if (!success) {
throw new ImageCopyException(R.string.error_compressing_image);
throw new FileCopyException(R.string.error_compressing_image);
}
os.flush();
os.close();
@ -147,18 +212,18 @@ public class FileBackend {
message.setBody(Long.toString(size) + ',' + width + ',' + height);
return file;
} catch (FileNotFoundException e) {
throw new ImageCopyException(R.string.error_file_not_found);
throw new FileCopyException(R.string.error_file_not_found);
} catch (IOException e) {
throw new ImageCopyException(R.string.error_io_exception);
throw new FileCopyException(R.string.error_io_exception);
} catch (SecurityException e) {
throw new ImageCopyException(
throw new FileCopyException(
R.string.error_security_exception_during_image_copy);
} catch (OutOfMemoryError e) {
++sampleSize;
if (sampleSize <= 3) {
return copyImageToPrivateStorage(message, image, sampleSize);
} else {
throw new ImageCopyException(R.string.error_out_of_memory);
throw new FileCopyException(R.string.error_out_of_memory);
}
}
}
@ -400,11 +465,34 @@ public class FileBackend {
return Uri.parse("file://" + file.getAbsolutePath());
}
public class ImageCopyException extends Exception {
public void updateFileParams(Message message) {
updateFileParams(message,null);
}
public void updateFileParams(Message message, URL url) {
DownloadableFile file = getFile(message);
if (message.getType() == Message.TYPE_IMAGE || file.getMimeType().startsWith("image/")) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file.getAbsolutePath(), options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
if (url == null) {
message.setBody(Long.toString(file.getSize()) + '|' + imageWidth + '|' + imageHeight);
} else {
message.setBody(url.toString()+"|"+Long.toString(file.getSize()) + '|' + imageWidth + '|' + imageHeight);
}
} else {
message.setBody(Long.toString(file.getSize()));
}
}
public class FileCopyException extends Exception {
private static final long serialVersionUID = -1010013599132881427L;
private int resId;
public ImageCopyException(int resId) {
public FileCopyException(int resId) {
this.resId = resId;
}

View file

@ -28,6 +28,7 @@ import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Downloadable;
import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.ui.ConversationActivity;
@ -267,14 +268,21 @@ public class NotificationService {
if (message.getDownloadable() != null
&& (message.getDownloadable().getStatus() == Downloadable.STATUS_OFFER || message
.getDownloadable().getStatus() == Downloadable.STATUS_OFFER_CHECK_FILESIZE)) {
if (message.getType() == Message.TYPE_FILE) {
return mXmppConnectionService.getString(R.string.file_offered_for_download);
} else {
return mXmppConnectionService.getText(
R.string.image_offered_for_download).toString();
}
} else if (message.getEncryption() == Message.ENCRYPTION_PGP) {
return mXmppConnectionService.getText(
R.string.encrypted_message_received).toString();
} else if (message.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
return mXmppConnectionService.getText(R.string.decryption_failed)
.toString();
} else if (message.getType() == Message.TYPE_FILE) {
DownloadableFile file = mXmppConnectionService.getFileBackend().getFile(message);
return mXmppConnectionService.getString(R.string.file,file.getMimeType());
} else if (message.getType() == Message.TYPE_IMAGE) {
return mXmppConnectionService.getText(R.string.image_file)
.toString();

View file

@ -56,6 +56,8 @@ import eu.siacs.conversations.entities.Bookmark;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Downloadable;
import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.entities.DownloadablePlaceholder;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.entities.MucOptions.OnRenameListener;
@ -211,7 +213,7 @@ public class XmppConnectionService extends Service {
private Integer rosterChangedListenerCount = 0;
private SecureRandom mRandom;
private FileObserver fileObserver = new FileObserver(
FileBackend.getConversationsDirectory()) {
FileBackend.getConversationsImageDirectory()) {
@Override
public void onEvent(int event, String path) {
@ -295,7 +297,49 @@ public class XmppConnectionService extends Service {
return this.mAvatarService;
}
public Message attachImageToConversation(final Conversation conversation,
public void attachFileToConversation(Conversation conversation, final Uri uri, final UiCallback<Message> callback) {
final 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);
String path = getFileBackend().getOriginalPath(uri);
if (path!=null) {
message.setRelativeFilePath(path);
getFileBackend().updateFileParams(message);
if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
getPgpEngine().encrypt(message, callback);
} else {
callback.success(message);
}
} else {
new Thread(new Runnable() {
@Override
public void run() {
try {
getFileBackend().copyFileToPrivateStorage(message, uri);
getFileBackend().updateFileParams(message);
if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
getPgpEngine().encrypt(message, callback);
} else {
callback.success(message);
}
} catch (FileBackend.FileCopyException e) {
callback.error(e.getResId(),message);
}
}
}).start();
}
}
public void attachImageToConversation(final Conversation conversation,
final Uri uri, final UiCallback<Message> callback) {
final Message message;
if (conversation.getNextEncryption(forceEncryption()) == Message.ENCRYPTION_PGP) {
@ -313,18 +357,17 @@ public class XmppConnectionService extends Service {
@Override
public void run() {
try {
getFileBackend().copyImageToPrivateStorage(message, uri);
DownloadableFile file = getFileBackend().copyImageToPrivateStorage(message, uri);
if (conversation.getNextEncryption(forceEncryption()) == Message.ENCRYPTION_PGP) {
getPgpEngine().encrypt(message, callback);
} else {
callback.success(message);
}
} catch (FileBackend.ImageCopyException e) {
} catch (FileBackend.FileCopyException e) {
callback.error(e.getResId(), message);
}
}
}).start();
return message;
}
public Conversation find(Bookmark bookmark) {
@ -561,7 +604,7 @@ public class XmppConnectionService extends Service {
boolean send = false;
if (account.getStatus() == Account.STATUS_ONLINE
&& 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.getEncryption() == Message.ENCRYPTION_OTR) {
if (!conv.hasValidOtrSession()) {
@ -678,12 +721,17 @@ public class XmppConnectionService extends Service {
} else {
if (message.getConversation().getOtrSession()
.getSessionStatus() == SessionStatus.ENCRYPTED) {
try {
message.setCounterpart(Jid.fromSessionID(message.getConversation().getOtrSession().getSessionID()));
if (message.getType() == Message.TYPE_TEXT) {
packet = mMessageGenerator.generateOtrChat(message,
true);
} else if (message.getType() == Message.TYPE_IMAGE) {
} else if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) {
mJingleConnectionManager.createNewConnection(message);
}
} catch (InvalidJidException e) {
}
}
}
} else if (message.getType() == Message.TYPE_TEXT) {
@ -693,7 +741,7 @@ public class XmppConnectionService extends Service {
|| (message.getEncryption() == Message.ENCRYPTION_PGP)) {
packet = mMessageGenerator.generatePgpChat(message, true);
}
} else if (message.getType() == Message.TYPE_IMAGE) {
} else if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) {
Contact contact = message.getConversation().getContact();
Presences presences = contact.getPresences();
if ((message.getCounterpart() != null)
@ -852,10 +900,10 @@ public class XmppConnectionService extends Service {
private void checkDeletedFiles(Conversation conversation) {
for (Message message : conversation.getMessages()) {
if (message.getType() == Message.TYPE_IMAGE
if ((message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE)
&& message.getEncryption() != Message.ENCRYPTION_PGP) {
if (!getFileBackend().isFileAvailable(message)) {
message.setDownloadable(new DeletedDownloadable());
message.setDownloadable(new DownloadablePlaceholder(Downloadable.STATUS_DELETED));
}
}
}
@ -868,7 +916,7 @@ public class XmppConnectionService extends Service {
&& message.getEncryption() != Message.ENCRYPTION_PGP
&& message.getUuid().equals(uuid)) {
if (!getFileBackend().isFileAvailable(message)) {
message.setDownloadable(new DeletedDownloadable());
message.setDownloadable(new DownloadablePlaceholder(Downloadable.STATUS_DELETED));
updateConversationUi();
}
return;
@ -1424,7 +1472,7 @@ public class XmppConnectionService extends Service {
databaseBackend.updateMessage(msg);
sendMessagePacket(account, outPacket);
}
} else if (msg.getType() == Message.TYPE_IMAGE) {
} else if (msg.getType() == Message.TYPE_IMAGE || msg.getType() == Message.TYPE_FILE) {
mJingleConnectionManager.createNewConnection(msg);
}
}
@ -1979,23 +2027,4 @@ public class XmppConnectionService extends Service {
return XmppConnectionService.this;
}
}
private class DeletedDownloadable implements Downloadable {
@Override
public boolean start() {
return false;
}
@Override
public int getStatus() {
return Downloadable.STATUS_DELETED;
}
@Override
public long getFileSize() {
return 0;
}
}
}

View file

@ -52,13 +52,14 @@ public class ConversationActivity extends XmppActivity implements
public static final int REQUEST_SEND_MESSAGE = 0x0201;
public static final int REQUEST_DECRYPT_PGP = 0x0202;
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_RECORD_AUDIO = 0x0205;
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_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_PANEL_OPEN = "state_panel_open";
private static final String STATE_PENDING_URI = "state_pending_uri";
@ -66,6 +67,7 @@ public class ConversationActivity extends XmppActivity implements
private String mOpenConverstaion = null;
private boolean mPanelOpen = true;
private Uri mPendingImageUri = null;
private Uri mPendingFileUri = null;
private View mContentView;
@ -76,7 +78,7 @@ public class ConversationActivity extends XmppActivity implements
private ArrayAdapter<Conversation> listAdapter;
private Toast prepareImageToast;
private Toast prepareFileToast;
public List<Conversation> getConversationList() {
@ -306,13 +308,18 @@ public class ConversationActivity extends XmppActivity implements
Intent attachFileIntent = new Intent();
attachFileIntent.setType("image/*");
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.setType("*/*");
attachFileIntent.addCategory(Intent.CATEGORY_OPENABLE);
attachFileIntent.setAction(Intent.ACTION_GET_CONTENT);
Intent chooser = Intent.createChooser(attachFileIntent,
getString(R.string.attach_file));
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);
break;
case R.id.attach_record_voice:
attachFile(ATTACHMENT_CHOICE_RECORD_VOICE);
attachFile(ATTACHMENT_CHOICE_CHOOSE_FILE);
break;
}
return false;
@ -675,14 +682,17 @@ public class ConversationActivity extends XmppActivity implements
} else {
showConversationsOverview();
mPendingImageUri = null;
mPendingFileUri = null;
setSelectedConversation(conversationList.get(0));
this.mConversationFragment.reInit(getSelectedConversation());
}
if (mPendingImageUri != null) {
attachImageToConversation(getSelectedConversation(),
mPendingImageUri);
attachImageToConversation(getSelectedConversation(),mPendingImageUri);
mPendingImageUri = null;
} else if (mPendingFileUri != null) {
attachFileToConversation(getSelectedConversation(),mPendingFileUri);
mPendingFileUri = null;
}
ExceptionHelper.checkForCrash(this, this.xmppConnectionService);
setIntent(new Intent());
@ -726,13 +736,20 @@ public class ConversationActivity extends XmppActivity implements
selectedFragment.hideSnackbar();
selectedFragment.updateMessages();
}
} else if (requestCode == REQUEST_ATTACH_FILE_DIALOG) {
} else if (requestCode == REQUEST_ATTACH_IMAGE_DIALOG) {
mPendingImageUri = data.getData();
if (xmppConnectionServiceBound) {
attachImageToConversation(getSelectedConversation(),
mPendingImageUri);
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 == ATTACHMENT_CHOICE_CHOOSE_IMAGE) {
@ -754,9 +771,6 @@ public class ConversationActivity extends XmppActivity implements
Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intent.setData(mPendingImageUri);
sendBroadcast(intent);
} else if (requestCode == REQUEST_RECORD_AUDIO) {
attachAudioToConversation(getSelectedConversation(),
data.getData());
}
} else {
if (requestCode == REQUEST_IMAGE_CAPTURE) {
@ -765,21 +779,40 @@ public class ConversationActivity extends XmppActivity implements
}
}
private void attachAudioToConversation(Conversation conversation, Uri uri) {
private void attachFileToConversation(Conversation conversation, Uri uri) {
prepareFileToast = Toast.makeText(getApplicationContext(),
getText(R.string.preparing_file), Toast.LENGTH_LONG);
prepareFileToast.show();
xmppConnectionService.attachFileToConversation(conversation,uri, new UiCallback<Message>() {
@Override
public void success(Message message) {
hidePrepareFileToast();
xmppConnectionService.sendMessage(message);
}
@Override
public void error(int errorCode, Message message) {
displayErrorDialog(errorCode);
}
@Override
public void userInputRequried(PendingIntent pi, Message message) {
}
});
}
private void attachImageToConversation(Conversation conversation, Uri uri) {
prepareImageToast = Toast.makeText(getApplicationContext(),
prepareFileToast = Toast.makeText(getApplicationContext(),
getText(R.string.preparing_image), Toast.LENGTH_LONG);
prepareImageToast.show();
prepareFileToast.show();
xmppConnectionService.attachImageToConversation(conversation, uri,
new UiCallback<Message>() {
@Override
public void userInputRequried(PendingIntent pi,
Message object) {
hidePrepareImageToast();
hidePrepareFileToast();
ConversationActivity.this.runIntent(pi,
ConversationActivity.REQUEST_SEND_PGP_IMAGE);
}
@ -791,19 +824,19 @@ public class ConversationActivity extends XmppActivity implements
@Override
public void error(int error, Message message) {
hidePrepareImageToast();
hidePrepareFileToast();
displayErrorDialog(error);
}
});
}
private void hidePrepareImageToast() {
if (prepareImageToast != null) {
private void hidePrepareFileToast() {
if (prepareFileToast != null) {
runOnUiThread(new Runnable() {
@Override
public void run() {
prepareImageToast.cancel();
prepareFileToast.cancel();
}
});
}

View file

@ -42,6 +42,9 @@ import eu.siacs.conversations.crypto.PgpEngine;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Downloadable;
import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.entities.DownloadablePlaceholder;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.entities.Presences;
@ -354,6 +357,7 @@ public class ConversationFragment extends Fragment {
MenuItem sendAgain = menu.findItem(R.id.send_again);
MenuItem copyUrl = menu.findItem(R.id.copy_url);
MenuItem downloadImage = menu.findItem(R.id.download_image);
MenuItem cancelTransmission = menu.findItem(R.id.cancel_transmission);
if (this.selectedMessage.getType() != Message.TYPE_TEXT
|| this.selectedMessage.getDownloadable() != null) {
copyText.setVisible(false);
@ -370,12 +374,15 @@ public class ConversationFragment extends Fragment {
|| this.selectedMessage.getImageParams().url == null) {
copyUrl.setVisible(false);
}
if (this.selectedMessage.getType() != Message.TYPE_TEXT
|| this.selectedMessage.getDownloadable() != null
|| !this.selectedMessage.bodyContainsDownloadable()) {
downloadImage.setVisible(false);
}
if (this.selectedMessage.getDownloadable() == null
|| this.selectedMessage.getDownloadable() instanceof DownloadablePlaceholder) {
cancelTransmission.setVisible(false);
}
}
}
@ -397,6 +404,9 @@ public class ConversationFragment extends Fragment {
case R.id.download_image:
downloadImage(selectedMessage);
return true;
case R.id.cancel_transmission:
cancelTransmission(selectedMessage);
return true;
default:
return super.onContextItemSelected(item);
}
@ -423,6 +433,14 @@ public class ConversationFragment extends Fragment {
}
private void resendMessage(Message message) {
if (message.getType() == Message.TYPE_FILE || message.getType() == Message.TYPE_IMAGE) {
DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message);
if (!file.exists()) {
Toast.makeText(activity,R.string.file_deleted,Toast.LENGTH_SHORT).show();
message.setDownloadable(new DownloadablePlaceholder(Downloadable.STATUS_DELETED));
return;
}
}
activity.xmppConnectionService.resendFailedMessages(message);
}
@ -439,6 +457,13 @@ public class ConversationFragment extends Fragment {
.createNewConnection(message);
}
private void cancelTransmission(Message message) {
Downloadable downloadable = message.getDownloadable();
if (downloadable!=null) {
downloadable.cancel();
}
}
protected void privateMessageWith(final Jid counterpart) {
this.mEditMessage.setText("");
this.conversation.setNextCounterpart(counterpart);

View file

@ -6,6 +6,7 @@ import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Downloadable;
import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.ui.ConversationActivity;
import eu.siacs.conversations.ui.XmppActivity;
@ -75,7 +76,7 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
convName.setTypeface(null, Typeface.NORMAL);
}
if (message.getType() == Message.TYPE_IMAGE
if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE
|| message.getDownloadable() != null) {
Downloadable d = message.getDownloadable();
if (conversation.isRead()) {
@ -89,13 +90,35 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
if (d.getStatus() == Downloadable.STATUS_CHECKING) {
mLastMessage.setText(R.string.checking_image);
} else if (d.getStatus() == Downloadable.STATUS_DOWNLOADING) {
mLastMessage.setText(R.string.receiving_image);
if (message.getType() == Message.TYPE_FILE) {
mLastMessage.setText(getContext().getString(R.string.receiving_file,d.getMimeType(), d.getProgress()));
} else {
mLastMessage.setText(getContext().getString(R.string.receiving_image, d.getProgress()));
}
} else if (d.getStatus() == Downloadable.STATUS_OFFER) {
if (message.getType() == Message.TYPE_FILE) {
mLastMessage.setText(R.string.file_offered_for_download);
} else {
mLastMessage.setText(R.string.image_offered_for_download);
}
} else if (d.getStatus() == Downloadable.STATUS_OFFER_CHECK_FILESIZE) {
mLastMessage.setText(R.string.image_offered_for_download);
} else if (d.getStatus() == Downloadable.STATUS_DELETED) {
if (message.getType() == Message.TYPE_FILE) {
mLastMessage.setText(R.string.file_deleted);
} else {
mLastMessage.setText(R.string.image_file_deleted);
}
} else if (d.getStatus() == Downloadable.STATUS_FAILED) {
if (message.getType() == Message.TYPE_FILE) {
mLastMessage.setText(R.string.file_transmission_failed);
} else {
mLastMessage.setText(R.string.image_transmission_failed);
}
} else if (message.getImageParams().width > 0) {
mLastMessage.setVisibility(View.GONE);
imagePreview.setVisibility(View.VISIBLE);
activity.loadBitmap(message, imagePreview);
} else {
mLastMessage.setText("");
}
@ -103,6 +126,11 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
imagePreview.setVisibility(View.GONE);
mLastMessage.setVisibility(View.VISIBLE);
mLastMessage.setText(R.string.encrypted_message_received);
} else if (message.getType() == Message.TYPE_FILE && message.getImageParams().width <= 0) {
DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message);
mLastMessage.setVisibility(View.VISIBLE);
imagePreview.setVisibility(View.GONE);
mLastMessage.setText(getContext().getString(R.string.file,file.getMimeType()));
} else {
mLastMessage.setVisibility(View.GONE);
imagePreview.setVisibility(View.VISIBLE);

View file

@ -1,16 +1,21 @@
package eu.siacs.conversations.ui.adapter;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Typeface;
import android.net.Uri;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.webkit.MimeTypeMap;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
@ -18,6 +23,9 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.List;
import eu.siacs.conversations.Config;
@ -25,6 +33,7 @@ import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Downloadable;
import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.Message.ImageParams;
import eu.siacs.conversations.ui.ConversationActivity;
@ -96,10 +105,11 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}
boolean multiReceived = message.getConversation().getMode() == Conversation.MODE_MULTI
&& message.getMergedStatus() <= Message.STATUS_RECEIVED;
if (message.getType() == Message.TYPE_IMAGE
|| message.getDownloadable() != null) {
if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE || message.getDownloadable() != null) {
ImageParams params = message.getImageParams();
if (params.size != 0) {
if (params.size > (1.5 * 1024 * 1024)) {
filesize = params.size / (1024 * 1024)+ " MB";
} else if (params.size > 0) {
filesize = params.size / 1024 + " KB";
}
if (message.getDownloadable() != null && message.getDownloadable().getStatus() == Downloadable.STATUS_FAILED) {
@ -111,7 +121,12 @@ public class MessageAdapter extends ArrayAdapter<Message> {
info = getContext().getString(R.string.waiting);
break;
case Message.STATUS_UNSEND:
Downloadable d = message.getDownloadable();
if (d!=null) {
info = getContext().getString(R.string.sending_file,d.getProgress());
} else {
info = getContext().getString(R.string.sending);
}
break;
case Message.STATUS_OFFERED:
info = getContext().getString(R.string.offering);
@ -181,13 +196,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) {
viewHolder.download_button.setVisibility(View.GONE);
}
viewHolder.image.setVisibility(View.GONE);
viewHolder.messageBody.setVisibility(View.VISIBLE);
viewHolder.messageBody.setText(getContext().getString(r));
viewHolder.messageBody.setText(text);
viewHolder.messageBody.setTextColor(activity.getSecondaryTextColor());
viewHolder.messageBody.setTypeface(null, Typeface.ITALIC);
viewHolder.messageBody.setTextIsSelectable(false);
@ -252,11 +267,11 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}
private void displayDownloadableMessage(ViewHolder viewHolder,
final Message message, int resid) {
final Message message, String text) {
viewHolder.image.setVisibility(View.GONE);
viewHolder.messageBody.setVisibility(View.GONE);
viewHolder.download_button.setVisibility(View.VISIBLE);
viewHolder.download_button.setText(resid);
viewHolder.download_button.setText(text);
viewHolder.download_button.setOnClickListener(new OnClickListener() {
@Override
@ -267,6 +282,22 @@ public class MessageAdapter extends ArrayAdapter<Message> {
viewHolder.download_button.setOnLongClickListener(openContextMenu);
}
private void displayOpenableMessage(ViewHolder viewHolder,final Message message) {
final DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(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,file.getMimeType()));
viewHolder.download_button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
openDonwloadable(file);
}
});
viewHolder.download_button.setOnLongClickListener(openContextMenu);
}
private void displayImageMessage(ViewHolder viewHolder,
final Message message) {
if (viewHolder.download_button != null) {
@ -455,42 +486,51 @@ public class MessageAdapter extends ArrayAdapter<Message> {
});
}
if (item.getType() == Message.TYPE_IMAGE
|| item.getDownloadable() != null) {
if (item.getDownloadable() != null && item.getDownloadable().getStatus() != Downloadable.STATUS_UPLOADING) {
Downloadable d = item.getDownloadable();
if (d != null && d.getStatus() == Downloadable.STATUS_DOWNLOADING) {
displayInfoMessage(viewHolder, R.string.receiving_image);
} else if (d != null
&& d.getStatus() == Downloadable.STATUS_CHECKING) {
displayInfoMessage(viewHolder, R.string.checking_image);
} else if (d != null
&& d.getStatus() == Downloadable.STATUS_DELETED) {
displayInfoMessage(viewHolder, R.string.image_file_deleted);
} else if (d != null && d.getStatus() == Downloadable.STATUS_OFFER) {
displayDownloadableMessage(viewHolder, item,
R.string.download_image);
} else if (d != null
&& d.getStatus() == Downloadable.STATUS_OFFER_CHECK_FILESIZE) {
displayDownloadableMessage(viewHolder, item,
R.string.check_image_filesize);
} else if (d != null && d.getStatus() == Downloadable.STATUS_FAILED) {
displayInfoMessage(viewHolder, R.string.image_transmission_failed);
} else if ((item.getEncryption() == Message.ENCRYPTION_DECRYPTED)
|| (item.getEncryption() == Message.ENCRYPTION_NONE)
|| (item.getEncryption() == Message.ENCRYPTION_OTR)) {
displayImageMessage(viewHolder, item);
} else if (item.getEncryption() == Message.ENCRYPTION_PGP) {
displayInfoMessage(viewHolder, R.string.encrypted_message);
if (d.getStatus() == Downloadable.STATUS_DOWNLOADING) {
if (item.getType() == Message.TYPE_FILE) {
displayInfoMessage(viewHolder,activity.getString(R.string.receiving_file,d.getMimeType(),d.getProgress()));
} else {
displayDecryptionFailed(viewHolder);
displayInfoMessage(viewHolder,activity.getString(R.string.receiving_image,d.getProgress()));
}
} else if (d.getStatus() == Downloadable.STATUS_CHECKING) {
displayInfoMessage(viewHolder,activity.getString(R.string.checking_image));
} else if (d.getStatus() == Downloadable.STATUS_DELETED) {
if (item.getType() == Message.TYPE_FILE) {
displayInfoMessage(viewHolder, activity.getString(R.string.file_deleted));
} else {
if (item.getEncryption() == Message.ENCRYPTION_PGP) {
displayInfoMessage(viewHolder, activity.getString(R.string.image_file_deleted));
}
} else if (d.getStatus() == Downloadable.STATUS_OFFER) {
if (item.getType() == Message.TYPE_FILE) {
displayDownloadableMessage(viewHolder,item,activity.getString(R.string.download_file,d.getMimeType()));
} else {
displayDownloadableMessage(viewHolder, item,activity.getString(R.string.download_image));
}
} else if (d.getStatus() == Downloadable.STATUS_OFFER_CHECK_FILESIZE) {
displayDownloadableMessage(viewHolder, item,activity.getString(R.string.check_image_filesize));
} else if (d.getStatus() == Downloadable.STATUS_FAILED) {
if (item.getType() == Message.TYPE_FILE) {
displayInfoMessage(viewHolder, activity.getString(R.string.file_transmission_failed));
} else {
displayInfoMessage(viewHolder, activity.getString(R.string.image_transmission_failed));
}
}
} else if (item.getType() == Message.TYPE_IMAGE && item.getEncryption() != Message.ENCRYPTION_PGP && item.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) {
displayImageMessage(viewHolder, item);
} else if (item.getType() == Message.TYPE_FILE && item.getEncryption() != Message.ENCRYPTION_PGP && item.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) {
if (item.getImageParams().width > 0) {
displayImageMessage(viewHolder,item);
} else {
displayOpenableMessage(viewHolder, item);
}
} else if (item.getEncryption() == Message.ENCRYPTION_PGP) {
if (activity.hasPgp()) {
displayInfoMessage(viewHolder, R.string.encrypted_message);
displayInfoMessage(viewHolder,activity.getString(R.string.encrypted_message));
} else {
displayInfoMessage(viewHolder,
R.string.install_openkeychain);
activity.getString(R.string.install_openkeychain));
if (viewHolder != null) {
viewHolder.message_box
.setOnClickListener(new OnClickListener() {
@ -507,7 +547,6 @@ public class MessageAdapter extends ArrayAdapter<Message> {
} else {
displayTextMessage(viewHolder, item);
}
}
displayStatus(viewHolder, item);
@ -524,6 +563,22 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}
}
public void openDonwloadable(DownloadableFile file) {
if (!file.exists()) {
Toast.makeText(activity,R.string.file_deleted,Toast.LENGTH_SHORT).show();
return;
}
Intent openIntent = new Intent(Intent.ACTION_VIEW);
openIntent.setDataAndType(Uri.fromFile(file), file.getMimeType());
PackageManager manager = activity.getPackageManager();
List<ResolveInfo> infos = manager.queryIntentActivities(openIntent, 0);
if (infos.size() > 0) {
getContext().startActivity(openIntent);
} else {
Toast.makeText(activity,R.string.no_application_found_to_open_file,Toast.LENGTH_SHORT).show();
}
}
public interface OnContactPictureClicked {
public void onContactPictureClicked(Message message);
}

View file

@ -1,5 +1,6 @@
package eu.siacs.conversations.xmpp.jingle;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
@ -9,14 +10,15 @@ import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.SystemClock;
import android.util.Log;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Downloadable;
import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.entities.DownloadablePlaceholder;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.xml.Element;
@ -29,9 +31,6 @@ import eu.siacs.conversations.xmpp.stanzas.IqPacket;
public class JingleConnection implements Downloadable {
private final String[] extensions = { "webp", "jpeg", "jpg", "png" };
private final String[] cryptoExtensions = { "pgp", "gpg", "otr" };
private JingleConnectionManager mJingleConnectionManager;
private XmppConnectionService mXmppConnectionService;
@ -46,7 +45,7 @@ public class JingleConnection implements Downloadable {
private int ibbBlockSize = 4096;
private int mJingleStatus = -1;
private int mStatus = -1;
private int mStatus = Downloadable.STATUS_UNKNOWN;
private Message message;
private String sessionId;
private Account account;
@ -62,6 +61,9 @@ public class JingleConnection implements Downloadable {
private String contentName;
private String contentCreator;
private int mProgress = 0;
private long mLastGuiRefresh = 0;
private boolean receivedCandidate = false;
private boolean sentCandidate = false;
@ -74,7 +76,7 @@ public class JingleConnection implements Downloadable {
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
if (packet.getType() == IqPacket.TYPE_ERROR) {
cancel();
fail();
}
}
};
@ -90,16 +92,14 @@ public class JingleConnection implements Downloadable {
JingleConnection.this.mXmppConnectionService
.getNotificationService().push(message);
}
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file.getAbsolutePath(), options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
message.setBody(Long.toString(file.getSize()) + '|'
+ imageWidth + '|' + imageHeight);
mXmppConnectionService.getFileBackend().updateFileParams(message);
mXmppConnectionService.databaseBackend.createMessage(message);
mXmppConnectionService.markMessage(message,
Message.STATUS_RECEIVED);
} else {
if (message.getEncryption() == Message.ENCRYPTION_PGP || message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
file.delete();
}
}
Log.d(Config.LOGTAG,
"sucessfully transmitted file:" + file.getAbsolutePath());
@ -114,7 +114,7 @@ public class JingleConnection implements Downloadable {
@Override
public void onFileTransferAborted() {
JingleConnection.this.sendCancel();
JingleConnection.this.cancel();
JingleConnection.this.fail();
}
};
@ -161,14 +161,14 @@ public class JingleConnection implements Downloadable {
Reason reason = packet.getReason();
if (reason != null) {
if (reason.hasChild("cancel")) {
this.cancel();
this.fail();
} else if (reason.hasChild("success")) {
this.receiveSuccess();
} else {
this.cancel();
this.fail();
}
} else {
this.cancel();
this.fail();
}
} else if (packet.isAction("session-accept")) {
returnResult = receiveAccept(packet);
@ -203,6 +203,8 @@ public class JingleConnection implements Downloadable {
this.contentCreator = "initiator";
this.contentName = this.mJingleConnectionManager.nextRandomId();
this.message = message;
this.message.setDownloadable(this);
this.mStatus = Downloadable.STATUS_UPLOADING;
this.account = message.getConversation().getAccount();
this.initiator = this.account.getJid();
this.responder = this.message.getCounterpart();
@ -258,7 +260,6 @@ public class JingleConnection implements Downloadable {
packet.getFrom().toBareJid(), false);
this.message = new Message(conversation, "", Message.ENCRYPTION_NONE);
this.message.setStatus(Message.STATUS_RECEIVED);
this.message.setType(Message.TYPE_IMAGE);
this.mStatus = Downloadable.STATUS_OFFER;
this.message.setDownloadable(this);
final Jid from = packet.getFrom();
@ -278,30 +279,42 @@ public class JingleConnection implements Downloadable {
Element fileSize = fileOffer.findChild("size");
Element fileNameElement = fileOffer.findChild("name");
if (fileNameElement != null) {
boolean supportedFile = false;
String[] filename = fileNameElement.getContent()
.toLowerCase(Locale.US).split("\\.");
if (Arrays.asList(this.extensions).contains(
if (Arrays.asList(VALID_IMAGE_EXTENSIONS).contains(
filename[filename.length - 1])) {
supportedFile = true;
} else if (Arrays.asList(this.cryptoExtensions).contains(
message.setType(Message.TYPE_IMAGE);
} else if (Arrays.asList(VALID_CRYPTO_EXTENSIONS).contains(
filename[filename.length - 1])) {
if (filename.length == 3) {
if (Arrays.asList(this.extensions).contains(
if (Arrays.asList(VALID_IMAGE_EXTENSIONS).contains(
filename[filename.length - 2])) {
supportedFile = true;
if (filename[filename.length - 1].equals("otr")) {
Log.d(Config.LOGTAG, "receiving otr file");
this.message
.setEncryption(Message.ENCRYPTION_OTR);
message.setType(Message.TYPE_IMAGE);
} else {
this.message
.setEncryption(Message.ENCRYPTION_PGP);
message.setType(Message.TYPE_FILE);
}
if (filename[filename.length - 1].equals("otr")) {
message.setEncryption(Message.ENCRYPTION_OTR);
} else {
message.setEncryption(Message.ENCRYPTION_PGP);
}
}
} else {
message.setType(Message.TYPE_FILE);
}
if (message.getType() == Message.TYPE_FILE) {
String suffix = "";
if (!fileNameElement.getContent().isEmpty()) {
String parts[] = fileNameElement.getContent().split("/");
suffix = parts[parts.length - 1];
if (message.getEncryption() == Message.ENCRYPTION_OTR && suffix.endsWith(".otr")) {
suffix = suffix.substring(0,suffix.length() - 4);
} else if (message.getEncryption() == Message.ENCRYPTION_PGP && (suffix.endsWith(".pgp") || suffix.endsWith(".gpg"))) {
suffix = suffix.substring(0,suffix.length() - 4);
}
}
if (supportedFile) {
message.setRelativeFilePath(message.getUuid()+"_"+suffix);
}
long size = Long.parseLong(fileSize.getContent());
message.setBody(Long.toString(size));
conversation.add(message);
@ -329,7 +342,7 @@ public class JingleConnection implements Downloadable {
byte[] key = conversation.getSymmetricKey();
if (key == null) {
this.sendCancel();
this.cancel();
this.fail();
return;
} else {
this.file.setKey(key);
@ -338,15 +351,11 @@ public class JingleConnection implements Downloadable {
this.file.setExpectedSize(size);
} else {
this.sendCancel();
this.cancel();
this.fail();
}
} else {
this.sendCancel();
this.cancel();
}
} else {
this.sendCancel();
this.cancel();
this.fail();
}
}
@ -354,7 +363,7 @@ public class JingleConnection implements Downloadable {
this.mXmppConnectionService.markMessage(this.message, Message.STATUS_OFFERED);
JinglePacket packet = this.bootstrapPacket("session-initiate");
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);
this.file = this.mXmppConnectionService.getFileBackend().getFile(
message, false);
@ -485,7 +494,7 @@ public class JingleConnection implements Downloadable {
} else {
Log.d(Config.LOGTAG, "activated connection not found");
this.sendCancel();
this.cancel();
this.fail();
}
}
return true;
@ -532,7 +541,7 @@ public class JingleConnection implements Downloadable {
this.transport = connection;
if (connection == null) {
Log.d(Config.LOGTAG, "could not find suitable candidate");
this.disconnect();
this.disconnectSocks5Connections();
if (this.initiator.equals(account.getJid())) {
this.sendFallbackToIbb();
}
@ -623,7 +632,7 @@ public class JingleConnection implements Downloadable {
reason.addChild("success");
packet.setReason(reason);
this.sendJinglePacket(packet);
this.disconnect();
this.disconnectSocks5Connections();
this.mJingleStatus = JINGLE_STATUS_FINISHED;
this.message.setStatus(Message.STATUS_RECEIVED);
this.message.setDownloadable(null);
@ -654,8 +663,7 @@ public class JingleConnection implements Downloadable {
}
}
this.transportId = packet.getJingleContent().getTransportId();
this.transport = new JingleInbandTransport(this.account,
this.responder, this.transportId, this.ibbBlockSize);
this.transport = new JingleInbandTransport(this, this.transportId, this.ibbBlockSize);
this.transport.receive(file, onFileTransmissionSatusChanged);
JinglePacket answer = bootstrapPacket("transport-accept");
Content content = new Content("initiator", "a-file-offer");
@ -677,8 +685,7 @@ public class JingleConnection implements Downloadable {
this.ibbBlockSize = bs;
}
}
this.transport = new JingleInbandTransport(this.account,
this.responder, this.transportId, this.ibbBlockSize);
this.transport = new JingleInbandTransport(this, this.transportId, this.ibbBlockSize);
this.transport.connect(new OnTransportConnected() {
@Override
@ -702,20 +709,51 @@ public class JingleConnection implements Downloadable {
this.mJingleStatus = JINGLE_STATUS_FINISHED;
this.mXmppConnectionService.markMessage(this.message,
Message.STATUS_SEND);
this.disconnect();
this.disconnectSocks5Connections();
if (this.transport != null && this.transport instanceof JingleInbandTransport) {
this.transport.disconnect();
}
this.message.setDownloadable(null);
this.mJingleConnectionManager.finishConnection(this);
}
public void cancel() {
this.mJingleStatus = JINGLE_STATUS_CANCELED;
this.disconnect();
if (this.message != null) {
this.disconnectSocks5Connections();
if (this.transport != null && this.transport instanceof JingleInbandTransport) {
this.transport.disconnect();
}
this.sendCancel();
this.mJingleConnectionManager.finishConnection(this);
if (this.responder.equals(account.getJid())) {
this.mStatus = Downloadable.STATUS_FAILED;
this.message.setDownloadable(new DownloadablePlaceholder(Downloadable.STATUS_FAILED));
if (this.file!=null) {
file.delete();
}
this.mXmppConnectionService.updateConversationUi();
} else {
this.mXmppConnectionService.markMessage(this.message,
Message.STATUS_SEND_FAILED);
this.message.setDownloadable(null);
}
}
private void fail() {
this.mJingleStatus = JINGLE_STATUS_FAILED;
this.disconnectSocks5Connections();
if (this.transport != null && this.transport instanceof JingleInbandTransport) {
this.transport.disconnect();
}
if (this.message != null) {
if (this.responder.equals(account.getJid())) {
this.message.setDownloadable(new DownloadablePlaceholder(Downloadable.STATUS_FAILED));
if (this.file!=null) {
file.delete();
}
this.mXmppConnectionService.updateConversationUi();
} else {
this.mXmppConnectionService.markMessage(this.message,
Message.STATUS_SEND_FAILED);
this.message.setDownloadable(null);
}
}
this.mJingleConnectionManager.finishConnection(this);
@ -764,7 +802,7 @@ public class JingleConnection implements Downloadable {
});
}
private void disconnect() {
private void disconnectSocks5Connections() {
Iterator<Entry<String, JingleSocks5Transport>> it = this.connections
.entrySet().iterator();
while (it.hasNext()) {
@ -856,6 +894,14 @@ public class JingleConnection implements Downloadable {
return null;
}
public void updateProgress(int i) {
this.mProgress = i;
if (SystemClock.elapsedRealtime() - this.mLastGuiRefresh > Config.PROGRESS_UI_UPDATE_INTERVAL) {
this.mLastGuiRefresh = SystemClock.elapsedRealtime();
mXmppConnectionService.updateConversationUi();
}
}
interface OnProxyActivated {
public void success();
@ -900,4 +946,29 @@ public class JingleConnection implements Downloadable {
return 0;
}
}
@Override
public int getProgress() {
return this.mProgress;
}
@Override
public String getMimeType() {
if (this.message.getType() == Message.TYPE_FILE) {
String mime = null;
String path = this.message.getRelativeFilePath();
if (path != null && !this.message.getRelativeFilePath().isEmpty()) {
mime = URLConnection.guessContentTypeFromName(this.message.getRelativeFilePath());
if (mime!=null) {
return mime;
} else {
return "";
}
} else {
return "";
}
} else {
return "image/webp";
}
}
}

View file

@ -75,6 +75,10 @@ public class JingleConnectionManager extends AbstractConnectionManager {
public void getPrimaryCandidate(Account account,
final OnPrimaryCandidateFound listener) {
if (Config.NO_PROXY_LOOKUP) {
listener.onPrimaryCandidateFound(false, null);
return;
}
if (!this.primaryCandidates.containsKey(account.getJid().toBareJid())) {
String xmlns = "http://jabber.org/protocol/bytestreams";
final String proxy = account.getXmppConnection()

View file

@ -8,6 +8,7 @@ import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import android.util.Base64;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.utils.CryptoHelper;
@ -27,11 +28,15 @@ public class JingleInbandTransport extends JingleTransport {
private boolean established = false;
private boolean connected = true;
private DownloadableFile file;
private JingleConnection connection;
private InputStream fileInputStream = null;
private OutputStream fileOutputStream;
private long remainingSize;
private OutputStream fileOutputStream = null;
private long remainingSize = 0;
private long fileSize = 0;
private MessageDigest digest;
private OnFileTransmissionStatusChanged onFileTransmissionStatusChanged;
@ -39,16 +44,16 @@ public class JingleInbandTransport extends JingleTransport {
private OnIqPacketReceived onAckReceived = new OnIqPacketReceived() {
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
if (packet.getType() == IqPacket.TYPE_RESULT) {
if (connected && packet.getType() == IqPacket.TYPE_RESULT) {
sendNextBlock();
}
}
};
public JingleInbandTransport(final Account account, final Jid counterpart,
final String sid, final int blocksize) {
this.account = account;
this.counterpart = counterpart;
public JingleInbandTransport(final JingleConnection connection, final String sid, final int blocksize) {
this.connection = connection;
this.account = connection.getAccount();
this.counterpart = connection.getCounterPart();
this.blockSize = blocksize;
this.bufferSize = blocksize / 4;
this.sessionId = sid;
@ -61,7 +66,7 @@ public class JingleInbandTransport extends JingleTransport {
open.setAttribute("sid", this.sessionId);
open.setAttribute("stanza", "iq");
open.setAttribute("block-size", Integer.toString(this.blockSize));
this.connected = true;
this.account.getXmppConnection().sendIqPacket(iq,
new OnIqPacketReceived() {
@ -92,7 +97,7 @@ public class JingleInbandTransport extends JingleTransport {
callback.onFileTransferAborted();
return;
}
this.remainingSize = file.getExpectedSize();
this.remainingSize = this.fileSize = file.getExpectedSize();
} catch (final NoSuchAlgorithmException | IOException e) {
callback.onFileTransferAborted();
}
@ -104,6 +109,8 @@ public class JingleInbandTransport extends JingleTransport {
this.onFileTransmissionStatusChanged = callback;
this.file = file;
try {
this.remainingSize = this.file.getSize();
this.fileSize = this.remainingSize;
this.digest = MessageDigest.getInstance("SHA-1");
this.digest.reset();
fileInputStream = this.file.createInputStream();
@ -111,12 +118,33 @@ public class JingleInbandTransport extends JingleTransport {
callback.onFileTransferAborted();
return;
}
if (this.connected) {
this.sendNextBlock();
}
} catch (NoSuchAlgorithmException e) {
callback.onFileTransferAborted();
}
}
@Override
public void disconnect() {
this.connected = false;
if (this.fileOutputStream != null) {
try {
this.fileOutputStream.close();
} catch (IOException e) {
}
}
if (this.fileInputStream != null) {
try {
this.fileInputStream.close();
} catch (IOException e) {
}
}
}
private void sendNextBlock() {
byte[] buffer = new byte[this.bufferSize];
try {
@ -126,6 +154,7 @@ public class JingleInbandTransport extends JingleTransport {
fileInputStream.close();
this.onFileTransmissionStatusChanged.onFileTransmitted(file);
} else {
this.remainingSize -= count;
this.digest.update(buffer);
String base64 = Base64.encodeToString(buffer, Base64.NO_WRAP);
IqPacket iq = new IqPacket(IqPacket.TYPE_SET);
@ -140,6 +169,7 @@ public class JingleInbandTransport extends JingleTransport {
this.account.getXmppConnection().sendIqPacket(iq,
this.onAckReceived);
this.seq++;
connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100));
}
} catch (IOException e) {
this.onFileTransmissionStatusChanged.onFileTransferAborted();
@ -155,6 +185,7 @@ public class JingleInbandTransport extends JingleTransport {
}
this.remainingSize -= buffer.length;
this.fileOutputStream.write(buffer);
this.digest.update(buffer);
@ -163,6 +194,8 @@ public class JingleInbandTransport extends JingleTransport {
fileOutputStream.flush();
fileOutputStream.close();
this.onFileTransmissionStatusChanged.onFileTransmitted(file);
} else {
connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100));
}
} catch (IOException e) {
this.onFileTransmissionStatusChanged.onFileTransferAborted();
@ -173,13 +206,14 @@ public class JingleInbandTransport extends JingleTransport {
if (payload.getName().equals("open")) {
if (!established) {
established = true;
connected = true;
this.account.getXmppConnection().sendIqPacket(
packet.generateRespone(IqPacket.TYPE_RESULT), null);
} else {
this.account.getXmppConnection().sendIqPacket(
packet.generateRespone(IqPacket.TYPE_ERROR), null);
}
} else if (payload.getName().equals("data")) {
} else if (connected && payload.getName().equals("data")) {
this.receiveNextBlock(payload.getContent());
this.account.getXmppConnection().sendIqPacket(
packet.generateRespone(IqPacket.TYPE_RESULT), null);

View file

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

View file

@ -10,4 +10,6 @@ public abstract class JingleTransport {
public abstract void send(final DownloadableFile file,
final OnFileTransmissionStatusChanged callback);
public abstract void disconnect();
}

View file

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

View file

@ -16,5 +16,8 @@
<item
android:id="@+id/download_image"
android:title="@string/download_image"/>
<item
android:id="@+id/cancel_transmission"
android:title="@string/cancel_transmission" />
</menu>

View file

@ -58,7 +58,7 @@
<string name="add_contact">Add contact</string>
<string name="send_failed">delivery failed</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="action_clear_history">Clear history</string>
<string name="clear_conversation_history">Clear Conversation History</string>
@ -332,4 +332,16 @@
<string name="touch_to_disable">Touch to disable foreground service</string>
<string name="pref_keep_foreground_service">Keep service in foreground</string>
<string name="pref_keep_foreground_service_summary">Prevents the operating system from killing your connection</string>
<string name="choose_file">Choose file</string>
<string name="receiving_file">Receiving %1$s file (%2$d%% completed)</string>
<string name="download_file">Download %s file</string>
<string name="open_file">Open %s file</string>
<string name="sending_file">sending (%1$d%% completed)</string>
<string name="preparing_file">Preparing file for transmission</string>
<string name="file_offered_for_download">File offered for download</string>
<string name="file">%s file</string>
<string name="cancel_transmission">Cancel transmission</string>
<string name="file_transmission_failed">file transmission failed</string>
<string name="file_deleted">The file has been deleted</string>
<string name="no_application_found_to_open_file">No application found to open file</string>
</resources>