aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorsteckbrief <steckbrief@chefmail.de>2017-01-05 21:05:24 +0100
committersteckbrief <steckbrief@chefmail.de>2017-01-05 21:05:24 +0100
commitc09e7f0a21f563359c69f0e4198c7563cd2d4809 (patch)
treea52009fa04da58a10bad7de19814e15e010fde7a /src
parent867ee316619f814e6311668d0f2b910cecdd6393 (diff)
parent2586fe2b9d745c0666656acb6beb4020805dd220 (diff)
Merge branch 'trz/rebase' into trz/rename
Diffstat (limited to '')
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/entities/FileParams.java44
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/entities/Message.java62
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/http/HttpConnectionManager.java13
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/http/HttpDownloadConnection.java34
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java13
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/persistance/FileBackend.java13
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/persistance/db/access/MessageDatabaseAccess.java36
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/persistance/db/migrations/FileParamsBodyToDatabaseFieldsMigration.java5
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/upload/HttpFileTransferEntity.java2
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/ui/ConversationActivity.java17
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/ui/ConversationFragment.java5
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/ui/adapter/MessageAdapter.java487
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/ui/dialogs/MessageDetailsDialog.java3
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/FileUtils.java9
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java67
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/UIHelper.java33
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/UrlUtil.java25
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/filetransfer/http/upload/HttpUploadRequestSlotPacketGenerator.java2
-rw-r--r--src/main/res/layout/dialog_message_details.xml22
-rw-r--r--src/main/res/values/dimens.xml1
-rw-r--r--src/main/res/values/strings.xml3
21 files changed, 583 insertions, 313 deletions
diff --git a/src/main/java/de/thedevstack/conversationsplus/entities/FileParams.java b/src/main/java/de/thedevstack/conversationsplus/entities/FileParams.java
index bce8e571..7d5741d9 100644
--- a/src/main/java/de/thedevstack/conversationsplus/entities/FileParams.java
+++ b/src/main/java/de/thedevstack/conversationsplus/entities/FileParams.java
@@ -8,6 +8,7 @@ import de.thedevstack.conversationsplus.utils.MimeUtils;
*/
public class FileParams {
private String name;
+ private String originalFilename;
private String path;
private String url;
private String mimeType;
@@ -15,6 +16,8 @@ public class FileParams {
private int width = 0;
private int height = 0;
private FileStatus fileStatus;
+ private byte[] aeskey;
+ private byte[] iv;
public FileParams() {
fileStatus = FileStatus.UNDEFINED;
@@ -88,6 +91,39 @@ public class FileParams {
return path;
}
+ public void setKeyAndIv(byte[] keyIvCombo) {
+ if (null != keyIvCombo) {
+ if (keyIvCombo.length == 48) {
+ this.aeskey = new byte[32];
+ this.iv = new byte[16];
+ System.arraycopy(keyIvCombo, 0, this.iv, 0, 16);
+ System.arraycopy(keyIvCombo, 16, this.aeskey, 0, 32);
+ } else if (keyIvCombo.length >= 32) {
+ this.aeskey = new byte[32];
+ System.arraycopy(keyIvCombo, 0, aeskey, 0, 32);
+ } else if (keyIvCombo.length >= 16) {
+ this.aeskey = new byte[16];
+ System.arraycopy(keyIvCombo, 0, this.aeskey, 0, 16);
+ }
+ }
+ }
+
+ public void setKey(byte[] key) {
+ this.aeskey = key;
+ }
+
+ public void setIv(byte[] iv) {
+ this.iv = iv;
+ }
+
+ public byte[] getKey() {
+ return this.aeskey;
+ }
+
+ public byte[] getIv() {
+ return this.iv;
+ }
+
/**
* Sets the path to the file.
* If no file name is stored yet here - this method tries to extract the file name from the path.
@@ -126,4 +162,12 @@ public class FileParams {
public FileStatus getFileStatus() {
return this.fileStatus;
}
+
+ public void setOriginalFilename(String originalFilename) {
+ this.originalFilename = originalFilename;
+ }
+
+ public String getOriginalFilename() {
+ return this.originalFilename;
+ }
}
diff --git a/src/main/java/de/thedevstack/conversationsplus/entities/Message.java b/src/main/java/de/thedevstack/conversationsplus/entities/Message.java
index a81ba404..a553a3e6 100644
--- a/src/main/java/de/thedevstack/conversationsplus/entities/Message.java
+++ b/src/main/java/de/thedevstack/conversationsplus/entities/Message.java
@@ -9,6 +9,7 @@ import java.net.URL;
import de.thedevstack.conversationsplus.crypto.axolotl.XmppAxolotlSession;
import de.thedevstack.conversationsplus.enums.FileStatus;
import de.thedevstack.conversationsplus.utils.FileUtils;
+import de.thedevstack.conversationsplus.utils.MessageUtil;
import de.thedevstack.conversationsplus.utils.MimeUtils;
import de.thedevstack.conversationsplus.xmpp.jid.InvalidJidException;
import de.thedevstack.conversationsplus.xmpp.jid.Jid;
@@ -337,7 +338,7 @@ public class Message extends AbstractEntity {
public void setType(int type) {
this.type = type;
- if (null != this.fileParams && (type == Message.TYPE_FILE || type == Message.TYPE_IMAGE)) {
+ if (null == this.fileParams && (type == Message.TYPE_FILE || type == Message.TYPE_IMAGE)) {
this.setFileParams(new FileParams());
}
}
@@ -480,18 +481,6 @@ public class Message extends AbstractEntity {
NOT_DECIDED,
}
- private String extractRelevantExtension(URL url) {
- if (url == null) {
- return null;
- }
- String path = url.getPath();
- return extractRelevantExtension(path);
- }
-
- private String extractRelevantExtension(String path) {
- return FileUtils.getRelevantExtension(path);
- }
-
public String getMimeType() { // TODO: Move to fileparams
if (relativeFilePath != null) {
int start = relativeFilePath.lastIndexOf('.') + 1;
@@ -502,7 +491,7 @@ public class Message extends AbstractEntity {
}
} else {
try {
- return MimeUtils.guessMimeTypeFromExtension(extractRelevantExtension(new URL(this.getBody())));
+ return MimeUtils.guessMimeTypeFromExtension(FileUtils.getRelevantExtension(new URL(this.getBody())));
} catch (MalformedURLException e) {
return null;
}
@@ -525,50 +514,9 @@ public class Message extends AbstractEntity {
if (mTreatAsDownloadAble != Decision.NOT_DECIDED) {
return mTreatAsDownloadAble;
}
- /**
- * there are a few cases where spaces result in an unwanted behavior, e.g.
- * "http://example.com/image.jpg" text that will not be shown /abc.png"
- * or more than one image link in one message.
- */
- if (getBody().contains(" ")) {
- mTreatAsDownloadAble = Decision.NEVER;
- return mTreatAsDownloadAble;
- }
- try {
- URL url = new URL(body);
- if (!url.getProtocol().equalsIgnoreCase("http") && !url.getProtocol().equalsIgnoreCase("https")) {
- mTreatAsDownloadAble = Decision.NEVER;
- return mTreatAsDownloadAble;
- }
- String extension = extractRelevantExtension(url);
- if (extension == null) {
- mTreatAsDownloadAble = Decision.NEVER;
- return mTreatAsDownloadAble;
- }
- String ref = url.getRef();
- boolean encrypted = ref != null && ref.matches("([A-Fa-f0-9]{2}){48}");
-
- if (encrypted) {
- if (MimeUtils.guessMimeTypeFromExtension(extension) != null) {
- mTreatAsDownloadAble = Decision.MUST;
- return mTreatAsDownloadAble;
- } else {
- mTreatAsDownloadAble = Decision.NEVER;
- return mTreatAsDownloadAble;
- }
- } else if (Transferable.VALID_IMAGE_EXTENSIONS.contains(extension)
- || Transferable.WELL_KNOWN_EXTENSIONS.contains(extension)) {
- mTreatAsDownloadAble = Decision.SHOULD;
- return mTreatAsDownloadAble;
- } else {
- mTreatAsDownloadAble = Decision.NEVER;
- return mTreatAsDownloadAble;
- }
- } catch (MalformedURLException e) {
- mTreatAsDownloadAble = Decision.NEVER;
- return mTreatAsDownloadAble;
- }
+ MessageUtil.extractFileParamsFromBody(this);
+ return this.mTreatAsDownloadAble;
}
public void untie() {
diff --git a/src/main/java/de/thedevstack/conversationsplus/http/HttpConnectionManager.java b/src/main/java/de/thedevstack/conversationsplus/http/HttpConnectionManager.java
index 686587c7..011e2529 100644
--- a/src/main/java/de/thedevstack/conversationsplus/http/HttpConnectionManager.java
+++ b/src/main/java/de/thedevstack/conversationsplus/http/HttpConnectionManager.java
@@ -20,8 +20,8 @@ import javax.net.ssl.X509TrustManager;
import de.thedevstack.conversationsplus.ConversationsPlusApplication;
import de.thedevstack.conversationsplus.entities.Message;
import de.thedevstack.conversationsplus.services.AbstractConnectionManager;
-import de.thedevstack.conversationsplus.services.XmppConnectionService;
import de.thedevstack.conversationsplus.utils.CryptoHelper;
+import de.thedevstack.conversationsplus.utils.MessageUtil;
import de.thedevstack.conversationsplus.utils.SSLSocketHelper;
public class HttpConnectionManager extends AbstractConnectionManager {
@@ -38,10 +38,13 @@ public class HttpConnectionManager extends AbstractConnectionManager {
}
public static HttpDownloadConnection createNewDownloadConnection(Message message, boolean interactive) {
- HttpDownloadConnection connection = new HttpDownloadConnection(INSTANCE);
- connection.init(message,interactive);
- INSTANCE.downloadConnections.add(connection);
- return connection;
+ if (MessageUtil.needsDownload(message)) {
+ HttpDownloadConnection connection = new HttpDownloadConnection(INSTANCE);
+ connection.init(message, interactive);
+ INSTANCE.downloadConnections.add(connection);
+ return connection;
+ }
+ return null;
}
public void finishConnection(HttpDownloadConnection connection) {
diff --git a/src/main/java/de/thedevstack/conversationsplus/http/HttpDownloadConnection.java b/src/main/java/de/thedevstack/conversationsplus/http/HttpDownloadConnection.java
index d9fc9584..07f308fe 100644
--- a/src/main/java/de/thedevstack/conversationsplus/http/HttpDownloadConnection.java
+++ b/src/main/java/de/thedevstack/conversationsplus/http/HttpDownloadConnection.java
@@ -18,6 +18,7 @@ import javax.net.ssl.SSLHandshakeException;
import de.thedevstack.android.logcat.Logging;
import de.thedevstack.conversationsplus.ConversationsPlusApplication;
import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
+import de.thedevstack.conversationsplus.entities.FileParams;
import de.thedevstack.conversationsplus.enums.FileStatus;
import de.thedevstack.conversationsplus.exceptions.RemoteFileNotFoundException;
import de.thedevstack.conversationsplus.utils.MessageUtil;
@@ -75,8 +76,22 @@ public class HttpDownloadConnection implements Transferable {
this.message = message;
this.message.setTransferable(this);
try {
- mUrl = new URL(message.getFileParams().getUrl());
- final String sUrlFilename = mUrl.getPath().substring(mUrl.getPath().lastIndexOf('/')).toLowerCase();
+ String url = (null != message && null != message.getFileParams()) ? message.getFileParams().getUrl() : null;
+ if (null == url) {
+ /*
+ * If this code is reached and the URL is null something went wrong.
+ * Try again to extract the file parameters from the message.
+ */
+ MessageUtil.extractFileParamsFromBody(message);
+ url = (null != message.getFileParams()) ? message.getFileParams().getUrl() : null;
+ if (null == url) {
+ message.setTreatAsDownloadable(Message.Decision.NEVER); // TODO find sth better
+ this.cancel();
+ return;
+ }
+ }
+ mUrl = new URL(url);
+ final String sUrlFilename = mUrl.getPath().substring(mUrl.getPath().lastIndexOf('/') + 1).toLowerCase();
final String lastPart = FileUtils.getLastExtension(sUrlFilename);
if (!lastPart.isEmpty() && ("pgp".equals(lastPart) || "gpg".equals(lastPart))) {
@@ -85,18 +100,25 @@ public class HttpDownloadConnection implements Transferable {
&& message.getEncryption() != Message.ENCRYPTION_AXOLOTL) {
this.message.setEncryption(Message.ENCRYPTION_NONE);
}
+
String extension;
+ String originalFilename;
if (!lastPart.isEmpty() && VALID_CRYPTO_EXTENSIONS.contains(lastPart)) {
extension = FileUtils.getSecondToLastExtension(sUrlFilename);
+ originalFilename = sUrlFilename.replace("." + lastPart, "");
} else {
extension = lastPart;
+ originalFilename = sUrlFilename;
}
message.setRelativeFilePath(message.getUuid() + "." + extension);
this.file = FileBackend.getFile(message, false);
- String reference = mUrl.getRef();
- if (reference != null && reference.length() == 96) {
- this.file.setKeyAndIv(CryptoHelper.hexToBytes(reference));
- }
+
+ FileParams fileParams = message.getFileParams();
+ if (null == fileParams) {
+ fileParams = new FileParams();
+ message.setFileParams(fileParams);
+ }
+ fileParams.setOriginalFilename(originalFilename);
if ((this.message.getEncryption() == Message.ENCRYPTION_OTR
|| this.message.getEncryption() == Message.ENCRYPTION_AXOLOTL)
diff --git a/src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java b/src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java
index b02677a8..53fbb107 100644
--- a/src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java
+++ b/src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java
@@ -469,15 +469,18 @@ public class MessageParser extends AbstractParser implements
if (message.getEncryption() == Message.ENCRYPTION_NONE || !ConversationsPlusPreferences.dontSaveEncrypted()) {
mXmppConnectionService.databaseBackend.createMessage(message);
}
+ MessageUtil.extractFileParamsFromBody(message);
+ FileParams fileParams = message.getFileParams();
+ if (message.treatAsDownloadable() != Message.Decision.NEVER && message.treatAsDownloadable() != Message.Decision.NOT_DECIDED) {
+ if (null != fileParams) {
+ fileParams.setFileStatus(FileStatus.NEEDS_DOWNLOAD);
+ }
+ }
if (message.trusted()
&& message.treatAsDownloadable() != Message.Decision.NEVER
&& ConversationsPlusPreferences.autoAcceptFileSize() > 0
&& (message.isHttpUploaded() || ConversationsPlusPreferences.autoDownloadFileLink())) {
- FileParams fileParams = new FileParams();
- fileParams.setFileStatus(FileStatus.NEEDS_DOWNLOAD);
- fileParams.setUrl(message.getBody());
- message.setFileParams(fileParams);
- HttpConnectionManager.createNewDownloadConnection(message);
+ HttpConnectionManager.createNewDownloadConnection(message);
} else {
if (query == null) {
mXmppConnectionService.getNotificationService().push(message);
diff --git a/src/main/java/de/thedevstack/conversationsplus/persistance/FileBackend.java b/src/main/java/de/thedevstack/conversationsplus/persistance/FileBackend.java
index a35ed043..6643c276 100644
--- a/src/main/java/de/thedevstack/conversationsplus/persistance/FileBackend.java
+++ b/src/main/java/de/thedevstack/conversationsplus/persistance/FileBackend.java
@@ -107,7 +107,18 @@ public class FileBackend {
}
public static DownloadableFile getFile(Message message, boolean decrypted) {
- return new DownloadableFile(getFilePath(message, decrypted));
+ DownloadableFile downloadableFile = new DownloadableFile(getFilePath(message, decrypted));
+ FileParams fileParams = message.getFileParams();
+ if (null != fileParams) {
+ if (null != fileParams.getKey()) {
+ downloadableFile.setKey(fileParams.getKey());
+ }
+ if (null != fileParams.getIv()) {
+ downloadableFile.setIv(fileParams.getIv());
+ }
+ }
+
+ return downloadableFile;
}
protected static String getFilePath(Message message, String extension) {
diff --git a/src/main/java/de/thedevstack/conversationsplus/persistance/db/access/MessageDatabaseAccess.java b/src/main/java/de/thedevstack/conversationsplus/persistance/db/access/MessageDatabaseAccess.java
index 51ad99a4..2fc4481d 100644
--- a/src/main/java/de/thedevstack/conversationsplus/persistance/db/access/MessageDatabaseAccess.java
+++ b/src/main/java/de/thedevstack/conversationsplus/persistance/db/access/MessageDatabaseAccess.java
@@ -9,6 +9,7 @@ import de.thedevstack.conversationsplus.entities.FileParams;
import de.thedevstack.conversationsplus.entities.Message;
import de.thedevstack.conversationsplus.enums.FileStatus;
import de.thedevstack.conversationsplus.persistance.db.migrations.FileParamsBodyToDatabaseFieldsMigration;
+import de.thedevstack.conversationsplus.utils.CryptoHelper;
/**
*
@@ -28,6 +29,9 @@ public class MessageDatabaseAccess extends AbstractDatabaseAccess {
private static final String COLUMN_NAME_MSG_PARAMS_FILE_NAME = "file_name";
private static final String COLUMN_NAME_MSG_PARAMS_FILE_PATH = "file_path";
private static final String COLUMN_NAME_MSG_PARAMS_FILE_URL = "file_url";
+ private static final String COLUMN_NAME_MSG_PARAMS_FILE_IV = "file_iv";
+ private static final String COLUMN_NAME_MSG_PARAMS_FILE_KEY = "file_key";
+ private static final String COLUMN_NAME_MSG_PARAMS_ORIGINAL_FILE_NAME = "original_file_name";
private static final String TABLE_ADDITIONAL_PARAMETERS_CREATE_V1 = "CREATE TABLE " + MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS + " ("
+ MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + " TEXT, "
@@ -45,8 +49,11 @@ public class MessageDatabaseAccess extends AbstractDatabaseAccess {
+ MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_WIDTH + " INTEGER DEFAULT 0, "
+ MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_STATUS + " TEXT DEFAULT 'UNDEFINED', "
+ MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_NAME + " TEXT DEFAULT NULL, "
+ + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_ORIGINAL_FILE_NAME + " TEXT DEFAULT NULL, "
+ MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_PATH + " TEXT DEFAULT NULL, "
+ MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_URL + " TEXT DEFAULT NULL, "
+ + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_IV + " TEXT DEFAULT NULL, "
+ + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_KEY + " TEXT DEFAULT NULL, "
+ "FOREIGN KEY(" + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + ") REFERENCES " + Message.TABLENAME + "(" + Message.UUID + ") ON DELETE CASCADE)";
public static void updateMessageParameters(SQLiteDatabase db, Message message, String uuid) {
@@ -65,8 +72,16 @@ public class MessageDatabaseAccess extends AbstractDatabaseAccess {
additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_SIZE, fileParams.getSize());
additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_HEIGHT, fileParams.getHeight());
additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_WIDTH, fileParams.getWidth());
+ additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_NAME, fileParams.getName());
+ additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_ORIGINAL_FILE_NAME, fileParams.getOriginalFilename());
additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_PATH, fileParams.getPath());
additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_URL, fileParams.getUrl());
+ if (null != fileParams.getIv()) {
+ additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_IV, CryptoHelper.bytesToHex(fileParams.getIv()));
+ }
+ if (null != fileParams.getKey()) {
+ additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_KEY, CryptoHelper.bytesToHex(fileParams.getKey()));
+ }
if (null != fileParams.getFileStatus()) {
additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_STATUS, fileParams.getFileStatus().name());
}
@@ -95,10 +110,20 @@ public class MessageDatabaseAccess extends AbstractDatabaseAccess {
fileParams.setMimeType(fileType);
String name = CursorHelper.getString(cursor, COLUMN_NAME_MSG_PARAMS_FILE_NAME);
fileParams.setName(name);
+ String originalFilename = CursorHelper.getString(cursor, COLUMN_NAME_MSG_PARAMS_ORIGINAL_FILE_NAME);
+ fileParams.setOriginalFilename(originalFilename);
String path = CursorHelper.getString(cursor, COLUMN_NAME_MSG_PARAMS_FILE_PATH);
fileParams.setPath(path);
String url = CursorHelper.getString(cursor, COLUMN_NAME_MSG_PARAMS_FILE_URL);
fileParams.setUrl(url);
+ String iv = CursorHelper.getString(cursor, COLUMN_NAME_MSG_PARAMS_FILE_IV);
+ if (null != iv && !iv.isEmpty()) {
+ fileParams.setIv(CryptoHelper.hexToBytes(iv));
+ }
+ String key = CursorHelper.getString(cursor, COLUMN_NAME_MSG_PARAMS_FILE_KEY);
+ if (null != key && !key.isEmpty()) {
+ fileParams.setKey(CryptoHelper.hexToBytes(key));
+ }
long fileSize = CursorHelper.getLong(cursor, COLUMN_NAME_MSG_PARAMS_FILE_SIZE);
fileParams.setSize(fileSize);
int imageHeight = CursorHelper.getInt(cursor, COLUMN_NAME_MSG_PARAMS_FILE_HEIGHT);
@@ -146,7 +171,11 @@ public class MessageDatabaseAccess extends AbstractDatabaseAccess {
MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_WIDTH + " INTEGER DEFAULT 0",
MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_STATUS + " TEXT DEFAULT 'UNDEFINED'",
MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_NAME + " TEXT DEFAULT NULL",
+ MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_ORIGINAL_FILE_NAME + " TEXT DEFAULT NULL",
MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_PATH + " TEXT DEFAULT NULL",
+ MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_URL + " TEXT DEFAULT NULL",
+ MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_IV + " TEXT DEFAULT NULL",
+ MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_KEY + " TEXT DEFAULT NULL",
};
addNewColumns(db, MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS, columnDefinitions);
@@ -166,6 +195,13 @@ public class MessageDatabaseAccess extends AbstractDatabaseAccess {
parameterValues.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_SIZE, fileParams.getSize());
parameterValues.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_HEIGHT, fileParams.getHeight());
parameterValues.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_WIDTH, fileParams.getWidth());
+ parameterValues.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_URL, fileParams.getUrl());
+ if (null != fileParams.getIv()) {
+ parameterValues.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_IV, CryptoHelper.bytesToHex(fileParams.getIv()));
+ }
+ if (null != fileParams.getKey()) {
+ parameterValues.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_KEY, CryptoHelper.bytesToHex(fileParams.getKey()));
+ }
db.update(MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS, parameterValues, MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + "=?", new String[] {uuid});
}
diff --git a/src/main/java/de/thedevstack/conversationsplus/persistance/db/migrations/FileParamsBodyToDatabaseFieldsMigration.java b/src/main/java/de/thedevstack/conversationsplus/persistance/db/migrations/FileParamsBodyToDatabaseFieldsMigration.java
index c0aa63c0..8ad94ca0 100644
--- a/src/main/java/de/thedevstack/conversationsplus/persistance/db/migrations/FileParamsBodyToDatabaseFieldsMigration.java
+++ b/src/main/java/de/thedevstack/conversationsplus/persistance/db/migrations/FileParamsBodyToDatabaseFieldsMigration.java
@@ -4,6 +4,7 @@ import java.net.MalformedURLException;
import java.net.URL;
import de.thedevstack.conversationsplus.entities.FileParams;
+import de.thedevstack.conversationsplus.utils.UrlUtil;
/**
* Created by steckbrief on 24.08.2016.
@@ -28,6 +29,8 @@ public class FileParamsBodyToDatabaseFieldsMigration {
try {
URL url = new URL(parts[0]);
params.setUrl(url.toString());
+ byte[] ivAndKey = UrlUtil.getIvAndKeyFromURL(url);
+ params.setKeyAndIv(ivAndKey);
} catch (MalformedURLException e1) {
}
}
@@ -37,6 +40,8 @@ public class FileParamsBodyToDatabaseFieldsMigration {
try {
URL url = new URL(parts[0]);
params.setUrl(url.toString());
+ byte[] ivAndKey = UrlUtil.getIvAndKeyFromURL(url);
+ params.setKeyAndIv(ivAndKey);
} catch (MalformedURLException e1) {
}
try {
diff --git a/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/upload/HttpFileTransferEntity.java b/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/upload/HttpFileTransferEntity.java
index a373d584..985dafcb 100644
--- a/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/upload/HttpFileTransferEntity.java
+++ b/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/upload/HttpFileTransferEntity.java
@@ -30,9 +30,9 @@ public class HttpFileTransferEntity extends FileTransferEntity {
FileParams fileParams = this.getMessage().getFileParams();
if (null == fileParams) {
fileParams = new FileParams();
+ this.getMessage().setFileParams(fileParams);
}
fileParams.setFileStatus(FileStatus.NEEDS_UPLOAD);
- this.getMessage().setFileParams(fileParams);
if (Config.ENCRYPT_ON_HTTP_UPLOADED
|| message.getEncryption() == Message.ENCRYPTION_AXOLOTL
|| message.getEncryption() == Message.ENCRYPTION_OTR) {
diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/ConversationActivity.java b/src/main/java/de/thedevstack/conversationsplus/ui/ConversationActivity.java
index 99d26b64..ac9c2766 100644
--- a/src/main/java/de/thedevstack/conversationsplus/ui/ConversationActivity.java
+++ b/src/main/java/de/thedevstack/conversationsplus/ui/ConversationActivity.java
@@ -44,13 +44,10 @@ import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import de.thedevstack.android.logcat.Logging;
-import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
-import de.thedevstack.conversationsplus.http.HttpConnectionManager;
-import de.thedevstack.conversationsplus.ui.dialogs.UserDecisionDialog;
-import de.thedevstack.conversationsplus.ui.listeners.ResizePictureUserDecisionListener;
-import de.thedevstack.conversationsplus.utils.ConversationUtil;
import de.timroes.android.listview.EnhancedListView;
+
import de.thedevstack.conversationsplus.Config;
+import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
import de.thedevstack.conversationsplus.R;
import de.thedevstack.conversationsplus.crypto.axolotl.AxolotlService;
import de.thedevstack.conversationsplus.crypto.axolotl.AxolotlServiceImpl;
@@ -61,12 +58,17 @@ import de.thedevstack.conversationsplus.entities.Contact;
import de.thedevstack.conversationsplus.entities.Conversation;
import de.thedevstack.conversationsplus.entities.Message;
import de.thedevstack.conversationsplus.entities.Transferable;
+import de.thedevstack.conversationsplus.http.HttpConnectionManager;
+import de.thedevstack.conversationsplus.http.HttpDownloadConnection;
import de.thedevstack.conversationsplus.persistance.FileBackend;
import de.thedevstack.conversationsplus.services.XmppConnectionService;
import de.thedevstack.conversationsplus.services.XmppConnectionService.OnAccountUpdate;
import de.thedevstack.conversationsplus.services.XmppConnectionService.OnConversationUpdate;
import de.thedevstack.conversationsplus.services.XmppConnectionService.OnRosterUpdate;
import de.thedevstack.conversationsplus.ui.adapter.ConversationAdapter;
+import de.thedevstack.conversationsplus.ui.dialogs.UserDecisionDialog;
+import de.thedevstack.conversationsplus.ui.listeners.ResizePictureUserDecisionListener;
+import de.thedevstack.conversationsplus.utils.ConversationUtil;
import de.thedevstack.conversationsplus.utils.ExceptionHelper;
import de.thedevstack.conversationsplus.utils.FileUtils;
import de.thedevstack.conversationsplus.xmpp.OnUpdateBlocklist;
@@ -621,7 +623,10 @@ public class ConversationActivity extends XmppActivity
Toast.makeText(this, R.string.not_connected_try_again, Toast.LENGTH_SHORT).show();
}
} else if (message.treatAsDownloadable() != Message.Decision.NEVER) {
- HttpConnectionManager.createNewDownloadConnection(message, true);
+ HttpDownloadConnection downloadConnection = HttpConnectionManager.createNewDownloadConnection(message, true);
+ if (null == downloadConnection) {
+ Toast.makeText(this, R.string.file_not_on_remote_host, Toast.LENGTH_LONG).show();
+ }
}
}
diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/ConversationFragment.java b/src/main/java/de/thedevstack/conversationsplus/ui/ConversationFragment.java
index 0c2f2960..aad15f51 100644
--- a/src/main/java/de/thedevstack/conversationsplus/ui/ConversationFragment.java
+++ b/src/main/java/de/thedevstack/conversationsplus/ui/ConversationFragment.java
@@ -688,7 +688,10 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
}
private void downloadFile(Message message) {
- HttpConnectionManager.createNewDownloadConnection(message,true);
+ HttpDownloadConnection downloadConnection = HttpConnectionManager.createNewDownloadConnection(message, true);
+ if (null == downloadConnection) {
+ Toast.makeText(activity, R.string.file_not_on_remote_host, Toast.LENGTH_LONG).show();
+ }
}
private void cancelTransmission(Message message) {
diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/adapter/MessageAdapter.java b/src/main/java/de/thedevstack/conversationsplus/ui/adapter/MessageAdapter.java
index dd190576..ac8fba0a 100644
--- a/src/main/java/de/thedevstack/conversationsplus/ui/adapter/MessageAdapter.java
+++ b/src/main/java/de/thedevstack/conversationsplus/ui/adapter/MessageAdapter.java
@@ -18,9 +18,7 @@ import android.text.style.ForegroundColorSpan;
import android.text.style.RelativeSizeSpan;
import android.text.style.StyleSpan;
import android.text.util.Linkify;
-import android.util.DisplayMetrics;
import android.util.Patterns;
-import android.view.Gravity;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
@@ -33,7 +31,6 @@ import android.widget.TextView;
import android.widget.Toast;
import java.lang.ref.WeakReference;
-import java.net.URL;
import java.util.List;
import java.util.concurrent.RejectedExecutionException;
import java.util.regex.Matcher;
@@ -51,6 +48,7 @@ import de.thedevstack.conversationsplus.entities.FileParams;
import de.thedevstack.conversationsplus.entities.Message;
import de.thedevstack.conversationsplus.entities.Transferable;
import de.thedevstack.conversationsplus.enums.FileStatus;
+import de.thedevstack.conversationsplus.http.HttpConnectionManager;
import de.thedevstack.conversationsplus.persistance.FileBackend;
import de.thedevstack.conversationsplus.providers.ConversationsPlusFileProvider;
import de.thedevstack.conversationsplus.services.AvatarService;
@@ -74,15 +72,12 @@ public class MessageAdapter extends ArrayAdapter<Message> {
private ConversationActivity activity;
- private DisplayMetrics metrics;
-
private OnContactPictureClicked mOnContactPictureClickedListener;
private OnContactPictureLongClicked mOnContactPictureLongClickedListener;
public MessageAdapter(ConversationActivity activity, List<Message> messages) {
super(activity, 0, messages);
this.activity = activity;
- metrics = getContext().getResources().getDisplayMetrics();
}
public void setOnContactPictureClicked(OnContactPictureClicked listener) {
@@ -133,14 +128,9 @@ public class MessageAdapter extends ArrayAdapter<Message> {
boolean multiReceived = message.getConversation().getMode() == Conversation.MODE_MULTI
&& message.getStatus() <= Message.STATUS_RECEIVED;
if (message.hasFileAttached() || message.getTransferable() != null) {
- FileParams params = message.getFileParams();
+ FileParams params = message.getFileParams();
if (null != params) {
- long size = params.getSize();
- if (size > (1.5 * 1024 * 1024)) {
- filesize = size / (1024 * 1024)+ " MiB";
- } else if (size > 0) {
- filesize = size / 1024 + " KiB";
- }
+ filesize = UIHelper.getHumanReadableFileSize(params.getSize());
}
if (message.getTransferable() != null && message.getTransferable().getStatus() == Transferable.STATUS_FAILED) {
@@ -221,15 +211,11 @@ public class MessageAdapter extends ArrayAdapter<Message> {
String formatedTime = UIHelper.readableTimeDifferenceFull(getContext(),
message.getTimeSent());
if (message.getStatus() <= Message.STATUS_RECEIVED) {
- if ((filesize != null) && (info != null)) {
- viewHolder.time.setText(formatedTime + " \u00B7 " + filesize +" \u00B7 " + info);
- } else if ((filesize == null) && (info != null)) {
- viewHolder.time.setText(formatedTime + " \u00B7 " + info);
- } else if ((filesize != null) && (info == null)) {
- viewHolder.time.setText(formatedTime + " \u00B7 " + filesize);
- } else {
- viewHolder.time.setText(formatedTime);
- }
+ StringBuilder timeText = new StringBuilder();
+ timeText.append((null != formatedTime) ? formatedTime + ((null != info || null != filesize) ? " \u00B7 " : "") : "");
+ timeText.append((null != filesize) ? filesize + ((null != info) ? " \u00B7 " : "") : "");
+ timeText.append((null != info) ? info : "");
+ viewHolder.time.setText(timeText);
} else {
if ((filesize != null) && (info != null)) {
viewHolder.time.setText(filesize + " \u00B7 " + info);
@@ -397,13 +383,37 @@ public class MessageAdapter extends ArrayAdapter<Message> {
return count;
}
- private void displayDownloadableMessage(ViewHolder viewHolder,
- final Message message, String text) {
+ private void displayDownloadButton(ViewHolder viewHolder, String btnText, OnClickListener onClickListener) {
+ viewHolder.download_button.setVisibility(View.VISIBLE);
+ viewHolder.download_button.setText(btnText);
+ viewHolder.download_button.setOnClickListener(onClickListener);
+ }
+
+ private void displayFileInfoForFileMessage(final Message message, ViewHolder viewHolder) {
+ viewHolder.messageBody.setVisibility(View.VISIBLE);
+ StringBuilder fileInfos = new StringBuilder();
+ String filename = UIHelper.getDisplayFilename(message);
+ fileInfos.append((null != filename && !filename.isEmpty()) ? (filename) : "");
+
+ int oldAutoLinkMask = viewHolder.messageBody.getAutoLinkMask();
+ viewHolder.messageBody.setAutoLinkMask(0);
+ viewHolder.messageBody.setText(fileInfos);
+ viewHolder.messageBody.setAutoLinkMask(oldAutoLinkMask);
+ }
+
+ private void displayDownloadableMessage(ViewHolder viewHolder, final Message message, int resId) {
viewHolder.image.setVisibility(View.GONE);
- viewHolder.messageBody.setVisibility(View.GONE);
- viewHolder.download_button.setVisibility(View.VISIBLE);
- viewHolder.download_button.setText(text);
- viewHolder.download_button.setOnClickListener(new OnClickListener() {
+ FileParams fileParams = message.getFileParams();
+ String btnText;
+ if (null != fileParams) {
+ this.displayFileInfoForFileMessage(message, viewHolder);
+ btnText = activity.getString(resId, "");
+ } else {
+ viewHolder.messageBody.setVisibility(View.GONE);
+ btnText = activity.getString(resId, UIHelper.getFileDescriptionString(activity, message));
+ }
+
+ this.displayDownloadButton(viewHolder, btnText, new OnClickListener() {
@Override
public void onClick(View v) {
@@ -414,10 +424,18 @@ public class MessageAdapter extends ArrayAdapter<Message> {
private void displayOpenableMessage(ViewHolder viewHolder,final Message message) {
viewHolder.image.setVisibility(View.GONE);
- viewHolder.messageBody.setVisibility(View.GONE);
- viewHolder.download_button.setVisibility(View.VISIBLE);
- viewHolder.download_button.setText(activity.getString(R.string.open_x_file, UIHelper.getFileDescriptionString(activity, message)));
- viewHolder.download_button.setOnClickListener(new OnClickListener() {
+
+ FileParams fileParams = message.getFileParams();
+ String btnText;
+ if (null != fileParams) {
+ this.displayFileInfoForFileMessage(message, viewHolder);
+ btnText = activity.getString(R.string.cplus_open);
+ } else {
+ viewHolder.messageBody.setVisibility(View.GONE);
+ btnText = activity.getString(R.string.open_x_file, UIHelper.getFileDescriptionString(activity, message));
+ }
+
+ this.displayDownloadButton(viewHolder, btnText, new OnClickListener() {
@Override
public void onClick(View v) {
@@ -474,6 +492,149 @@ public class MessageAdapter extends ArrayAdapter<Message> {
});
}
+ private View displayStatusMessage(final Message message, ViewHolder viewHolder) {
+ if (null != viewHolder) {
+ final Conversation conversation = message.getConversation();
+
+ viewHolder.status_message.setVisibility(View.VISIBLE);
+ viewHolder.contact_picture.setVisibility(View.VISIBLE);
+ if (conversation.getMode() == Conversation.MODE_SINGLE) {
+ viewHolder.contact_picture.setImageBitmap(AvatarService.getInstance().get(conversation.getContact(),
+ activity.getPixel(32)));
+ viewHolder.contact_picture.setAlpha(0.5f);
+ }
+ viewHolder.status_message.setText(message.getBody());
+ }
+ return viewHolder.view;
+ }
+
+ private void displayFileMessage(final Message message, ViewHolder viewHolder) {
+ final FileParams fileParams = message.getFileParams();
+ String mimeType = (null != fileParams) ? fileParams.getMimeType() : null;
+ if ((message.getType() == Message.TYPE_IMAGE && message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED)
+ || (fileParams != null && fileParams.getWidth() > 0)
+ || (null != mimeType && mimeType.startsWith("image/"))) {
+ displayImageMessage(viewHolder, message);
+ } else if ((message.getType() == Message.TYPE_FILE && message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED)
+ && null != fileParams && (FileStatus.NEEDS_DOWNLOAD != fileParams.getFileStatus() || 0 == fileParams.getWidth())) {
+ displayOpenableMessage(viewHolder, message);
+ } else if (Message.Decision.NEVER == message.treatAsDownloadable()) {
+ displayTextMessage(viewHolder, message, getItemViewType(message) == RECEIVED && !message.isValidInSession());
+ } else {
+ displayDownloadableMessage(viewHolder, message, R.string.download_x_file);
+ }
+ }
+
+
+ private void loadAvatar(Message message, ImageView imageView) {
+ if (cancelPotentialWork(message, imageView)) {
+ int avatarPixel = imageView.getResources().getDimensionPixelOffset(R.dimen.msg_avatar_size);
+ final Bitmap bm = AvatarService.getInstance().get(message, avatarPixel, true);
+ if (bm != null) {
+ imageView.setImageBitmap(bm);
+ imageView.setBackgroundColor(0x00000000);
+ } else {
+ imageView.setBackgroundColor(UIHelper.getColorForName(UIHelper.getMessageDisplayName(message)));
+ imageView.setImageDrawable(null);
+ final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
+ final AsyncDrawable asyncDrawable = new AsyncDrawable(activity.getResources(), null, task);
+ imageView.setImageDrawable(asyncDrawable);
+ try {
+ task.execute(message);
+ } catch (final RejectedExecutionException ignored) {
+ }
+ }
+ }
+ }
+
+ private void displayAvatar(final Message message, int type, ViewHolder viewHolder) {
+ if (type == RECEIVED) {
+ ImageView imageView = viewHolder.contact_picture;
+ if (null != imageView) {
+ this.loadAvatar(message, imageView);
+ imageView.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (MessageAdapter.this.mOnContactPictureClickedListener != null) {
+ MessageAdapter.this.mOnContactPictureClickedListener
+ .onContactPictureClicked(message);
+ }
+
+ }
+ });
+ if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
+ imageView.setOnLongClickListener(new OnLongClickListener() {
+
+ @Override
+ public boolean onLongClick(View v) {
+ if (MessageAdapter.this.mOnContactPictureLongClickedListener != null) {
+ MessageAdapter.this.mOnContactPictureLongClickedListener
+ .onContactPictureLongClicked(message);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ });
+ }
+ }
+ }
+ }
+
+ private View initializeView(int type, ViewGroup parent) {
+ Integer viewResId = null;
+
+ switch (type) {
+ case SENT:
+ viewResId = R.layout.message_sent;
+ break;
+ case RECEIVED:
+ viewResId = R.layout.message_received;
+ break;
+ case STATUS:
+ viewResId = R.layout.message_status;
+ break;
+ }
+
+ return activity.getLayoutInflater().inflate(viewResId, parent, false);
+ }
+
+ private ViewHolder initializeViewHolderAndView(Message message, int type, ViewGroup parent) {
+ View view = initializeView(type, parent);
+ ViewHolder viewHolder = new ViewHolder(view);
+ if (SENT == type
+ || RECEIVED == type) {
+ viewHolder.message_box = (LinearLayout) view.findViewById(R.id.message_box);
+ viewHolder.message_box.setVisibility(View.VISIBLE);
+ viewHolder.indicator = (ImageView) view.findViewById(R.id.security_indicator);
+ viewHolder.messageBody = (TextView) view.findViewById(R.id.message_body);
+ viewHolder.time = (TextView) view.findViewById(R.id.message_time);
+ viewHolder.indicatorReceived = (ImageView) view.findViewById(R.id.indicator_received);
+ }
+ if ((SENT == type
+ || RECEIVED == type)) {
+ viewHolder.download_button = (Button) view.findViewById(R.id.download_button);
+ viewHolder.image = (ImageView) view.findViewById(R.id.message_image);
+ }
+ if (RECEIVED == type) { // Extra block as preparation for new /me representation
+ viewHolder.contact_picture = (ImageView) view.findViewById(R.id.message_photo);
+ viewHolder.contact_picture.setVisibility(View.VISIBLE);
+ }
+ if (RECEIVED == type) {
+ viewHolder.encryption = (TextView) view.findViewById(R.id.message_encryption);
+ }
+ if (STATUS == type) {
+ viewHolder.contact_picture = (ImageView) view.findViewById(R.id.message_photo);
+ viewHolder.contact_picture.setVisibility(View.VISIBLE);
+ viewHolder.status_message = (TextView) view.findViewById(R.id.status_message);
+ viewHolder.status_message.setVisibility(View.VISIBLE);
+ }
+ view.setTag(viewHolder);
+
+ return viewHolder;
+ }
+
@Override
public View getView(int position, View view, ViewGroup parent) {
final Message message = getItem(position);
@@ -481,180 +642,81 @@ public class MessageAdapter extends ArrayAdapter<Message> {
final Conversation conversation = message.getConversation();
final Account account = conversation.getAccount();
final int type = getItemViewType(position);
- ViewHolder viewHolder;
- if (view == null) {
- viewHolder = new ViewHolder();
- switch (type) {
- case SENT:
- view = activity.getLayoutInflater().inflate(
- R.layout.message_sent, parent, false);
- viewHolder.message_box = (LinearLayout) view
- .findViewById(R.id.message_box);
- viewHolder.contact_picture = (ImageView) view
- .findViewById(R.id.message_photo);
- viewHolder.download_button = (Button) view
- .findViewById(R.id.download_button);
- viewHolder.indicator = (ImageView) view
- .findViewById(R.id.security_indicator);
- viewHolder.image = (ImageView) view
- .findViewById(R.id.message_image);
- viewHolder.messageBody = (TextView) view
- .findViewById(R.id.message_body);
- viewHolder.time = (TextView) view
- .findViewById(R.id.message_time);
- viewHolder.indicatorReceived = (ImageView) view
- .findViewById(R.id.indicator_received);
- viewHolder.remoteFileStatus = (TextView) view.findViewById(R.id.remote_file_status);
- break;
- case RECEIVED:
- view = activity.getLayoutInflater().inflate(
- R.layout.message_received, parent, false);
- viewHolder.message_box = (LinearLayout) view
- .findViewById(R.id.message_box);
- viewHolder.contact_picture = (ImageView) view
- .findViewById(R.id.message_photo);
- viewHolder.download_button = (Button) view
- .findViewById(R.id.download_button);
- viewHolder.indicator = (ImageView) view
- .findViewById(R.id.security_indicator);
- viewHolder.image = (ImageView) view
- .findViewById(R.id.message_image);
- viewHolder.messageBody = (TextView) view
- .findViewById(R.id.message_body);
- viewHolder.time = (TextView) view
- .findViewById(R.id.message_time);
- viewHolder.indicatorReceived = (ImageView) view
- .findViewById(R.id.indicator_received);
- viewHolder.encryption = (TextView) view.findViewById(R.id.message_encryption);
- break;
- case STATUS:
- view = activity.getLayoutInflater().inflate(R.layout.message_status, parent, false);
- viewHolder.contact_picture = (ImageView) view.findViewById(R.id.message_photo);
- viewHolder.status_message = (TextView) view.findViewById(R.id.status_message);
- break;
- default:
- viewHolder = null;
- break;
- }
- view.setTag(viewHolder);
- } else {
- viewHolder = (ViewHolder) view.getTag();
- if (viewHolder == null) {
- return view;
- }
- }
-
- boolean darkBackground = (type == RECEIVED && !isInValidSession);
-
- if (type == STATUS) {
- viewHolder.status_message.setVisibility(View.VISIBLE);
- viewHolder.contact_picture.setVisibility(View.VISIBLE);
- if (conversation.getMode() == Conversation.MODE_SINGLE) {
- viewHolder.contact_picture.setImageBitmap(AvatarService.getInstance().get(conversation.getContact(),
- activity.getPixel(32)));
- viewHolder.contact_picture.setAlpha(0.5f);
- }
- viewHolder.status_message.setText(message.getBody());
- return view;
- } else {
- loadAvatar(message, viewHolder.contact_picture);
- }
+ ViewHolder viewHolder;
+ if (null == view) {
+ viewHolder = initializeViewHolderAndView(message, type, parent);
+ view = viewHolder.view;
+ } else {
+ viewHolder = (ViewHolder) view.getTag();
+ if (null == viewHolder) {
+ return view;
+ }
+ }
- viewHolder.contact_picture
- .setOnClickListener(new OnClickListener() {
+ if (type == STATUS) {
+ return displayStatusMessage(message, viewHolder);
+ }
- @Override
- public void onClick(View v) {
- if (MessageAdapter.this.mOnContactPictureClickedListener != null) {
- MessageAdapter.this.mOnContactPictureClickedListener
- .onContactPictureClicked(message);
- }
+ this.displayAvatar(message, type, viewHolder);
- }
- });
- viewHolder.contact_picture
- .setOnLongClickListener(new OnLongClickListener() {
-
- @Override
- public boolean onLongClick(View v) {
- if (MessageAdapter.this.mOnContactPictureLongClickedListener != null) {
- MessageAdapter.this.mOnContactPictureLongClickedListener
- .onContactPictureLongClicked(message);
- return true;
- } else {
- return false;
- }
- }
- });
+ boolean darkBackground = (type == RECEIVED && !isInValidSession);
+ this.displayStatus(viewHolder, message, type, darkBackground);
final Transferable transferable = message.getTransferable();
- if (transferable != null && transferable.getStatus() != Transferable.STATUS_UPLOADING) {
- if (transferable.getStatus() == Transferable.STATUS_OFFER) {
- displayDownloadableMessage(viewHolder,message,activity.getString(R.string.download_x_file, UIHelper.getFileDescriptionString(activity, message)));
- } else if (transferable.getStatus() == Transferable.STATUS_OFFER_CHECK_FILESIZE) {
- displayDownloadableMessage(viewHolder, message, activity.getString(R.string.check_x_filesize, UIHelper.getFileDescriptionString(activity, message)));
- } else {
- displayInfoMessage(viewHolder, UIHelper.getMessagePreview(activity, message).first,darkBackground);
- }
- } else if (message.getType() == Message.TYPE_IMAGE && message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) {
- displayImageMessage(viewHolder, message);
- } else if (message.getType() == Message.TYPE_FILE && message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) {
- if (message.getFileParams() != null && message.getFileParams().getWidth() > 0) { // TODO Use FileParams.getMimeType()
- displayImageMessage(viewHolder,message);
- } else {
- displayOpenableMessage(viewHolder, message);
- }
- } else if (message.getEncryption() == Message.ENCRYPTION_PGP) {
- if (activity.hasPgp()) {
- if (account.getPgpDecryptionService().isRunning()) {
- displayInfoMessage(viewHolder, activity.getString(R.string.message_decrypting), darkBackground);
- } else {
- displayInfoMessage(viewHolder, activity.getString(R.string.pgp_message), darkBackground);
- }
- } else {
- displayInfoMessage(viewHolder,activity.getString(R.string.install_openkeychain),darkBackground);
- if (viewHolder != null) {
- viewHolder.message_box
- .setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- activity.showInstallPgpDialog();
- }
- });
- }
- }
- } else if (message.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
- displayDecryptionFailed(viewHolder,darkBackground);
- } else if (message.hasFileAttached()) { // TODO: Move to a single block with Message.TYPE_FILE OR Message.TYPE_IMAGE
- FileParams fileParams = message.getFileParams();
- String mimeType = (null != fileParams) ? fileParams.getMimeType() : null;
- if (null != mimeType && mimeType.startsWith("image/")) {
- displayImageMessage(viewHolder, message);
+ if (null != transferable) {
+ switch (transferable.getStatus()) {
+ case Transferable.STATUS_OFFER:
+ displayDownloadableMessage(viewHolder, message, R.string.download_x_file);
+ break;
+ case Transferable.STATUS_OFFER_CHECK_FILESIZE:
+ displayDownloadableMessage(viewHolder, message, R.string.check_x_filesize);
+ break;
+ case Transferable.STATUS_UPLOADING:
+ displayFileMessage(message, viewHolder);
+ break;
+ case Transferable.STATUS_DELETED:
+ case Transferable.STATUS_CHECKING:
+ case Transferable.STATUS_FAILED:
+ case Transferable.STATUS_UNKNOWN:
+ displayInfoMessage(viewHolder, UIHelper.getMessagePreview(activity, message).first,darkBackground);
+ break;
+ }
+ } else if (message.hasFileAttached()) {
+ if (message.trusted()
+ && MessageUtil.needsDownload(message)
+ && ConversationsPlusPreferences.autoAcceptFileSize() > 0
+ && (message.isHttpUploaded() || ConversationsPlusPreferences.autoDownloadFileLink())) {
+ HttpConnectionManager.createNewDownloadConnection(message);
+ } else {
+ displayFileMessage(message, viewHolder);
+ }
+ } else if (GeoHelper.isGeoUri(message.getBody())) {
+ displayLocationMessage(viewHolder, message);
+ } else if (message.getEncryption() == Message.ENCRYPTION_PGP) {
+ if (activity.hasPgp()) {
+ if (account.getPgpDecryptionService().isRunning()) {
+ displayInfoMessage(viewHolder, activity.getString(R.string.message_decrypting), darkBackground);
+ } else {
+ displayInfoMessage(viewHolder, activity.getString(R.string.pgp_message), darkBackground);
+ }
} else {
- displayOpenableMessage(viewHolder, message);
+ displayInfoMessage(viewHolder,activity.getString(R.string.install_openkeychain),darkBackground);
+ if (viewHolder != null) {
+ viewHolder.message_box
+ .setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ activity.showInstallPgpDialog();
+ }
+ });
+ }
}
+ } else if (message.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
+ displayDecryptionFailed(viewHolder,darkBackground);
} else {
- if (GeoHelper.isGeoUri(message.getBody())) {
- displayLocationMessage(viewHolder,message);
- } else if (MessageUtil.needsDownload(message)) {
- try {
- URL url = new URL(message.getFileParams().getUrl());
- displayDownloadableMessage(viewHolder,
- message,
- activity.getString(R.string.check_x_filesize_on_host,
- UIHelper.getFileDescriptionString(activity, message),
- url.getHost()));
- } catch (Exception e) {
- displayDownloadableMessage(viewHolder,
- message,
- activity.getString(R.string.check_x_filesize,
- UIHelper.getFileDescriptionString(activity, message)));
- }
- } else {
- displayTextMessage(viewHolder, message, darkBackground);
- }
- }
+ displayTextMessage(viewHolder, message, darkBackground);
+ }
if (type == RECEIVED) {
if(isInValidSession) {
@@ -666,8 +728,6 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}
}
- displayStatus(viewHolder, message, type, darkBackground);
-
return view;
}
@@ -735,6 +795,10 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}
private static class ViewHolder {
+ protected ViewHolder(View view) {
+ this.view = view;
+ }
+ protected View view;
protected LinearLayout message_box;
protected Button download_button;
@@ -759,7 +823,8 @@ public class MessageAdapter extends ArrayAdapter<Message> {
@Override
protected Bitmap doInBackground(Message... params) {
- return AvatarService.getInstance().get(params[0], activity.getPixel(48), isCancelled());
+ int avatarPixel = activity.getResources().getDimensionPixelOffset(R.dimen.msg_avatar_size);
+ return AvatarService.getInstance().get(params[0], avatarPixel, isCancelled());
}
@Override
@@ -774,26 +839,6 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}
}
- public void loadAvatar(Message message, ImageView imageView) {
- if (cancelPotentialWork(message, imageView)) {
- final Bitmap bm = AvatarService.getInstance().get(message, activity.getPixel(48), true);
- if (bm != null) {
- imageView.setImageBitmap(bm);
- imageView.setBackgroundColor(0x00000000);
- } else {
- imageView.setBackgroundColor(UIHelper.getColorForName(UIHelper.getMessageDisplayName(message)));
- imageView.setImageDrawable(null);
- final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
- final AsyncDrawable asyncDrawable = new AsyncDrawable(activity.getResources(), null, task);
- imageView.setImageDrawable(asyncDrawable);
- try {
- task.execute(message);
- } catch (final RejectedExecutionException ignored) {
- }
- }
- }
- }
-
public static boolean cancelPotentialWork(Message message, ImageView imageView) {
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/dialogs/MessageDetailsDialog.java b/src/main/java/de/thedevstack/conversationsplus/ui/dialogs/MessageDetailsDialog.java
index 4ec8e3eb..c640ce6d 100644
--- a/src/main/java/de/thedevstack/conversationsplus/ui/dialogs/MessageDetailsDialog.java
+++ b/src/main/java/de/thedevstack/conversationsplus/ui/dialogs/MessageDetailsDialog.java
@@ -61,8 +61,9 @@ public class MessageDetailsDialog extends AbstractAlertDialog {
view.findViewById(R.id.dlgMsgDetFileTable).setVisibility(View.VISIBLE);
if (null != message.getFileParams()) {
FileParams params = message.getFileParams();
- TextViewUtil.setText(view, R.id.dlgMsgDetFileSize, UIHelper.getHumanReadableFileSize(params.getSize()));
+ TextViewUtil.setText(view, R.id.dlgMsgDetFileSize, UIHelper.getHumanReadableDetailedFileSize(params.getSize()));
TextViewUtil.setText(view, R.id.dlgMsgDetFileMimeType, params.getMimeType());
+ TextViewUtil.setText(view, R.id.dlgMsgDetFileOriginalName, params.getOriginalFilename());
}
TextViewUtil.setText(view, R.id.dlgMsgDetFileHttpUploaded, message.isHttpUploaded() ? R.string.cplus_yes : R.string.cplus_no);
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/FileUtils.java b/src/main/java/de/thedevstack/conversationsplus/utils/FileUtils.java
index ce289968..77c313f7 100644
--- a/src/main/java/de/thedevstack/conversationsplus/utils/FileUtils.java
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/FileUtils.java
@@ -13,6 +13,7 @@ import android.provider.MediaStore;
import android.provider.OpenableColumns;
import java.io.File;
+import java.net.URL;
import java.util.List;
import de.thedevstack.conversationsplus.ConversationsPlusApplication;
@@ -161,6 +162,14 @@ public final class FileUtils {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
+ public static String getRelevantExtension(URL url) {
+ if (url == null) {
+ return null;
+ }
+ String path = url.getPath();
+ return getRelevantExtension(path);
+ }
+
public static String getRelevantExtension(String path) {
if (path == null || path.isEmpty()) {
return null;
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java b/src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java
index dc9607f0..acd73575 100644
--- a/src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java
@@ -3,6 +3,7 @@ package de.thedevstack.conversationsplus.utils;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
+import java.net.MalformedURLException;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -12,6 +13,7 @@ import de.thedevstack.conversationsplus.entities.Conversation;
import de.thedevstack.conversationsplus.entities.DownloadableFile;
import de.thedevstack.conversationsplus.entities.FileParams;
import de.thedevstack.conversationsplus.entities.Message;
+import de.thedevstack.conversationsplus.entities.Transferable;
import de.thedevstack.conversationsplus.enums.FileStatus;
import de.thedevstack.conversationsplus.persistance.DatabaseBackend;
import de.thedevstack.conversationsplus.persistance.FileBackend;
@@ -21,10 +23,69 @@ import de.thedevstack.conversationsplus.persistance.FileBackend;
*/
public final class MessageUtil {
+ public static void extractFileParamsFromBody(Message message) {
+ if (null == message) {
+ return;
+ }
+ String body = message.getBody();
+ /**
+ * there are a few cases where spaces result in an unwanted behavior, e.g.
+ * "http://example.com/image.jpg" text that will not be shown /abc.png"
+ * or more than one image link in one message.
+ */
+ if (null == body || body.isEmpty() || body.contains(" ")) {
+ return;
+ }
+
+ FileParams fileParams = message.getFileParams();
+ if (null == fileParams) {
+ fileParams = new FileParams();
+ message.setFileParams(fileParams);
+ }
+
+ try {
+ URL url = new URL(body);
+ if (!url.getProtocol().equalsIgnoreCase("http") && !url.getProtocol().equalsIgnoreCase("https")) {
+ message.setTreatAsDownloadable(Message.Decision.NEVER);
+ return;
+ }
+ if (message.isHttpUploaded()) {
+ fileParams.setUrl(url.toString());
+ message.setTreatAsDownloadable(Message.Decision.MUST);
+ return;
+ }
+ String extension = FileUtils.getRelevantExtension(url);
+ if (extension == null) {
+ message.setTreatAsDownloadable(Message.Decision.NEVER);
+ return;
+ }
+ byte[] ivAndKey = UrlUtil.getIvAndKeyFromURL(url);
+
+ if (null != ivAndKey) {
+ if (MimeUtils.guessMimeTypeFromExtension(extension) != null) {
+ message.setTreatAsDownloadable(Message.Decision.MUST);
+ fileParams.setKeyAndIv(ivAndKey);
+ fileParams.setUrl(url.toString());
+ } else {
+ message.setTreatAsDownloadable(Message.Decision.NEVER);
+ }
+ } else if (Transferable.VALID_IMAGE_EXTENSIONS.contains(extension)
+ || Transferable.WELL_KNOWN_EXTENSIONS.contains(extension)) {
+ message.setTreatAsDownloadable(Message.Decision.SHOULD);
+ fileParams.setUrl(url.toString());
+ } else {
+ message.setTreatAsDownloadable(Message.Decision.NEVER);
+ }
+
+ } catch (MalformedURLException e) {
+ message.setTreatAsDownloadable(Message.Decision.NEVER);
+ }
+ }
+
public static boolean needsDownload(Message message) {
FileStatus fileStatus = (null != message.getFileParams()) ? message.getFileParams().getFileStatus() : null;
- return (null != fileStatus && (fileStatus == FileStatus.NEEDS_DOWNLOAD
- || fileStatus == FileStatus.UNDEFINED))
+ return (null == fileStatus
+ || (null != fileStatus && (fileStatus == FileStatus.NEEDS_DOWNLOAD || fileStatus == FileStatus.UNDEFINED)))
&& message.treatAsDownloadable() != Message.Decision.NEVER;
}
@@ -121,6 +182,7 @@ public final class MessageUtil {
FileParams fileParams = message.getFileParams();
if (null == fileParams) {
fileParams = new FileParams();
+ message.setFileParams(fileParams);
}
fileParams.setSize(size);
if (null != url) {
@@ -136,7 +198,6 @@ public final class MessageUtil {
if (null != relativeFilePathFromMessage && relativeFilePathFromMessage.startsWith("/")) {
fileParams.setPath(relativeFilePathFromMessage);
}
- message.setFileParams(fileParams);
}
private MessageUtil() {
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/UIHelper.java b/src/main/java/de/thedevstack/conversationsplus/utils/UIHelper.java
index f27aabb6..a2ed6d81 100644
--- a/src/main/java/de/thedevstack/conversationsplus/utils/UIHelper.java
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/UIHelper.java
@@ -210,16 +210,26 @@ public class UIHelper {
} else if (mime.startsWith("image/")) {
return context.getString(R.string.image);
} else if (mime.contains("pdf")) {
- return context.getString(R.string.pdf_document) ;
+ return context.getString(R.string.pdf_document);
} else if (mime.contains("application/vnd.android.package-archive")) {
- return context.getString(R.string.apk) ;
+ return context.getString(R.string.apk);
} else if (mime.contains("vcard")) {
- return context.getString(R.string.vcard) ;
+ return context.getString(R.string.vcard);
} else {
- return message.getRelativeFilePath();
+ String filename = getDisplayFilename(message);
+ if (null == filename) {
+ return context.getString(R.string.file);
+ } else {
+ return filename;
+ }
}
}
+ public static String getDisplayFilename(final Message message) {
+ String originalFilename = (null != message.getFileParams()) ? message.getFileParams().getOriginalFilename() : null;
+ return (null != originalFilename) ? originalFilename : message.getRelativeFilePath();
+ }
+
public static String getMessageDisplayName(final Message message) {
final Conversation conversation = message.getConversation();
if (message.getStatus() == Message.STATUS_RECEIVED) {
@@ -280,7 +290,20 @@ public class UIHelper {
return LOCATION_QUESTIONS.contains(body);
}
- public static String getHumanReadableFileSize(long filesize) {
+ public static String getHumanReadableFileSize(long size) {
+ String filesize;
+ if (size > (1.5 * 1024 * 1024)) {
+ filesize = size / (1024 * 1024)+ " MiB";
+ } else if (size > 0) {
+ filesize = size / 1024 + " KiB";
+ } else {
+ filesize = null;
+ }
+
+ return filesize;
+ }
+
+ public static String getHumanReadableDetailedFileSize(long filesize) {
if (0 > filesize) {
return "?";
}
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/UrlUtil.java b/src/main/java/de/thedevstack/conversationsplus/utils/UrlUtil.java
new file mode 100644
index 00000000..53bc24d6
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/UrlUtil.java
@@ -0,0 +1,25 @@
+package de.thedevstack.conversationsplus.utils;
+
+import java.net.URL;
+
+import de.thedevstack.conversationsplus.utils.CryptoHelper;
+
+/**
+ * This utility class provides helper methods to handle URLs.
+ */
+
+public final class UrlUtil {
+ public static byte[] getIvAndKeyFromURL(URL url) {
+ if (null == url) {
+ return null;
+ }
+ String reference = url.getRef();
+ boolean linkHasIvAndKey = reference != null && reference.matches("([A-Fa-f0-9]{2}){48}");
+
+ return linkHasIvAndKey ? CryptoHelper.hexToBytes(reference) : null;
+ }
+
+ private UrlUtil() {
+ // Helper Class
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/filetransfer/http/upload/HttpUploadRequestSlotPacketGenerator.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/filetransfer/http/upload/HttpUploadRequestSlotPacketGenerator.java
index eaa90ab0..4b0a956c 100644
--- a/src/main/java/de/thedevstack/conversationsplus/xmpp/filetransfer/http/upload/HttpUploadRequestSlotPacketGenerator.java
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/filetransfer/http/upload/HttpUploadRequestSlotPacketGenerator.java
@@ -33,7 +33,7 @@ public final class HttpUploadRequestSlotPacketGenerator {
public static IqPacket generate(Jid host, String filename, long filesize, String mime) {
SlotRequestPacket packet = new SlotRequestPacket(filename, filesize);
packet.setTo(host);
- packet.setMime((mime == null) ? HttpUpload.DEFAULT_MIME_TYPE : mime);
+ packet.setMime((mime == null || mime.isEmpty()) ? HttpUpload.DEFAULT_MIME_TYPE : mime);
return packet;
}
diff --git a/src/main/res/layout/dialog_message_details.xml b/src/main/res/layout/dialog_message_details.xml
index 7c5d92cb..f2d7af07 100644
--- a/src/main/res/layout/dialog_message_details.xml
+++ b/src/main/res/layout/dialog_message_details.xml
@@ -150,6 +150,28 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:text="@string/dlg_msg_details_original_filename" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text=""
+ android:textAlignment="textEnd"
+ android:gravity="end"
+ android:id="@+id/dlgMsgDetFileOriginalName" />
+ </TableRow>
+
+ <TableRow
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/white"
+ android:layout_marginTop="0.3dp"
+ android:layout_marginLeft="0.3dp"
+ android:layout_marginRight="0.3dp">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:text="@string/dlg_msg_details_file_mime" />
<TextView
diff --git a/src/main/res/values/dimens.xml b/src/main/res/values/dimens.xml
index d5d4aad4..7f20b59b 100644
--- a/src/main/res/values/dimens.xml
+++ b/src/main/res/values/dimens.xml
@@ -9,4 +9,5 @@
<dimen name="ambilwarna_hsvWidth">240dp</dimen>
<dimen name="ambilwarna_hueWidth">30dp</dimen>
<dimen name="ambilwarna_spacer">8dp</dimen>
+ <dimen name="msg_avatar_size">48dp</dimen>
</resources>
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index e356a947..b4b4d2c5 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -679,4 +679,7 @@
<string name="remote_filestatus_delete_failed">Failed to delete remote file</string>
<string name="remote_filestatus_delete_success">Remote file deleted</string>
<string name="remote_filestatus_delete_inprogress">Deleting remote file...</string>
+ <string name="file_not_on_remote_host">No file on remote host</string>
+ <string name="dlg_msg_details_original_filename">Original Filename</string>
+ <string name="cplus_open">Open</string>
</resources>