aboutsummaryrefslogtreecommitdiffstats
path: root/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/eu/siacs/conversations/Config.java3
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/PgpEngine.java27
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Downloadable.java9
-rw-r--r--src/main/java/eu/siacs/conversations/entities/DownloadableFile.java14
-rw-r--r--src/main/java/eu/siacs/conversations/entities/DownloadablePlaceholder.java39
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Message.java25
-rw-r--r--src/main/java/eu/siacs/conversations/http/HttpConnection.java34
-rw-r--r--src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java7
-rw-r--r--src/main/java/eu/siacs/conversations/persistance/FileBackend.java134
-rw-r--r--src/main/java/eu/siacs/conversations/services/NotificationService.java12
-rw-r--r--src/main/java/eu/siacs/conversations/services/XmppConnectionService.java99
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConversationActivity.java77
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConversationFragment.java27
-rw-r--r--src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java36
-rw-r--r--src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java165
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java235
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java4
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java56
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java22
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/jingle/JingleTransport.java2
-rw-r--r--src/main/res/menu/attachment_choices.xml3
-rw-r--r--src/main/res/menu/message_context.xml3
-rw-r--r--src/main/res/values/strings.xml14
23 files changed, 772 insertions, 275 deletions
diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java
index 7dd5a7995..7af29451f 100644
--- a/src/main/java/eu/siacs/conversations/Config.java
+++ b/src/main/java/eu/siacs/conversations/Config.java
@@ -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() {
diff --git a/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java b/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java
index 3d7cc6715..83d9b7b2e 100644
--- a/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java
+++ b/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java
@@ -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);
diff --git a/src/main/java/eu/siacs/conversations/entities/Downloadable.java b/src/main/java/eu/siacs/conversations/entities/Downloadable.java
index e4c853367..d25bf93a8 100644
--- a/src/main/java/eu/siacs/conversations/entities/Downloadable.java
+++ b/src/main/java/eu/siacs/conversations/entities/Downloadable.java
@@ -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();
}
diff --git a/src/main/java/eu/siacs/conversations/entities/DownloadableFile.java b/src/main/java/eu/siacs/conversations/entities/DownloadableFile.java
index 1605c75b4..25f339071 100644
--- a/src/main/java/eu/siacs/conversations/entities/DownloadableFile.java
+++ b/src/main/java/eu/siacs/conversations/entities/DownloadableFile.java
@@ -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;
}
diff --git a/src/main/java/eu/siacs/conversations/entities/DownloadablePlaceholder.java b/src/main/java/eu/siacs/conversations/entities/DownloadablePlaceholder.java
new file mode 100644
index 000000000..03fceceb7
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/entities/DownloadablePlaceholder.java
@@ -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() {
+
+ }
+}
diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java
index 5b44435e8..33f3443b7 100644
--- a/src/main/java/eu/siacs/conversations/entities/Message.java
+++ b/src/main/java/eu/siacs/conversations/entities/Message.java
@@ -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 {
diff --git a/src/main/java/eu/siacs/conversations/http/HttpConnection.java b/src/main/java/eu/siacs/conversations/http/HttpConnection.java
index 147ac42f2..68c26c474 100644
--- a/src/main/java/eu/siacs/conversations/http/HttpConnection.java
+++ b/src/main/java/eu/siacs/conversations/http/HttpConnection.java
@@ -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 "";
+ }
} \ No newline at end of file
diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
index 310c8f420..38f4fdf13 100644
--- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
+++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
@@ -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) {
diff --git a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java
index 0241b77e8..9683d38d2 100644
--- a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java
+++ b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java
@@ -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,25 +56,40 @@ public class FileBackend {
}
public DownloadableFile getFile(Message message, boolean decrypted) {
- 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");
+ 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 {
- filename.append(".webp.pgp");
+ 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(getConversationsImageDirectory());
+ filename.append(message.getUuid()+".webp");
+ return new DownloadableFile(filename.toString());
}
- 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/";
+ Environment.DIRECTORY_PICTURES).getAbsolutePath()
+ + "/Conversations/";
}
public Bitmap resize(Bitmap originalBitmap, int size) {
@@ -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;
}
diff --git a/src/main/java/eu/siacs/conversations/services/NotificationService.java b/src/main/java/eu/siacs/conversations/services/NotificationService.java
index 11b885383..8b544efc7 100644
--- a/src/main/java/eu/siacs/conversations/services/NotificationService.java
+++ b/src/main/java/eu/siacs/conversations/services/NotificationService.java
@@ -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)) {
- return mXmppConnectionService.getText(
- R.string.image_offered_for_download).toString();
+ 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();
diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
index 97aba64fa..42ec4f77a 100644
--- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
@@ -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,11 +721,16 @@ public class XmppConnectionService extends Service {
} else {
if (message.getConversation().getOtrSession()
.getSessionStatus() == SessionStatus.ENCRYPTED) {
- if (message.getType() == Message.TYPE_TEXT) {
- packet = mMessageGenerator.generateOtrChat(message,
- true);
- } else if (message.getType() == Message.TYPE_IMAGE) {
- mJingleConnectionManager.createNewConnection(message);
+ 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 || message.getType() == Message.TYPE_FILE) {
+ mJingleConnectionManager.createNewConnection(message);
+ }
+ } catch (InvalidJidException e) {
+
}
}
}
@@ -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;
- }
-
- }
}
diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
index 5d11bb597..7e28cc6c3 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
@@ -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() {
@@ -308,11 +310,16 @@ public class ConversationActivity extends XmppActivity implements
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();
}
});
}
diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
index 11f2c4fcb..bc609fb3a 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
@@ -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);
diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java
index b3df8d72b..b81544e64 100644
--- a/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java
+++ b/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java
@@ -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) {
- mLastMessage.setText(R.string.image_offered_for_download);
+ 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) {
- mLastMessage.setText(R.string.image_file_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);
diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
index 3ebb93908..fc80c2349 100644
--- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
+++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
@@ -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:
- info = getContext().getString(R.string.sending);
+ 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,58 +486,66 @@ 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);
- } else {
- displayDecryptionFailed(viewHolder);
- }
- } else {
- if (item.getEncryption() == Message.ENCRYPTION_PGP) {
- if (activity.hasPgp()) {
- 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 {
- displayInfoMessage(viewHolder,
- R.string.install_openkeychain);
- if (viewHolder != null) {
- viewHolder.message_box
- .setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- activity.showInstallPgpDialog();
- }
- });
- }
+ displayInfoMessage(viewHolder,activity.getString(R.string.receiving_image,d.getProgress()));
}
- } else if (item.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
- displayDecryptionFailed(viewHolder);
+ } 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 {
+ 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,activity.getString(R.string.encrypted_message));
} else {
- displayTextMessage(viewHolder, item);
+ displayInfoMessage(viewHolder,
+ activity.getString(R.string.install_openkeychain));
+ if (viewHolder != null) {
+ viewHolder.message_box
+ .setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ activity.showInstallPgpDialog();
+ }
+ });
+ }
}
+ } else if (item.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
+ displayDecryptionFailed(viewHolder);
+ } 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);
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
index 30e1c7da0..e4e00e433 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
@@ -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,75 +279,83 @@ 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);
- } else {
- this.message
- .setEncryption(Message.ENCRYPTION_PGP);
- }
+ message.setType(Message.TYPE_IMAGE);
+ } else {
+ 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 (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 {
- this.file.setKey(key);
+ 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);
}
}
- this.file.setExpectedSize(size);
+ 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 {
- this.sendCancel();
- this.cancel();
+ 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.fail();
+ return;
+ } else {
+ this.file.setKey(key);
+ }
+ }
+ this.file.setExpectedSize(size);
} else {
this.sendCancel();
- this.cancel();
+ this.fail();
}
} 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();
+ 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.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.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);
}
}
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";
+ }
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java
index 05a658be2..72c960d84 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java
@@ -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()
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java
index e3f4fd619..04b225d0e 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java
@@ -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;
}
- this.sendNextBlock();
+ 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);
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java
index 83b597eb5..c34195804 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java
@@ -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();
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleTransport.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleTransport.java
index 1374e61cc..e832d3f58 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleTransport.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleTransport.java
@@ -10,4 +10,6 @@ public abstract class JingleTransport {
public abstract void send(final DownloadableFile file,
final OnFileTransmissionStatusChanged callback);
+
+ public abstract void disconnect();
}
diff --git a/src/main/res/menu/attachment_choices.xml b/src/main/res/menu/attachment_choices.xml
index 20932489d..12b37c08e 100644
--- a/src/main/res/menu/attachment_choices.xml
+++ b/src/main/res/menu/attachment_choices.xml
@@ -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> \ No newline at end of file
diff --git a/src/main/res/menu/message_context.xml b/src/main/res/menu/message_context.xml
index 80d4d1960..3be52442e 100644
--- a/src/main/res/menu/message_context.xml
+++ b/src/main/res/menu/message_context.xml
@@ -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> \ No newline at end of file
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index 0fd706f33..e720101c1 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -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>