aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsteckbrief <steckbrief@chefmail.de>2016-09-29 11:57:16 +0200
committersteckbrief <steckbrief@chefmail.de>2016-09-29 11:57:16 +0200
commit34fcdda53fa8ae1174909b62860534d2d874eb72 (patch)
tree50564c1d946ef1779567c67ad25a646b446a6403
parent297bed106efdfa619d700ae44ce047d7f2663c93 (diff)
Implements FS#235: Deletion of remote files uploaded via httpupload
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/crypto/PgpEngine.java11
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlServiceImpl.java7
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/dto/RemoteFile.java (renamed from src/main/java/de/thedevstack/conversationsplus/entities/RemoteFile.java)2
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/entities/Conversation.java7
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/entities/FileParams.java129
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/entities/Message.java170
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/enums/FileStatus.java17
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/generator/MessageGenerator.java24
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/http/HttpConnectionManager.java5
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/http/HttpDownloadConnection.java10
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/http/HttpUploadConnection.java243
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java7
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/persistance/DatabaseBackend.java33
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/persistance/FileBackend.java81
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/persistance/MessageDatabaseAccess.java59
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/persistance/db/access/AbstractDatabaseAccess.java21
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/persistance/db/access/CursorHelper.java (renamed from src/main/java/de/thedevstack/conversationsplus/persistance/CursorHelper.java)32
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/persistance/db/access/MessageDatabaseAccess.java180
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/persistance/db/migrations/FileParamsBodyToDatabaseFieldsMigration.java101
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/services/ExportLogsService.java2
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/services/NotificationService.java3
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/delete/DeleteRemoteFile.java4
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/delete/DeleteRemoteFileService.java11
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/delete/DeleteTokenReceived.java18
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/upload/HttpFileTransferEntity.java11
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/upload/HttpFileUploader.java1
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/upload/HttpUploadFileTransferService.java3
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/ui/ConversationFragment.java4
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/ui/adapter/ConversationAdapter.java2
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/ui/adapter/MessageAdapter.java65
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/ui/dialogs/MessageDetailsDialog.java7
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/ui/listeners/ResizePictureUserDecisionListener.java24
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/ConversationUtil.java7
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/FileUtils.java22
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/ImageUtil.java22
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java61
-rw-r--r--src/main/res/layout/message_sent.xml12
-rw-r--r--src/main/res/values/strings.xml3
38 files changed, 790 insertions, 631 deletions
diff --git a/src/main/java/de/thedevstack/conversationsplus/crypto/PgpEngine.java b/src/main/java/de/thedevstack/conversationsplus/crypto/PgpEngine.java
index d0b30d6d..c97b8395 100644
--- a/src/main/java/de/thedevstack/conversationsplus/crypto/PgpEngine.java
+++ b/src/main/java/de/thedevstack/conversationsplus/crypto/PgpEngine.java
@@ -14,10 +14,8 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.net.URL;
import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
-import de.thedevstack.conversationsplus.utils.MessageUtil;
import de.thedevstack.conversationsplus.utils.StreamUtil;
import de.thedevstack.conversationsplus.R;
import de.thedevstack.conversationsplus.entities.Account;
@@ -106,8 +104,6 @@ public class PgpEngine {
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
OpenPgpApi.RESULT_CODE_ERROR)) {
case OpenPgpApi.RESULT_CODE_SUCCESS:
- URL url = message.getFileParams().url;
- MessageUtil.updateFileParams(message, url);
message.setEncryption(Message.ENCRYPTION_DECRYPTED);
PgpEngine.this.mXmppConnectionService
.updateMessage(message);
@@ -149,12 +145,7 @@ public class PgpEngine {
if (!message.needsUploading()) {
params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
- String body;
- if (message.hasFileOnRemoteHost()) {
- body = message.getFileParams().url.toString();
- } else {
- body = message.getBody();
- }
+ String body = message.getBody();
InputStream is = new ByteArrayInputStream(body.getBytes());
final OutputStream os = new ByteArrayOutputStream();
api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
diff --git a/src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlServiceImpl.java b/src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlServiceImpl.java
index 5e302011..215e0995 100644
--- a/src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlServiceImpl.java
+++ b/src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlServiceImpl.java
@@ -925,12 +925,7 @@ public class AxolotlServiceImpl implements OnAdvancedStreamFeaturesLoaded, Axolo
XmppAxolotlMessage axolotlMessage = buildHeader(message.getConversation());
if (axolotlMessage != null) {
- final String content;
- if (message.hasFileOnRemoteHost()) {
- content = message.getFileParams().url.toString();
- } else {
- content = message.getBody();
- }
+ final String content = message.getBody();
try {
axolotlMessage.encrypt(content);
} catch (CryptoFailedException e) {
diff --git a/src/main/java/de/thedevstack/conversationsplus/entities/RemoteFile.java b/src/main/java/de/thedevstack/conversationsplus/dto/RemoteFile.java
index 76c1dbc9..58b64b28 100644
--- a/src/main/java/de/thedevstack/conversationsplus/entities/RemoteFile.java
+++ b/src/main/java/de/thedevstack/conversationsplus/dto/RemoteFile.java
@@ -1,4 +1,4 @@
-package de.thedevstack.conversationsplus.entities;
+package de.thedevstack.conversationsplus.dto;
import android.support.annotation.NonNull;
diff --git a/src/main/java/de/thedevstack/conversationsplus/entities/Conversation.java b/src/main/java/de/thedevstack/conversationsplus/entities/Conversation.java
index bfd00b5d..c0ae56fd 100644
--- a/src/main/java/de/thedevstack/conversationsplus/entities/Conversation.java
+++ b/src/main/java/de/thedevstack/conversationsplus/entities/Conversation.java
@@ -747,12 +747,7 @@ public class Conversation extends AbstractEntity implements Blockable {
for (int i = this.messages.size() - 1; i >= 0; --i) {
Message message = this.messages.get(i);
if (message.getStatus() == Message.STATUS_UNSEND || message.getStatus() == Message.STATUS_SEND) {
- String otherBody;
- if (message.hasFileOnRemoteHost()) {
- otherBody = message.getFileParams().url.toString();
- } else {
- otherBody = message.getBody();
- }
+ String otherBody = message.getBody();
if (otherBody != null && otherBody.equals(body)) {
return message;
}
diff --git a/src/main/java/de/thedevstack/conversationsplus/entities/FileParams.java b/src/main/java/de/thedevstack/conversationsplus/entities/FileParams.java
new file mode 100644
index 00000000..bce8e571
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/entities/FileParams.java
@@ -0,0 +1,129 @@
+package de.thedevstack.conversationsplus.entities;
+
+import de.thedevstack.conversationsplus.enums.FileStatus;
+import de.thedevstack.conversationsplus.utils.MimeUtils;
+
+/**
+ *
+ */
+public class FileParams {
+ private String name;
+ private String path;
+ private String url;
+ private String mimeType;
+ private long size = 0;
+ private int width = 0;
+ private int height = 0;
+ private FileStatus fileStatus;
+
+ public FileParams() {
+ fileStatus = FileStatus.UNDEFINED;
+ }
+
+ public FileParams(String url) {
+ this();
+ this.url = url;
+ }
+
+ public FileParams(String url, long size) {
+ this(url);
+ this.size = size;
+ }
+
+ public FileParams(String url, long size, int width, int height) {
+ this(url, size);
+ this.width = width;
+ this.height = height;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public long getSize() {
+ return size;
+ }
+
+ public void setSize(long size) {
+ this.size = size;
+ }
+
+ public int getWidth() {
+ return width;
+ }
+
+ public void setWidth(int width) {
+ this.width = width;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+ public void setHeight(int height) {
+ this.height = height;
+ }
+
+ public String getMimeType() {
+ return mimeType;
+ }
+
+ public void setMimeType(String mimeType) {
+ this.mimeType = mimeType;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ /**
+ * 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.
+ * If the file name is stored here and the path does not end with the name - the name is appended to the path.
+ * @param path the path to be stored
+ */
+ public void setPath(String path) {
+ if (null != path) {
+ if (null != this.name) {
+ path = (!path.endsWith(this.name)) ? path + "/" + this.name : path;
+ } else {
+ if (!path.endsWith("/")) {
+ this.setName(path.substring(path.lastIndexOf('/') + 1));
+ }
+ }
+ if (null == this.mimeType) {
+ int start = path.lastIndexOf('.') + 1;
+ if (start < path.length()) {
+ String extension = path.substring(start);
+ this.mimeType = MimeUtils.guessMimeTypeFromExtension(extension);
+ }
+ }
+ }
+
+ this.path = path;
+ }
+
+ public boolean isRemoteAvailable() {
+ return null != this.url || FileStatus.UPLOADED == this.fileStatus || FileStatus.DELETE_FAILED == this.fileStatus;
+ }
+
+ public void setFileStatus(FileStatus fileStatus) {
+ this.fileStatus = fileStatus;
+ }
+
+ public FileStatus getFileStatus() {
+ return this.fileStatus;
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/entities/Message.java b/src/main/java/de/thedevstack/conversationsplus/entities/Message.java
index f081e82a..a81ba404 100644
--- a/src/main/java/de/thedevstack/conversationsplus/entities/Message.java
+++ b/src/main/java/de/thedevstack/conversationsplus/entities/Message.java
@@ -7,6 +7,7 @@ import java.net.MalformedURLException;
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.MimeUtils;
import de.thedevstack.conversationsplus.xmpp.jid.InvalidJidException;
@@ -27,9 +28,6 @@ public class Message extends AbstractEntity {
public static final int STATUS_SEND_RECEIVED = 7;
public static final int STATUS_SEND_DISPLAYED = 8;
- public static final int STATUS_REMOTE_FILE_DELETE_FAILED = 101;
- public static final int STATUS_REMOTE_FILE_DELETED = 102;
-
public static final int ENCRYPTION_NONE = 0;
public static final int ENCRYPTION_PGP = 1;
public static final int ENCRYPTION_OTR = 2;
@@ -87,6 +85,7 @@ public class Message extends AbstractEntity {
private Decision mTreatAsDownloadAble = Decision.NOT_DECIDED;
private boolean httpUploaded;
+ private FileParams fileParams;
private Message() {
@@ -338,6 +337,9 @@ 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)) {
+ this.setFileParams(new FileParams());
+ }
}
public boolean isCarbon() {
@@ -371,17 +373,8 @@ public class Message extends AbstractEntity {
|| message.getBody() == null || message.getCounterpart() == null) {
return false;
} else {
- String body, otherBody;
- if (this.hasFileOnRemoteHost()) {
- body = this.getFileParams().url.toString();
- } else {
- body = this.getBody();
- }
- if (message.hasFileOnRemoteHost()) {
- otherBody = message.getFileParams().url.toString();
- } else {
- otherBody = message.getBody();
- }
+ String body = this.getBody();
+ String otherBody = message.getBody();
if (message.getRemoteMsgId() != null && this.getRemoteMsgId() != null) {
return (message.getRemoteMsgId().equals(this.getRemoteMsgId())
@@ -496,27 +489,10 @@ public class Message extends AbstractEntity {
}
private String extractRelevantExtension(String path) {
- if (path == null || path.isEmpty()) {
- return null;
- }
-
- String filename = path.substring(path.lastIndexOf('/') + 1).toLowerCase();
-
- final String lastPart = FileUtils.getLastExtension(filename);
-
- if (!lastPart.isEmpty()) {
- // we want the real file extension, not the crypto one
- final String secondToLastPart = FileUtils.getSecondToLastExtension(filename);
- if (!secondToLastPart.isEmpty() && Transferable.VALID_CRYPTO_EXTENSIONS.contains(lastPart)) {
- return secondToLastPart;
- } else {
- return lastPart;
- }
- }
- return null;
+ return FileUtils.getRelevantExtension(path);
}
- public String getMimeType() {
+ public String getMimeType() { // TODO: Move to fileparams
if (relativeFilePath != null) {
int start = relativeFilePath.lastIndexOf('.') + 1;
if (start < relativeFilePath.length()) {
@@ -595,103 +571,6 @@ public class Message extends AbstractEntity {
}
}
- public FileParams getFileParams() {
- FileParams params = getLegacyFileParams();
- if (params != null) {
- return params;
- }
- params = new FileParams();
- if (this.transferable != null) {
- params.size = this.transferable.getFileSize();
- }
- if (this.getBody() == null) {
- return params;
- }
- String parts[] = this.getBody().split("\\|");
- switch (parts.length) {
- case 1:
- try {
- params.size = Long.parseLong(parts[0]);
- } catch (NumberFormatException e) {
- try {
- params.url = new URL(parts[0]);
- } catch (MalformedURLException e1) {
- params.url = null;
- }
- }
- break;
- case 2:
- case 4:
- try {
- params.url = new URL(parts[0]);
- } catch (MalformedURLException e1) {
- params.url = null;
- }
- try {
- params.size = Long.parseLong(parts[1]);
- } catch (NumberFormatException e) {
- params.size = 0;
- }
- try {
- params.width = Integer.parseInt(parts[2]);
- } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
- params.width = 0;
- }
- try {
- params.height = Integer.parseInt(parts[3]);
- } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
- params.height = 0;
- }
- break;
- case 3:
- try {
- params.size = Long.parseLong(parts[0]);
- } catch (NumberFormatException e) {
- params.size = 0;
- }
- try {
- params.width = Integer.parseInt(parts[1]);
- } catch (NumberFormatException e) {
- params.width = 0;
- }
- try {
- params.height = Integer.parseInt(parts[2]);
- } catch (NumberFormatException e) {
- params.height = 0;
- }
- break;
- }
- return params;
- }
-
- public FileParams getLegacyFileParams() {
- FileParams params = new FileParams();
- if (this.getBody() == null) {
- return params;
- }
- String parts[] = this.getBody().split(",");
- if (parts.length == 3) {
- try {
- params.size = Long.parseLong(parts[0]);
- } catch (NumberFormatException e) {
- return null;
- }
- try {
- params.width = Integer.parseInt(parts[1]);
- } catch (NumberFormatException e) {
- return null;
- }
- try {
- params.height = Integer.parseInt(parts[2]);
- } catch (NumberFormatException e) {
- return null;
- }
- return params;
- } else {
- return null;
- }
- }
-
public void untie() {
this.mNextMessage = null;
this.mPreviousMessage = null;
@@ -701,19 +580,22 @@ public class Message extends AbstractEntity {
return type == TYPE_FILE || type == TYPE_IMAGE;
}
- public boolean hasFileOnRemoteHost() {
- return isFileOrImage() && getFileParams().url != null;
- }
+ public boolean hasFileAttached() {
+ return isFileOrImage() || isHttpUploaded() || (null != fileParams && null != fileParams.getPath());
+ }
- public boolean needsUploading() {
- return isFileOrImage() && getFileParams().url == null;
+ /*
+ @TODO better
+ */
+ public boolean hasFileOnRemoteHost() {
+ return hasFileAttached() && null != getFileParams() && getFileParams().isRemoteAvailable();
}
- public class FileParams {
- public URL url;
- public long size = 0;
- public int width = 0;
- public int height = 0;
+ /*
+ @TODO better
+ */
+ public boolean needsUploading() {
+ return hasFileAttached() && getFileParams().getFileStatus() == FileStatus.NEEDS_UPLOAD;
}
public void setFingerprint(String fingerprint) {
@@ -768,6 +650,14 @@ public class Message extends AbstractEntity {
this.httpUploaded = httpUploaded;
}
+ public FileParams getFileParams() {
+ return this.fileParams;
+ }
+
+ public void setFileParams(FileParams params) {
+ this.fileParams = params;
+ }
+
private static int getCleanedEncryption(int encryption) {
if (encryption == ENCRYPTION_DECRYPTED || encryption == ENCRYPTION_DECRYPTION_FAILED) {
return ENCRYPTION_PGP;
diff --git a/src/main/java/de/thedevstack/conversationsplus/enums/FileStatus.java b/src/main/java/de/thedevstack/conversationsplus/enums/FileStatus.java
new file mode 100644
index 00000000..b6a4ef9a
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/enums/FileStatus.java
@@ -0,0 +1,17 @@
+package de.thedevstack.conversationsplus.enums;
+
+/**
+ * Created by steckbrief on 23.08.2016.
+ */
+public enum FileStatus {
+ NOT_DOWNLOADED,
+ DOWNLOADED,
+ DOWNLOAD_FAILED,
+ DELETED,
+ DELETE_FAILED,
+ UPLOADED,
+ NEEDS_UPLOAD,
+ UNDEFINED,
+ NEEDS_DOWNLOAD,
+ DELETING;
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/generator/MessageGenerator.java b/src/main/java/de/thedevstack/conversationsplus/generator/MessageGenerator.java
index 5a1d6f3f..678f2c75 100644
--- a/src/main/java/de/thedevstack/conversationsplus/generator/MessageGenerator.java
+++ b/src/main/java/de/thedevstack/conversationsplus/generator/MessageGenerator.java
@@ -13,6 +13,7 @@ import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
import de.thedevstack.conversationsplus.crypto.axolotl.XmppAxolotlMessage;
import de.thedevstack.conversationsplus.entities.Account;
import de.thedevstack.conversationsplus.entities.Conversation;
+import de.thedevstack.conversationsplus.entities.FileParams;
import de.thedevstack.conversationsplus.entities.Message;
import de.thedevstack.conversationsplus.xml.Element;
import de.thedevstack.conversationsplus.xmpp.chatstate.ChatState;
@@ -70,13 +71,13 @@ public class MessageGenerator extends AbstractGenerator {
return packet;
}
- public static void addXhtmlImImage(MessagePacket packet, Message.FileParams params) {
+ public static void addXhtmlImImage(MessagePacket packet, FileParams params) {
Element html = packet.addChild("html", "http://jabber.org/protocol/xhtml-im");
Element body = html.addChild("body", "http://www.w3.org/1999/xhtml");
Element img = body.addChild("img");
- img.setAttribute("src", params.url.toString());
- img.setAttribute("height", params.height);
- img.setAttribute("width", params.width);
+ img.setAttribute("src", params.getUrl());
+ img.setAttribute("height", params.getHeight());
+ img.setAttribute("width", params.getWidth());
}
public static void addMessageHints(MessagePacket packet) {
@@ -94,12 +95,7 @@ public class MessageGenerator extends AbstractGenerator {
MessagePacket packet = preparePacket(message);
addMessageHints(packet);
try {
- String content;
- if (message.hasFileOnRemoteHost()) {
- content = message.getFileParams().url.toString();
- } else {
- content = message.getBody();
- }
+ String content = message.getBody();
packet.setBody(otrSession.transformSending(content)[0]);
return packet;
} catch (OtrException e) {
@@ -111,14 +107,14 @@ public class MessageGenerator extends AbstractGenerator {
MessagePacket packet = preparePacket(message);
String content;
if (message.hasFileOnRemoteHost()) {
- Message.FileParams fileParams = message.getFileParams();
- content = fileParams.url.toString();
+ FileParams fileParams = message.getFileParams();
+ content = message.getBody();
if (message.isHttpUploaded()) {
packet.addChild(new HttpUploadHint());
}
packet.addChild("x","jabber:x:oob").addChild("url").setContent(content);
- if (fileParams.width > 0 && fileParams.height > 0) {
- addXhtmlImImage(packet,fileParams);
+ if (fileParams.getWidth() > 0 && fileParams.getHeight() > 0) {
+ addXhtmlImImage(packet, fileParams);
}
} else {
content = message.getBody();
diff --git a/src/main/java/de/thedevstack/conversationsplus/http/HttpConnectionManager.java b/src/main/java/de/thedevstack/conversationsplus/http/HttpConnectionManager.java
index 529d12c4..686587c7 100644
--- a/src/main/java/de/thedevstack/conversationsplus/http/HttpConnectionManager.java
+++ b/src/main/java/de/thedevstack/conversationsplus/http/HttpConnectionManager.java
@@ -32,7 +32,6 @@ public class HttpConnectionManager extends AbstractConnectionManager {
}
private List<HttpDownloadConnection> downloadConnections = new CopyOnWriteArrayList<>();
- private List<HttpUploadConnection> uploadConnections = new CopyOnWriteArrayList<>();
public static HttpDownloadConnection createNewDownloadConnection(Message message) {
return createNewDownloadConnection(message, false);
@@ -49,10 +48,6 @@ public class HttpConnectionManager extends AbstractConnectionManager {
this.downloadConnections.remove(connection);
}
- public void finishUploadConnection(HttpUploadConnection httpUploadConnection) {
- this.uploadConnections.remove(httpUploadConnection);
- }
-
public static void setupTrustManager(final HttpsURLConnection connection, final boolean interactive) {
final X509TrustManager trustManager;
final HostnameVerifier hostnameVerifier;
diff --git a/src/main/java/de/thedevstack/conversationsplus/http/HttpDownloadConnection.java b/src/main/java/de/thedevstack/conversationsplus/http/HttpDownloadConnection.java
index 3facc14a..d9fc9584 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.enums.FileStatus;
import de.thedevstack.conversationsplus.exceptions.RemoteFileNotFoundException;
import de.thedevstack.conversationsplus.utils.MessageUtil;
import de.thedevstack.conversationsplus.utils.StreamUtil;
@@ -74,11 +75,7 @@ public class HttpDownloadConnection implements Transferable {
this.message = message;
this.message.setTransferable(this);
try {
- if (message.hasFileOnRemoteHost()) {
- mUrl = message.getFileParams().url;
- } else {
- mUrl = new URL(message.getBody());
- }
+ mUrl = new URL(message.getFileParams().getUrl());
final String sUrlFilename = mUrl.getPath().substring(mUrl.getPath().lastIndexOf('/')).toLowerCase();
final String lastPart = FileUtils.getLastExtension(sUrlFilename);
@@ -131,6 +128,7 @@ public class HttpDownloadConnection implements Transferable {
private void finish() {
FileBackend.updateMediaScanner(file, mXmppConnectionService);
message.setTransferable(null);
+ MessageUtil.setAndSaveFileStatus(this.message, FileStatus.DOWNLOADED);
mHttpConnectionManager.finishConnection(this);
if (message.getEncryption() == Message.ENCRYPTION_PGP) {
message.getConversation().getAccount().getPgpDecryptionService().add(message);
@@ -176,7 +174,7 @@ public class HttpDownloadConnection implements Transferable {
HttpDownloadConnection.this.mXmppConnectionService.getNotificationService().push(message);
return;
} catch (RemoteFileNotFoundException e) {
- message.setNoDownloadable();
+ message.setNoDownloadable(); // TODO Set remote file status to not-available
cancel();
return;
} catch (IOException e) {
diff --git a/src/main/java/de/thedevstack/conversationsplus/http/HttpUploadConnection.java b/src/main/java/de/thedevstack/conversationsplus/http/HttpUploadConnection.java
deleted file mode 100644
index 3c49d083..00000000
--- a/src/main/java/de/thedevstack/conversationsplus/http/HttpUploadConnection.java
+++ /dev/null
@@ -1,243 +0,0 @@
-package de.thedevstack.conversationsplus.http;
-
-import android.app.PendingIntent;
-import android.os.PowerManager;
-import android.util.Pair;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.HttpURLConnection;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.Scanner;
-
-import javax.net.ssl.HttpsURLConnection;
-
-import de.thedevstack.android.logcat.Logging;
-import de.thedevstack.conversationsplus.ConversationsPlusApplication;
-import de.thedevstack.conversationsplus.utils.MessageUtil;
-import de.thedevstack.conversationsplus.utils.StreamUtil;
-import de.thedevstack.conversationsplus.Config;
-import de.thedevstack.conversationsplus.entities.Account;
-import de.thedevstack.conversationsplus.entities.DownloadableFile;
-import de.thedevstack.conversationsplus.entities.Message;
-import de.thedevstack.conversationsplus.entities.Transferable;
-import de.thedevstack.conversationsplus.persistance.FileBackend;
-import de.thedevstack.conversationsplus.services.AbstractConnectionManager;
-import de.thedevstack.conversationsplus.services.XmppConnectionService;
-import de.thedevstack.conversationsplus.ui.UiCallback;
-import de.thedevstack.conversationsplus.utils.CryptoHelper;
-import de.thedevstack.conversationsplus.utils.XmppConnectionServiceAccessor;
-import de.thedevstack.conversationsplus.xmpp.OnIqPacketReceived;
-import de.thedevstack.conversationsplus.xmpp.exceptions.XmppException;
-import de.thedevstack.conversationsplus.xmpp.filetransfer.http.upload.HttpUpload;
-import de.thedevstack.conversationsplus.xmpp.filetransfer.http.upload.HttpUploadRequestSlotPacketGenerator;
-import de.thedevstack.conversationsplus.xmpp.filetransfer.http.upload.HttpUploadSlot;
-import de.thedevstack.conversationsplus.xmpp.filetransfer.http.upload.SlotPacketParser;
-import de.thedevstack.conversationsplus.xmpp.jid.Jid;
-import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket;
-
-@Deprecated
-public class HttpUploadConnection implements Transferable {
-
- private HttpConnectionManager mHttpConnectionManager;
- private XmppConnectionService mXmppConnectionService;
-
- private boolean canceled = false;
- private boolean delayed = false;
- private Account account;
- private DownloadableFile file;
- private Message message;
- private String mime;
- private URL mGetUrl;
- private URL mPutUrl;
-
- private byte[] key = null;
-
- private long transmitted = 0;
-
- private InputStream mFileInputStream;
-
- public HttpUploadConnection(HttpConnectionManager httpConnectionManager) {
- this.mHttpConnectionManager = httpConnectionManager;
- this.mXmppConnectionService = XmppConnectionServiceAccessor.xmppConnectionService;
- }
-
- @Override
- public boolean start() {
- return false;
- }
-
- @Override
- public int getStatus() {
- return STATUS_UPLOADING;
- }
-
- @Override
- public long getFileSize() {
- return file == null ? 0 : file.getExpectedSize();
- }
-
- @Override
- public int getProgress() {
- if (file == null) {
- return 0;
- }
- return (int) ((((double) transmitted) / file.getExpectedSize()) * 100);
- }
-
- @Override
- public void cancel() {
- this.canceled = true;
- }
-
- private void fail() {
- mHttpConnectionManager.finishUploadConnection(this);
- message.setTransferable(null);
- MessageUtil.markMessage(message, Message.STATUS_SEND_FAILED);
- StreamUtil.close(mFileInputStream);
- }
-
- public void init(Message message, boolean delay) {
- this.message = message;
- this.message.setHttpUploaded(true);
- this.message.setNoDownloadable();
- this.account = message.getConversation().getAccount();
- this.file = FileBackend.getFile(message, false);
- this.mime = this.file.getMimeType();
- this.delayed = delay;
- if (Config.ENCRYPT_ON_HTTP_UPLOADED
- || message.getEncryption() == Message.ENCRYPTION_AXOLOTL
- || message.getEncryption() == Message.ENCRYPTION_OTR) {
- this.key = new byte[48];
- ConversationsPlusApplication.getSecureRandom().nextBytes(this.key);
- this.file.setKeyAndIv(this.key);
- }
- Pair<InputStream,Integer> pair;
- try {
- pair = AbstractConnectionManager.createInputStream(file, true);
- } catch (FileNotFoundException e) {
- fail();
- return;
- }
- this.file.setExpectedSize(pair.second);
- this.mFileInputStream = pair.first;
- Jid host = account.getXmppConnection().findDiscoItemByFeature(HttpUpload.NAMESPACE);
- IqPacket request = HttpUploadRequestSlotPacketGenerator.generate(host, file.getName(), file.getSize(), mime);
- mXmppConnectionService.sendIqPacket(account, request, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- try {
- HttpUploadSlot slot = SlotPacketParser.parseGetAndPutUrl(packet);
- mGetUrl = new URL(slot.getGetUrl());
- mPutUrl = new URL(slot.getPutUrl());
- if (!canceled) {
- new Thread(new FileUploader()).start();
- }
- } catch (XmppException e) {
- Logging.e("httpupload", e.getMessage());
- fail();
- } catch (MalformedURLException e) {
- Logging.e("httpupload", "malformed url retrieved from slot", e);
- fail();
- }
- }
- });
- message.setTransferable(this);
- MessageUtil.markMessage(message, Message.STATUS_UNSEND);
- }
-
- private class FileUploader implements Runnable {
-
- @Override
- public void run() {
- this.upload();
- }
-
- private void upload() {
- OutputStream os = null;
- InputStream errorStream = null;
- HttpURLConnection connection = null;
- PowerManager.WakeLock wakeLock = ConversationsPlusApplication.createPartialWakeLock("http_upload_"+message.getUuid());
- try {
- wakeLock.acquire();
- Logging.d(Config.LOGTAG, "uploading to " + mPutUrl.toString());
- connection = (HttpURLConnection) mPutUrl.openConnection();
-
- if (connection instanceof HttpsURLConnection) {
- mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, true);
- }
- connection.setRequestMethod("PUT");
- connection.setFixedLengthStreamingMode((int) file.getExpectedSize());
- connection.setRequestProperty("Content-Type", mime == null ? "application/octet-stream" : mime);
- connection.setRequestProperty("User-Agent", ConversationsPlusApplication.getNameAndVersion());
- connection.setDoOutput(true);
- connection.connect();
- os = connection.getOutputStream();
- transmitted = 0;
- int count = -1;
- byte[] buffer = new byte[4096];
- while (((count = mFileInputStream.read(buffer)) != -1) && !canceled) {
- transmitted += count;
- os.write(buffer, 0, count);
- mXmppConnectionService.updateConversationUi();
- }
- os.flush();
- os.close();
- mFileInputStream.close();
- int code = connection.getResponseCode();
- if (code == 200 || code == 201) {
- Logging.d(Config.LOGTAG, "finished uploading file");
- if (key != null) {
- mGetUrl = new URL(mGetUrl.toString() + "#" + CryptoHelper.bytesToHex(key));
- }
- MessageUtil.updateFileParams(message, mGetUrl);
- FileBackend.updateMediaScanner(file, mXmppConnectionService);
- message.setTransferable(null);
- message.setCounterpart(message.getConversation().getJid().toBareJid());
- if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
- mXmppConnectionService.getPgpEngine().encrypt(message, new UiCallback<Message>() {
- @Override
- public void success(Message message) {
- mXmppConnectionService.resendMessage(message,delayed);
- }
-
- @Override
- public void error(int errorCode, Message object) {
- fail();
- }
-
- @Override
- public void userInputRequried(PendingIntent pi, Message object) {
- fail();
- }
- });
- } else {
- mXmppConnectionService.resendMessage(message, delayed);
- }
- } else {
- errorStream = connection.getErrorStream();
- Logging.e("httpupload", "file upload failed: http code (" + code + ") " + new Scanner(errorStream).useDelimiter("\\A").next());
- fail();
- }
- } catch (IOException e) {
- errorStream = (null != connection) ? connection.getErrorStream() : null;
- String httpResponseMessage = null;
- if (null != errorStream) {
- httpResponseMessage = new Scanner(errorStream).useDelimiter("\\A").next();
- }
- Logging.e("httpupload", ((null != httpResponseMessage) ? ("http response: " + httpResponseMessage + ", ") : "") + "exception message: " + e.getMessage());
- fail();
- } finally {
- StreamUtil.close(os);
- StreamUtil.close(errorStream);
- if (connection != null) {
- connection.disconnect();
- }
- wakeLock.release();
- }
- }
- }
-}
diff --git a/src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java b/src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java
index 2f1701df..b02677a8 100644
--- a/src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java
+++ b/src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java
@@ -3,6 +3,8 @@ package de.thedevstack.conversationsplus.parser;
import android.util.Log;
import android.util.Pair;
+import de.thedevstack.conversationsplus.entities.FileParams;
+import de.thedevstack.conversationsplus.enums.FileStatus;
import de.thedevstack.conversationsplus.utils.MessageUtil;
import de.thedevstack.conversationsplus.xmpp.httpuploadim.HttpUploadHint;
import de.tzur.conversations.Settings;
@@ -10,7 +12,6 @@ import de.tzur.conversations.Settings;
import net.java.otr4j.session.Session;
import net.java.otr4j.session.SessionStatus;
-import java.net.URL;
import java.util.ArrayList;
import java.util.Set;
@@ -472,6 +473,10 @@ public class MessageParser extends AbstractParser implements
&& 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);
} else {
if (query == null) {
diff --git a/src/main/java/de/thedevstack/conversationsplus/persistance/DatabaseBackend.java b/src/main/java/de/thedevstack/conversationsplus/persistance/DatabaseBackend.java
index 7c2583ae..e721afbb 100644
--- a/src/main/java/de/thedevstack/conversationsplus/persistance/DatabaseBackend.java
+++ b/src/main/java/de/thedevstack/conversationsplus/persistance/DatabaseBackend.java
@@ -35,6 +35,7 @@ import org.json.JSONException;
import de.thedevstack.android.logcat.Logging;
import de.thedevstack.conversationsplus.Config;
+import de.thedevstack.conversationsplus.ConversationsPlusApplication;
import de.thedevstack.conversationsplus.crypto.axolotl.AxolotlServiceImpl;
import de.thedevstack.conversationsplus.crypto.axolotl.SQLiteAxolotlStore;
import de.thedevstack.conversationsplus.crypto.axolotl.XmppAxolotlSession;
@@ -44,6 +45,8 @@ import de.thedevstack.conversationsplus.entities.Conversation;
import de.thedevstack.conversationsplus.entities.Message;
import de.thedevstack.conversationsplus.entities.Roster;
import de.thedevstack.conversationsplus.entities.ServiceDiscoveryResult;
+import de.thedevstack.conversationsplus.persistance.db.access.CursorHelper;
+import de.thedevstack.conversationsplus.persistance.db.access.MessageDatabaseAccess;
import de.thedevstack.conversationsplus.utils.SimpleCryptoUtil;
import de.thedevstack.conversationsplus.xmpp.jid.InvalidJidException;
import de.thedevstack.conversationsplus.xmpp.jid.Jid;
@@ -55,7 +58,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "history";
private static final int DATABASE_VERSION = 25;
private static final int C_TO_CPLUS_VERSION_OFFSET = 1000;
- private static final int CPLUS_DATABASE_VERSION = 2;
+ private static final int CPLUS_DATABASE_VERSION = 3;
private static final int CPLUS_DATABASE_VERSION_MULTIPLIER = 100;
private static final int PHYSICAL_DATABASE_VERSION = DATABASE_VERSION + C_TO_CPLUS_VERSION_OFFSET + (CPLUS_DATABASE_VERSION * CPLUS_DATABASE_VERSION_MULTIPLIER);
@@ -191,18 +194,14 @@ public class DatabaseBackend extends SQLiteOpenHelper {
db.execSQL(CREATE_SIGNED_PREKEYS_STATEMENT);
db.execSQL(CREATE_IDENTITIES_STATEMENT);
- // Create Conversations+ related tables
- db.execSQL(MessageDatabaseAccess.TABLE_ADDITIONAL_PARAMETERS_CREATE_V0);
+ MessageDatabaseAccess.create(db);
}
protected void onUpgradeCPlusDatabase(SQLiteDatabase db, int oldVersion, int newVersion) {
Logging.d("db.upgrade.cplus", "Updating Conversations+ database from version '" + oldVersion + "' to '" + newVersion + "'");
if (oldVersion < newVersion) {
if (oldVersion < 1 && newVersion >= 1) {
- Logging.d("db.upgrade.cplus", "Creating additional parameters table for messages.");
- db.execSQL(MessageDatabaseAccess.TABLE_ADDITIONAL_PARAMETERS_CREATE_V0);
- db.execSQL("INSERT INTO " + MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS + "(" + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + ") "
- + " SELECT " + Message.UUID + " FROM " + Message.TABLENAME);
+ MessageDatabaseAccess.upgrade(db, oldVersion, newVersion);
}
if (oldVersion < 2 && newVersion >= 2) {
Logging.d("db.upgrade.cplus", "Encrypt all passwords for the first time");
@@ -217,6 +216,10 @@ public class DatabaseBackend extends SQLiteOpenHelper {
}
cursor.close();
}
+
+ if (oldVersion < 3 && newVersion >= 3) {
+ MessageDatabaseAccess.upgrade(db, oldVersion, newVersion);
+ }
}
}
@@ -434,6 +437,10 @@ public class DatabaseBackend extends SQLiteOpenHelper {
}
}
+ public static synchronized DatabaseBackend getInstance() {
+ return getInstance(ConversationsPlusApplication.getAppContext());
+ }
+
public static synchronized DatabaseBackend getInstance(Context context) {
if (instance == null) {
instance = new DatabaseBackend(context);
@@ -657,15 +664,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
}
public void updateMessage(Message message) {
- Logging.d("db.msg.update", "Updating message with uuid '" + message.getUuid() + "', isRead: " + message.isRead());
- SQLiteDatabase db = this.getWritableDatabase();
- String[] args = {message.getUuid()};
- db.beginTransaction();
- db.update(Message.TABLENAME, message.getContentValues(), Message.UUID
- + "=?", args);
- db.update(MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS, MessageDatabaseAccess.getAdditionalParametersContentValues(message), MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + "=?", args);
- db.setTransactionSuccessful();
- db.endTransaction();
+ this.updateMessage(message, message.getUuid());
}
public void updateMessage(Message message, String uuid) {
@@ -676,7 +675,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
db.beginTransaction();
db.update(Message.TABLENAME, message.getContentValues(), Message.UUID
+ "=?", args);
- db.update(MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS, MessageDatabaseAccess.getAdditionalParametersContentValues(message), MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + "=?", args);
+ MessageDatabaseAccess.updateMessageParameters(db, message, uuid);
db.setTransactionSuccessful();
db.endTransaction();
}
diff --git a/src/main/java/de/thedevstack/conversationsplus/persistance/FileBackend.java b/src/main/java/de/thedevstack/conversationsplus/persistance/FileBackend.java
index e5597c3c..a35ed043 100644
--- a/src/main/java/de/thedevstack/conversationsplus/persistance/FileBackend.java
+++ b/src/main/java/de/thedevstack/conversationsplus/persistance/FileBackend.java
@@ -20,6 +20,7 @@ import java.util.Locale;
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.exceptions.FileCopyException;
import de.thedevstack.conversationsplus.persistance.observers.FileDeletionObserver;
import de.thedevstack.conversationsplus.utils.StreamUtil;
@@ -106,31 +107,55 @@ public class FileBackend {
}
public static DownloadableFile getFile(Message message, boolean decrypted) {
- final boolean encrypted = !decrypted
- && (message.getEncryption() == Message.ENCRYPTION_PGP
- || message.getEncryption() == Message.ENCRYPTION_DECRYPTED);
- final DownloadableFile file;
- String path = message.getRelativeFilePath();
- if (path == null) {
- path = message.getUuid();
- }
- if (path.startsWith("/")) {
- file = new DownloadableFile(path);
- } else {
- String mime = message.getMimeType();
- if (mime != null && mime.startsWith("image")) {
- file = new DownloadableFile(getConversationsImageDirectory() + path);
- } else {
- file = new DownloadableFile(getConversationsFileDirectory() + path);
- }
- }
- if (encrypted) {
- return new DownloadableFile(getConversationsFileDirectory() + file.getName() + ".pgp");
- } else {
- return file;
- }
+ return new DownloadableFile(getFilePath(message, decrypted));
}
+ protected static String getFilePath(Message message, String extension) {
+ String path = FileBackend.getFilePath(message, true);
+ if (!path.endsWith(extension)) {
+ path += "." + extension;
+ }
+
+ return path;
+ }
+
+ protected static String getFilePath(Message message, Uri uri) {
+ String mime = ConversationsPlusApplication.getInstance().getContentResolver().getType(uri);
+ String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mime);
+
+ return getFilePath(message, extension);
+ }
+
+ protected static String getFilePath(Message message, boolean decrypted) {
+ final boolean encrypted = !decrypted
+ && (message.getEncryption() == Message.ENCRYPTION_PGP
+ || message.getEncryption() == Message.ENCRYPTION_DECRYPTED);
+ FileParams fileParams = message.getFileParams();
+ if (null == fileParams) {
+ fileParams = new FileParams();
+ message.setFileParams(fileParams);
+ }
+ String path = fileParams.getPath();
+
+ if (null == path) { // File does not yet exist
+ path = message.getUuid();
+ String mime = message.getMimeType();
+ if (mime != null && mime.startsWith("image")) { // TODO: Check if this can be determined in a better way
+ path = getConversationsImageDirectory() + path;
+ } else {
+ path = getConversationsFileDirectory() + path;
+ }
+ String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mime);
+ path += "." + extension;
+
+ fileParams.setPath(path);
+ }
+ if (encrypted) {
+ path += ".pgp";
+ }
+ return path;
+ }
+
public static String getConversationsFileDirectory() {
return Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + ConversationsPlusPreferences.fileTransferFolder() + File.separator;
}
@@ -175,14 +200,16 @@ public class FileBackend {
public static void copyFileToPrivateStorage(Message message, Uri uri) throws FileCopyException {
Log.d(Config.LOGTAG, "copy " + uri.toString() + " to private storage");
- String mime = ConversationsPlusApplication.getInstance().getContentResolver().getType(uri);
- String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mime);
- message.setRelativeFilePath(message.getUuid() + "." + extension);
+ String path = getFilePath(message, uri);
+ message.getFileParams().setPath(path);
+ message.setRelativeFilePath(path); // TODO: Remove
copyFileToPrivateStorage(getFile(message), uri);
}
public static DownloadableFile compressImageAndCopyToPrivateStorage(Message message, Bitmap scaledBitmap) throws FileCopyException {
- message.setRelativeFilePath(FileBackend.getPrivateImageDirectoryPath() + message.getUuid() + ".jpg");
+ String path = getFilePath(message, "jpg");
+ message.getFileParams().setPath(path);
+ message.setRelativeFilePath(path); // TODO: Remove
DownloadableFile file = getFile(message);
file.getParentFile().mkdirs();
OutputStream os = null;
diff --git a/src/main/java/de/thedevstack/conversationsplus/persistance/MessageDatabaseAccess.java b/src/main/java/de/thedevstack/conversationsplus/persistance/MessageDatabaseAccess.java
deleted file mode 100644
index 7776174d..00000000
--- a/src/main/java/de/thedevstack/conversationsplus/persistance/MessageDatabaseAccess.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package de.thedevstack.conversationsplus.persistance;
-
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-
-import de.thedevstack.android.logcat.Logging;
-import de.thedevstack.conversationsplus.entities.Message;
-import de.thedevstack.conversationsplus.xmpp.jid.InvalidJidException;
-import de.thedevstack.conversationsplus.xmpp.jid.Jid;
-
-/**
- * Created by steckbrief on 15.04.2016.
- */
-public class MessageDatabaseAccess {
- static final String TABLE_NAME_ADDITIONAL_PARAMETERS = "message_parameters";
- static final String COLUMN_NAME_MSG_PARAMS_HTTPUPLOAD = "httpupload";
- static final String COLUMN_NAME_MSG_PARAMS_MSGUUID = "message_uuid";
- static final String COLUMN_NAME_MSG_PARAMS_TREATASDOWNLOADABLE_DECISION = "treatasdownloadable_decision";
-
- static final String TABLE_ADDITIONAL_PARAMETERS_CREATE_V0 = "CREATE TABLE " + MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS + " ("
- + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + " TEXT, "
- + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_HTTPUPLOAD + " INTEGER DEFAULT 0, "
- + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_TREATASDOWNLOADABLE_DECISION + " TEXT DEFAULT 'NOT_DECIDED', "
- + "FOREIGN KEY(" + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + ") REFERENCES " + Message.TABLENAME + "(" + Message.UUID + ") ON DELETE CASCADE)";
-
- static ContentValues getAdditionalParametersContentValues(Message message) {
- ContentValues additionalParameters = new ContentValues();
- additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID, message.getUuid());
- additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_HTTPUPLOAD, message.isHttpUploaded() ? 1 : 0);
- additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_TREATASDOWNLOADABLE_DECISION, message.treatAsDownloadable().name());
-
- return additionalParameters;
- }
-
- static void populateMessageParametersFromCursor(Cursor cursor, Message message) {
- boolean isHttpUploaded = CursorHelper.getInt(cursor, COLUMN_NAME_MSG_PARAMS_HTTPUPLOAD) == 1;
- message.setHttpUploaded(isHttpUploaded);
- String downloadable = CursorHelper.getString(cursor, COLUMN_NAME_MSG_PARAMS_TREATASDOWNLOADABLE_DECISION);
- Message.Decision treatAsDownloadable = Message.Decision.NOT_DECIDED;
- try {
- treatAsDownloadable = Message.Decision.valueOf(downloadable);
- } catch (IllegalArgumentException e) {
- // Should only happen if the database is corrupted, but to be on the save side catch it here
- Logging.e("db.msg", "Unknown Decision for treatAsDownloadable found: '" + downloadable + "'");
- }
- message.setTreatAsDownloadable(treatAsDownloadable);
- }
-
- static void populateMessageParameters(SQLiteDatabase db, Message message) {
- Cursor paramsCursor = db.query(MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS,
- null, MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + "=?",
- new String[] {message.getUuid()},
- null, null, null, null);
- paramsCursor.moveToNext();
- MessageDatabaseAccess.populateMessageParametersFromCursor(paramsCursor, message);
- paramsCursor.close();
- }
-}
diff --git a/src/main/java/de/thedevstack/conversationsplus/persistance/db/access/AbstractDatabaseAccess.java b/src/main/java/de/thedevstack/conversationsplus/persistance/db/access/AbstractDatabaseAccess.java
new file mode 100644
index 00000000..407859c7
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/persistance/db/access/AbstractDatabaseAccess.java
@@ -0,0 +1,21 @@
+package de.thedevstack.conversationsplus.persistance.db.access;
+
+import android.database.sqlite.SQLiteDatabase;
+
+/**
+ * Created by steckbrief on 24.08.2016.
+ */
+public abstract class AbstractDatabaseAccess {
+ static void executeAlterStatements(SQLiteDatabase db, String... alterStatements) {
+ for (String alterStatement : alterStatements) {
+ db.execSQL(alterStatement);
+ }
+ }
+
+ static void addNewColumns(SQLiteDatabase db, String tableName, String... columnDefinitions) {
+ for (String columnDefinition : columnDefinitions) {
+ String alterStatement = "ALTER TABLE " + tableName + " ADD COLUMN " + columnDefinition;
+ db.execSQL(alterStatement);
+ }
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/persistance/CursorHelper.java b/src/main/java/de/thedevstack/conversationsplus/persistance/db/access/CursorHelper.java
index 7e3fdab0..122e5160 100644
--- a/src/main/java/de/thedevstack/conversationsplus/persistance/CursorHelper.java
+++ b/src/main/java/de/thedevstack/conversationsplus/persistance/db/access/CursorHelper.java
@@ -1,4 +1,4 @@
-package de.thedevstack.conversationsplus.persistance;
+package de.thedevstack.conversationsplus.persistance.db.access;
import android.database.Cursor;
@@ -7,7 +7,7 @@ import android.database.Cursor;
*/
public abstract class CursorHelper {
- static double getDouble(Cursor cursor, String columnName) {
+ public static double getDouble(Cursor cursor, String columnName) {
if (null == cursor) {
return Double.MIN_VALUE;
}
@@ -18,7 +18,7 @@ public abstract class CursorHelper {
return getDouble(cursor, columnIndex);
}
- static String getString(Cursor cursor, String columnName) {
+ public static String getString(Cursor cursor, String columnName) {
if (null == cursor) {
return null;
}
@@ -29,7 +29,7 @@ public abstract class CursorHelper {
return getString(cursor, columnIndex);
}
- static float getFloat(Cursor cursor, String columnName) {
+ public static float getFloat(Cursor cursor, String columnName) {
if (null == cursor) {
return Float.MIN_VALUE;
}
@@ -40,7 +40,7 @@ public abstract class CursorHelper {
return getFloat(cursor, columnIndex);
}
- static int getInt(Cursor cursor, String columnName) {
+ public static int getInt(Cursor cursor, String columnName) {
if (null == cursor) {
return Integer.MIN_VALUE;
}
@@ -51,7 +51,7 @@ public abstract class CursorHelper {
return getInt(cursor, columnIndex);
}
- static long getLong(Cursor cursor, String columnName) {
+ public static long getLong(Cursor cursor, String columnName) {
if (null == cursor) {
return Long.MIN_VALUE;
}
@@ -62,7 +62,7 @@ public abstract class CursorHelper {
return getLong(cursor, columnIndex);
}
- static int getShort(Cursor cursor, String columnName) {
+ public static int getShort(Cursor cursor, String columnName) {
if (null == cursor) {
return Short.MIN_VALUE;
}
@@ -73,7 +73,7 @@ public abstract class CursorHelper {
return getShort(cursor, columnIndex);
}
- static byte[] getBlob(Cursor cursor, String columnName) {
+ public static byte[] getBlob(Cursor cursor, String columnName) {
if (null == cursor) {
return null;
}
@@ -94,7 +94,7 @@ public abstract class CursorHelper {
* the column name does not exist.
* @see Cursor#getColumnIndexOrThrow(String)
*/
- static int getColumnIndex(Cursor cursor, String columnName) {
+ public static int getColumnIndex(Cursor cursor, String columnName) {
return cursor.getColumnIndex(columnName);
}
@@ -108,7 +108,7 @@ public abstract class CursorHelper {
* @param columnIndex the zero-based index of the target column.
* @return the value of that column as a byte array.
*/
- static byte[] getBlob(Cursor cursor, int columnIndex) {
+ public static byte[] getBlob(Cursor cursor, int columnIndex) {
return cursor.getBlob(columnIndex);
}
@@ -122,7 +122,7 @@ public abstract class CursorHelper {
* @param columnIndex the zero-based index of the target column.
* @return the value of that column as a String.
*/
- static String getString(Cursor cursor, int columnIndex) {
+ public static String getString(Cursor cursor, int columnIndex) {
return cursor.getString(columnIndex);
}
@@ -137,7 +137,7 @@ public abstract class CursorHelper {
* @param columnIndex the zero-based index of the target column.
* @return the value of that column as a short.
*/
- static short getShort(Cursor cursor, int columnIndex) {
+ public static short getShort(Cursor cursor, int columnIndex) {
return cursor.getShort(columnIndex);
}
@@ -152,7 +152,7 @@ public abstract class CursorHelper {
* @param columnIndex the zero-based index of the target column.
* @return the value of that column as an int.
*/
- static int getInt(Cursor cursor, int columnIndex) {
+ public static int getInt(Cursor cursor, int columnIndex) {
return cursor.getInt(columnIndex);
}
@@ -167,7 +167,7 @@ public abstract class CursorHelper {
* @param columnIndex the zero-based index of the target column.
* @return the value of that column as a long.
*/
- static long getLong(Cursor cursor, int columnIndex) {
+ public static long getLong(Cursor cursor, int columnIndex) {
return cursor.getLong(columnIndex);
}
@@ -182,7 +182,7 @@ public abstract class CursorHelper {
* @param columnIndex the zero-based index of the target column.
* @return the value of that column as a float.
*/
- static float getFloat(Cursor cursor, int columnIndex) {
+ public static float getFloat(Cursor cursor, int columnIndex) {
return cursor.getFloat(columnIndex);
}
@@ -197,7 +197,7 @@ public abstract class CursorHelper {
* @param columnIndex the zero-based index of the target column.
* @return the value of that column as a double.
*/
- static double getDouble(Cursor cursor, int columnIndex) {
+ public static double getDouble(Cursor cursor, int columnIndex) {
return cursor.getDouble(columnIndex);
}
}
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
new file mode 100644
index 00000000..51ad99a4
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/persistance/db/access/MessageDatabaseAccess.java
@@ -0,0 +1,180 @@
+package de.thedevstack.conversationsplus.persistance.db.access;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+
+import de.thedevstack.android.logcat.Logging;
+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;
+
+/**
+ *
+ */
+public class MessageDatabaseAccess extends AbstractDatabaseAccess {
+ // since cplus db version 1
+ public static final String TABLE_NAME_ADDITIONAL_PARAMETERS = "message_parameters";
+ private static final String COLUMN_NAME_MSG_PARAMS_HTTPUPLOAD = "httpupload";
+ private static final String COLUMN_NAME_MSG_PARAMS_MSGUUID = "message_uuid";
+ private static final String COLUMN_NAME_MSG_PARAMS_TREATASDOWNLOADABLE_DECISION = "treatasdownloadable_decision";
+ // since cplus db version 3
+ private static final String COLUMN_NAME_MSG_PARAMS_FILE_SIZE = "file_size";
+ private static final String COLUMN_NAME_MSG_PARAMS_FILE_TYPE = "file_type";
+ private static final String COLUMN_NAME_MSG_PARAMS_FILE_WIDTH = "file_width";
+ private static final String COLUMN_NAME_MSG_PARAMS_FILE_HEIGHT = "file_height";
+ private static final String COLUMN_NAME_MSG_PARAMS_FILE_STATUS = "file_status";
+ 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 TABLE_ADDITIONAL_PARAMETERS_CREATE_V1 = "CREATE TABLE " + MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS + " ("
+ + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + " TEXT, "
+ + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_HTTPUPLOAD + " INTEGER DEFAULT 0, "
+ + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_TREATASDOWNLOADABLE_DECISION + " TEXT DEFAULT 'NOT_DECIDED', "
+ + "FOREIGN KEY(" + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + ") REFERENCES " + Message.TABLENAME + "(" + Message.UUID + ") ON DELETE CASCADE)";
+
+ private static final String TABLE_ADDITIONAL_PARAMETERS_CREATE = "CREATE TABLE " + MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS + " ("
+ + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + " TEXT, "
+ + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_HTTPUPLOAD + " INTEGER DEFAULT 0, "
+ + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_TREATASDOWNLOADABLE_DECISION + " TEXT DEFAULT 'NOT_DECIDED', "
+ + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_TYPE + " TEXT DEFAULT NULL, "
+ + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_SIZE + " INTEGER DEFAULT 0, "
+ + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_HEIGHT + " INTEGER DEFAULT 0, "
+ + 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_FILE_PATH + " TEXT DEFAULT NULL, "
+ + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_URL + " 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) {
+ String[] args = {uuid};
+ db.update(MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS, MessageDatabaseAccess.getAdditionalParametersContentValues(message), MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + "=?", args);
+ }
+
+ public static ContentValues getAdditionalParametersContentValues(Message message) {
+ ContentValues additionalParameters = new ContentValues();
+ additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID, message.getUuid());
+ additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_HTTPUPLOAD, message.isHttpUploaded() ? 1 : 0);
+ additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_TREATASDOWNLOADABLE_DECISION, message.treatAsDownloadable().name());
+ if (null != message.getFileParams()) {
+ FileParams fileParams = message.getFileParams();
+ additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_TYPE, fileParams.getMimeType());
+ 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_PATH, fileParams.getPath());
+ additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_URL, fileParams.getUrl());
+ if (null != fileParams.getFileStatus()) {
+ additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_STATUS, fileParams.getFileStatus().name());
+ }
+ }
+
+ return additionalParameters;
+ }
+
+ private static void populateMessageParametersFromCursor(Cursor cursor, Message message) {
+ boolean isHttpUploaded = CursorHelper.getInt(cursor, COLUMN_NAME_MSG_PARAMS_HTTPUPLOAD) == 1;
+ message.setHttpUploaded(isHttpUploaded);
+ String downloadable = CursorHelper.getString(cursor, COLUMN_NAME_MSG_PARAMS_TREATASDOWNLOADABLE_DECISION);
+ Message.Decision treatAsDownloadable = Message.Decision.NOT_DECIDED;
+ try {
+ treatAsDownloadable = Message.Decision.valueOf(downloadable);
+ } catch (IllegalArgumentException e) {
+ // Should only happen if the database is corrupted, but to be on the save side catch it here
+ Logging.e("db.msg", "Unknown Decision for treatAsDownloadable found: '" + downloadable + "'");
+ }
+
+ message.setTreatAsDownloadable(treatAsDownloadable);
+
+ if (message.hasFileAttached()) {
+ FileParams fileParams = new FileParams(message.getBody());
+ String fileType = CursorHelper.getString(cursor, COLUMN_NAME_MSG_PARAMS_FILE_TYPE);
+ fileParams.setMimeType(fileType);
+ String name = CursorHelper.getString(cursor, COLUMN_NAME_MSG_PARAMS_FILE_NAME);
+ fileParams.setName(name);
+ 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);
+ 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);
+ fileParams.setHeight(imageHeight);
+ int imageWidth = CursorHelper.getInt(cursor, COLUMN_NAME_MSG_PARAMS_FILE_WIDTH);
+ fileParams.setWidth(imageWidth);
+ String status = CursorHelper.getString(cursor, COLUMN_NAME_MSG_PARAMS_FILE_STATUS);
+ if (null != status && !status.isEmpty()) {
+ FileStatus fileStatus = FileStatus.UNDEFINED;
+ try {
+ fileStatus = FileStatus.valueOf(status);
+ } catch (IllegalArgumentException e) {
+ // Should only happen if the database is corrupted, but to be on the save side catch it here
+ Logging.e("db.msg", "Unknown FileStatus for fileParams.fileStatus found: '" + status + "'");
+ }
+ fileParams.setFileStatus(fileStatus);
+ }
+ message.setFileParams(fileParams);
+ }
+ }
+
+ public static void populateMessageParameters(SQLiteDatabase db, Message message) {
+ Cursor paramsCursor = db.query(MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS,
+ null, MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + "=?",
+ new String[] {message.getUuid()},
+ null, null, null, null);
+ paramsCursor.moveToNext();
+ MessageDatabaseAccess.populateMessageParametersFromCursor(paramsCursor, message);
+ paramsCursor.close();
+ }
+
+ public static void upgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ if (oldVersion < 1 && newVersion >= 1) {
+ Logging.d("db.upgrade.cplus", "Creating additional parameters table for messages.");
+ db.execSQL(MessageDatabaseAccess.TABLE_ADDITIONAL_PARAMETERS_CREATE_V1);
+ db.execSQL("INSERT INTO " + MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS + "(" + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + ") "
+ + " SELECT " + Message.UUID + " FROM " + Message.TABLENAME);
+ }
+ if (oldVersion < 3 && newVersion >= 3) {
+ Logging.d("db.upgrade.cplus", "Upgrade " + MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS);
+ String[] columnDefinitions = {
+ MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_TYPE + " TEXT DEFAULT NULL",
+ MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_SIZE + " INTEGER DEFAULT 0",
+ MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_HEIGHT + " INTEGER DEFAULT 0",
+ 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_FILE_PATH + " TEXT DEFAULT NULL",
+ };
+
+ addNewColumns(db, MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS, columnDefinitions);
+
+ Logging.d("db.upgrade.cplus", "Migrate file params from message body to database fields");
+ Cursor cursor = db.rawQuery("SELECT " + Message.UUID + ", " + Message.BODY + " FROM " + Message.TABLENAME + " WHERE " + Message.TYPE + "=? OR " + Message.TYPE + "=?", new String[] {String.valueOf(Message.TYPE_FILE), String.valueOf(Message.TYPE_IMAGE)});
+ while (cursor.moveToNext()) {
+ String uuid = CursorHelper.getString(cursor, Message.UUID);
+ String body = CursorHelper.getString(cursor, Message.BODY);
+ FileParams fileParams = FileParamsBodyToDatabaseFieldsMigration.getFileParams(body);
+ String newBody = fileParams.getUrl();
+ ContentValues values = new ContentValues();
+ values.put(Message.BODY, newBody);
+ db.update(Message.TABLENAME, values, Message.UUID + "=?", new String[] {uuid});
+
+ ContentValues parameterValues = new ContentValues();
+ 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());
+
+ db.update(MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS, parameterValues, MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + "=?", new String[] {uuid});
+ }
+ cursor.close();
+ }
+ }
+
+ public static void create(SQLiteDatabase db) {
+ // Create Conversations+ related tables
+ db.execSQL(MessageDatabaseAccess.TABLE_ADDITIONAL_PARAMETERS_CREATE);
+ }
+}
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
new file mode 100644
index 00000000..c0aa63c0
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/persistance/db/migrations/FileParamsBodyToDatabaseFieldsMigration.java
@@ -0,0 +1,101 @@
+package de.thedevstack.conversationsplus.persistance.db.migrations;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import de.thedevstack.conversationsplus.entities.FileParams;
+
+/**
+ * Created by steckbrief on 24.08.2016.
+ */
+public class FileParamsBodyToDatabaseFieldsMigration {
+
+ public static FileParams getFileParams(String body) {
+ FileParams params = getLegacyFileParams(body);
+ if (params != null) {
+ return params;
+ }
+ params = new FileParams();
+ if (body == null) {
+ return params;
+ }
+ String parts[] = body.split("\\|");
+ switch (parts.length) {
+ case 1:
+ try {
+ params.setSize(Long.parseLong(parts[0]));
+ } catch (NumberFormatException e) {
+ try {
+ URL url = new URL(parts[0]);
+ params.setUrl(url.toString());
+ } catch (MalformedURLException e1) {
+ }
+ }
+ break;
+ case 2:
+ case 4:
+ try {
+ URL url = new URL(parts[0]);
+ params.setUrl(url.toString());
+ } catch (MalformedURLException e1) {
+ }
+ try {
+ params.setSize(Long.parseLong(parts[1]));
+ } catch (NumberFormatException e) {
+ }
+ try {
+ params.setWidth(Integer.parseInt(parts[2]));
+ } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
+ }
+ try {
+ params.setHeight(Integer.parseInt(parts[3]));
+ } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
+ }
+ break;
+ case 3:
+ try {
+ params.setSize(Long.parseLong(parts[0]));
+ } catch (NumberFormatException e) {
+ }
+ try {
+ params.setWidth(Integer.parseInt(parts[1]));
+ } catch (NumberFormatException e) {
+ }
+ try {
+ params.setHeight(Integer.parseInt(parts[2]));
+ } catch (NumberFormatException e) {
+ }
+ break;
+ }
+ return params;
+ }
+
+ private static FileParams getLegacyFileParams(String body) {
+ FileParams params = new FileParams();
+ if (body == null) {
+ return params;
+ }
+ String parts[] = body.split(",");
+ if (parts.length == 3) {
+ try {
+ params.setSize(Long.parseLong(parts[0]));
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ try {
+ params.setWidth(Integer.parseInt(parts[1]));
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ try {
+ params.setHeight(Integer.parseInt(parts[2]));
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ return params;
+ } else {
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/services/ExportLogsService.java b/src/main/java/de/thedevstack/conversationsplus/services/ExportLogsService.java
index 18930d1c..0dcfa05e 100644
--- a/src/main/java/de/thedevstack/conversationsplus/services/ExportLogsService.java
+++ b/src/main/java/de/thedevstack/conversationsplus/services/ExportLogsService.java
@@ -103,7 +103,7 @@ public class ExportLogsService extends Service {
break;
}
if (jid != null) {
- String body = message.hasFileOnRemoteHost() ? message.getFileParams().url.toString() : message.getBody();
+ String body = message.hasFileOnRemoteHost() ? message.getFileParams().getUrl() : message.getBody();
bw.write(String.format(MESSAGE_STRING_FORMAT, date, jid,
body.replace("\\\n", "\\ \n").replace("\n", "\\ \n")));
}
diff --git a/src/main/java/de/thedevstack/conversationsplus/services/NotificationService.java b/src/main/java/de/thedevstack/conversationsplus/services/NotificationService.java
index a3142df4..f4388b21 100644
--- a/src/main/java/de/thedevstack/conversationsplus/services/NotificationService.java
+++ b/src/main/java/de/thedevstack/conversationsplus/services/NotificationService.java
@@ -356,7 +356,8 @@ public class NotificationService {
if (message.getType() != Message.TYPE_TEXT
&& message.getTransferable() == null
&& message.getEncryption() != Message.ENCRYPTION_PGP
- && message.getFileParams().height > 0) {
+ && message.getFileParams() != null
+ && message.getFileParams().getHeight() > 0) { // TODO Use FileParams.getMimeType()
return message;
}
}
diff --git a/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/delete/DeleteRemoteFile.java b/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/delete/DeleteRemoteFile.java
index 39a369ef..1549388e 100644
--- a/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/delete/DeleteRemoteFile.java
+++ b/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/delete/DeleteRemoteFile.java
@@ -3,10 +3,10 @@ package de.thedevstack.conversationsplus.services.filetransfer.http.delete;
import android.support.annotation.NonNull;
import de.thedevstack.conversationsplus.entities.Message;
-import de.thedevstack.conversationsplus.entities.RemoteFile;
+import de.thedevstack.conversationsplus.dto.RemoteFile;
/**
- * Created by steckbrief on 22.08.2016.
+ *
*/
public class DeleteRemoteFile extends RemoteFile {
private final Message message;
diff --git a/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/delete/DeleteRemoteFileService.java b/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/delete/DeleteRemoteFileService.java
index 2b26fd85..eb9f1b04 100644
--- a/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/delete/DeleteRemoteFileService.java
+++ b/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/delete/DeleteRemoteFileService.java
@@ -1,8 +1,13 @@
package de.thedevstack.conversationsplus.services.filetransfer.http.delete;
+import de.thedevstack.conversationsplus.ConversationsPlusApplication;
import de.thedevstack.conversationsplus.entities.Account;
import de.thedevstack.conversationsplus.entities.Message;
+import de.thedevstack.conversationsplus.enums.FileStatus;
+import de.thedevstack.conversationsplus.persistance.DatabaseBackend;
import de.thedevstack.conversationsplus.ui.listeners.SimpleUserDecisionCallback;
+import de.thedevstack.conversationsplus.utils.MessageUtil;
+import de.thedevstack.conversationsplus.utils.UiUpdateHelper;
import de.thedevstack.conversationsplus.utils.XmppSendUtil;
import de.thedevstack.conversationsplus.xmpp.filetransfer.http.FileTransferHttp;
import de.thedevstack.conversationsplus.xmpp.filetransfer.http.delete.FileTransferHttpDeleteSlotRequestPacketGenerator;
@@ -10,7 +15,7 @@ import de.thedevstack.conversationsplus.xmpp.jid.Jid;
import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket;
/**
- * Created by steckbrief on 21.08.2016.
+ *
*/
public class DeleteRemoteFileService implements SimpleUserDecisionCallback {
private Message message;
@@ -23,14 +28,14 @@ public class DeleteRemoteFileService implements SimpleUserDecisionCallback {
if (this.message.isHttpUploaded()) {
String path = this.message.getBody();
if (this.message.hasFileOnRemoteHost()) {
- path = this.message.getFileParams().url.toString();
+ path = this.message.getFileParams().getUrl();
}
DeleteRemoteFile remoteFile = new DeleteRemoteFile(path, this.message);
Account account = this.message.getConversation().getAccount();
Jid host = account.getXmppConnection().findDiscoItemByFeature(FileTransferHttp.NAMESPACE);
IqPacket request = FileTransferHttpDeleteSlotRequestPacketGenerator.generate(host, path);
-
+ MessageUtil.setAndSaveFileStatus(this.message, FileStatus.DELETING);
XmppSendUtil.sendIqPacket(account, request, new DeleteTokenReceived(remoteFile));
}
}
diff --git a/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/delete/DeleteTokenReceived.java b/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/delete/DeleteTokenReceived.java
index a8bef0ed..186454e4 100644
--- a/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/delete/DeleteTokenReceived.java
+++ b/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/delete/DeleteTokenReceived.java
@@ -7,7 +7,7 @@ import java.io.IOException;
import de.thedevstack.android.logcat.Logging;
import de.thedevstack.conversationsplus.entities.Account;
-import de.thedevstack.conversationsplus.entities.Message;
+import de.thedevstack.conversationsplus.enums.FileStatus;
import de.thedevstack.conversationsplus.http.HttpClient;
import de.thedevstack.conversationsplus.utils.MessageUtil;
import de.thedevstack.conversationsplus.xmpp.OnIqPacketReceived;
@@ -21,7 +21,7 @@ import okhttp3.Request;
import okhttp3.Response;
/**
- * Created by steckbrief on 21.08.2016.
+ *
*/
public class DeleteTokenReceived implements OnIqPacketReceived {
private static final String HEADER_NAME_DELETE_TOKEN = "X-FILETRANSFER-HTTP-DELETE-TOKEN";
@@ -47,19 +47,19 @@ public class DeleteTokenReceived implements OnIqPacketReceived {
@Override
public void onFailure(Call call, IOException e) {
Logging.e("filetransfer.http.delete", "Error while connecting to '" + call.request().url() + "': " + e.getMessage());
- MessageUtil.markMessage(remoteFile.getMessage(), Message.STATUS_REMOTE_FILE_DELETE_FAILED);
+ MessageUtil.setAndSaveFileStatus(remoteFile.getMessage(), FileStatus.DELETE_FAILED);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
- MessageUtil.markMessage(remoteFile.getMessage(), Message.STATUS_REMOTE_FILE_DELETED);
+ MessageUtil.setAndSaveFileStatus(remoteFile.getMessage(), FileStatus.DELETED);
Logging.i("filetransfer.http.delete", "Remote file successfully deleted '" + remoteFile.getPath() + "'");
} else {
String detailedMessage = response.body().string();
+ FileStatus fileStatus = FileStatus.DELETE_FAILED;
switch (response.code()) {
case 403:
- case 404:
case 500:
try {
JSONObject jsonObject = new JSONObject(detailedMessage);
@@ -68,16 +68,20 @@ public class DeleteTokenReceived implements OnIqPacketReceived {
Logging.e("filetransfer.http.delete", "Failed to get error message from expected json response: " + detailedMessage);
}
break;
+ case 404:
+ fileStatus = FileStatus.DELETED;
+ Logging.i("filetransfer.http.delete", "Failed to delete file - it was already deleted.");
+ break;
}
Logging.e("filetransfer.http.delete", "Could not delete remote file '" + remoteFile.getPath() + "'. Response Code: " + response.code() + ", details: " + detailedMessage);
- MessageUtil.markMessage(remoteFile.getMessage(), Message.STATUS_REMOTE_FILE_DELETE_FAILED);
+ MessageUtil.setAndSaveFileStatus(remoteFile.getMessage(), fileStatus);
}
}
});
} catch (XmppException e) {
Logging.e("filetransfer.http.delete", "Error while trying to get the delete token: " + e.getMessage());
- MessageUtil.markMessage(remoteFile.getMessage(), Message.STATUS_REMOTE_FILE_DELETE_FAILED);
+ MessageUtil.setAndSaveFileStatus(remoteFile.getMessage(), FileStatus.DELETE_FAILED);
}
}
}
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 fbe7186b..a373d584 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
@@ -6,7 +6,9 @@ import java.net.URL;
import de.thedevstack.android.logcat.Logging;
import de.thedevstack.conversationsplus.Config;
import de.thedevstack.conversationsplus.ConversationsPlusApplication;
+import de.thedevstack.conversationsplus.entities.FileParams;
import de.thedevstack.conversationsplus.entities.Message;
+import de.thedevstack.conversationsplus.enums.FileStatus;
import de.thedevstack.conversationsplus.services.filetransfer.FileTransferEntity;
import de.thedevstack.conversationsplus.services.filetransfer.FileTransferFailureReason;
import de.thedevstack.conversationsplus.utils.CryptoHelper;
@@ -24,7 +26,13 @@ public class HttpFileTransferEntity extends FileTransferEntity {
public HttpFileTransferEntity(Message message, boolean delayed) {
super(message);
this.getMessage().setHttpUploaded(true);
- this.getMessage().setNoDownloadable();
+ this.getMessage().setNoDownloadable(); // TODO Set rmeote file status to uploaded
+ FileParams fileParams = this.getMessage().getFileParams();
+ if (null == fileParams) {
+ fileParams = new 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) {
@@ -77,6 +85,7 @@ public class HttpFileTransferEntity extends FileTransferEntity {
getUrl = new URL(getUrl.toString() + "#" + CryptoHelper.bytesToHex(this.getKey()));
}
+ this.getMessage().getFileParams().setFileStatus(FileStatus.UPLOADED);
MessageUtil.updateFileParams(this.getMessage(), getUrl);
} catch (MalformedURLException e) {
Logging.e("httpupload", "Not a valid get url");
diff --git a/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/upload/HttpFileUploader.java b/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/upload/HttpFileUploader.java
index 318a8b8f..8cae599c 100644
--- a/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/upload/HttpFileUploader.java
+++ b/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/upload/HttpFileUploader.java
@@ -84,6 +84,7 @@ public class HttpFileUploader implements Runnable {
// Send the URL to the counterpart
Message message = this.entity.getMessage();
+ message.setBody(this.entity.getGetUrl());
message.setCounterpart(message.getConversation().getJid().toBareJid());
if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
XmppConnectionServiceAccessor.xmppConnectionService.getPgpEngine().encrypt(message, new HttpUploadedFileEncryptionUiCallback(this.entity));
diff --git a/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/upload/HttpUploadFileTransferService.java b/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/upload/HttpUploadFileTransferService.java
index f08e27f5..6b19cd5f 100644
--- a/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/upload/HttpUploadFileTransferService.java
+++ b/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/upload/HttpUploadFileTransferService.java
@@ -86,6 +86,7 @@ public class HttpUploadFileTransferService extends AbstractFileTransferService i
return null != message
&& null != message.getConversation()
&& null != message.getConversation().getAccount()
- && message.getConversation().getAccount().httpUploadAvailable(FileBackend.getFile(message, false).getSize());
+ && null != message.getFileParams()
+ && message.getConversation().getAccount().httpUploadAvailable(message.getFileParams().getSize());
}
}
diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/ConversationFragment.java b/src/main/java/de/thedevstack/conversationsplus/ui/ConversationFragment.java
index 56065ea4..0c2f2960 100644
--- a/src/main/java/de/thedevstack/conversationsplus/ui/ConversationFragment.java
+++ b/src/main/java/de/thedevstack/conversationsplus/ui/ConversationFragment.java
@@ -578,7 +578,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
deleteFile.setVisible(true);
deleteFile.setTitle(activity.getString(R.string.delete_x_file,UIHelper.getFileDescriptionString(activity, m)));
}
- if (m.isHttpUploaded() && (MessageUtil.isMessageSent(m) || m.getStatus() == Message.STATUS_REMOTE_FILE_DELETE_FAILED)) {
+ if (m.isHttpUploaded() && MessageUtil.isMessageSent(m)) {
MenuItem deleteRemoteFile = menu.findItem(R.id.msg_ctx_menu_delete_remote_file);
deleteRemoteFile.setVisible(true);
}
@@ -676,7 +676,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
url = message.getBody();
} else if (message.hasFileOnRemoteHost()) {
resId = R.string.file_url;
- url = message.getFileParams().url.toString();
+ url = message.getFileParams().getUrl();
} else {
url = message.getBody();
resId = R.string.file_url;
diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/adapter/ConversationAdapter.java b/src/main/java/de/thedevstack/conversationsplus/ui/adapter/ConversationAdapter.java
index e28774f2..98a9a96c 100644
--- a/src/main/java/de/thedevstack/conversationsplus/ui/adapter/ConversationAdapter.java
+++ b/src/main/java/de/thedevstack/conversationsplus/ui/adapter/ConversationAdapter.java
@@ -90,7 +90,7 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
convName.setTypeface(null, Typeface.NORMAL);
}
- if (message.getFileParams().width > 0
+ if ((null != message.getFileParams() && message.getFileParams().getWidth() > 0) // TODO: Use FileParams.getMimeType()
&& (message.getTransferable() == null
|| message.getTransferable().getStatus() != Transferable.STATUS_DELETED)) {
mLastMessage.setVisibility(View.GONE);
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 b2dca6b9..e93ddbfa 100644
--- a/src/main/java/de/thedevstack/conversationsplus/ui/adapter/MessageAdapter.java
+++ b/src/main/java/de/thedevstack/conversationsplus/ui/adapter/MessageAdapter.java
@@ -20,6 +20,7 @@ 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;
@@ -46,15 +47,17 @@ import de.thedevstack.conversationsplus.crypto.axolotl.XmppAxolotlSession;
import de.thedevstack.conversationsplus.entities.Account;
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.Message.FileParams;
import de.thedevstack.conversationsplus.entities.Transferable;
+import de.thedevstack.conversationsplus.enums.FileStatus;
import de.thedevstack.conversationsplus.persistance.FileBackend;
import de.thedevstack.conversationsplus.providers.ConversationsPlusFileProvider;
import de.thedevstack.conversationsplus.services.AvatarService;
import de.thedevstack.conversationsplus.ui.ConversationActivity;
import de.thedevstack.conversationsplus.utils.CryptoHelper;
import de.thedevstack.conversationsplus.utils.GeoHelper;
+import de.thedevstack.conversationsplus.utils.MessageUtil;
import de.thedevstack.conversationsplus.utils.UIHelper;
public class MessageAdapter extends ArrayAdapter<Message> {
@@ -138,13 +141,17 @@ public class MessageAdapter extends ArrayAdapter<Message> {
boolean multiReceived = message.getConversation().getMode() == Conversation.MODE_MULTI
&& message.getStatus() <= Message.STATUS_RECEIVED;
- if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE || message.getTransferable() != null) {
+ if (message.hasFileAttached() || message.getTransferable() != null) {
FileParams params = message.getFileParams();
- if (params.size > (1.5 * 1024 * 1024)) {
- filesize = params.size / (1024 * 1024)+ " MiB";
- } else if (params.size > 0) {
- filesize = params.size / 1024 + " KiB";
- }
+ 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";
+ }
+ }
+
if (message.getTransferable() != null && message.getTransferable().getStatus() == Transferable.STATUS_FAILED) {
error = true;
}
@@ -247,6 +254,25 @@ public class MessageAdapter extends ArrayAdapter<Message> {
viewHolder.time.setText(formatedTime);
}
}
+
+ if (message.hasFileAttached() && null != message.getFileParams() && null != viewHolder.remoteFileStatus) {
+ FileStatus fileStatus = message.getFileParams().getFileStatus();
+ if (fileStatus == FileStatus.DELETE_FAILED || fileStatus == FileStatus.DELETED || fileStatus == FileStatus.DELETING) {
+ viewHolder.remoteFileStatus.setVisibility(View.VISIBLE);
+ viewHolder.remoteFileStatus.setTypeface(null, Typeface.ITALIC);
+ switch (fileStatus) {
+ case DELETE_FAILED:
+ viewHolder.remoteFileStatus.setText(R.string.remote_filestatus_delete_failed);
+ break;
+ case DELETED:
+ viewHolder.remoteFileStatus.setText(R.string.remote_filestatus_delete_success);
+ break;
+ case DELETING:
+ viewHolder.remoteFileStatus.setText(R.string.remote_filestatus_delete_inprogress);
+ break;
+ }
+ }
+ }
}
private void displayInfoMessage(ViewHolder viewHolder, String text, boolean darkBackground) {
@@ -434,9 +460,10 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}
viewHolder.messageBody.setVisibility(View.GONE);
viewHolder.image.setVisibility(View.VISIBLE);
- FileParams params = message.getFileParams();
//TODO: Check what value add the following lines have (compared with setting height/width in XmppActivity.loadBitmap from thumbnail after thumbnail is created)
- /*double target = metrics.density * 288;
+ /*
+ FileParams params = message.getFileParams();
+ double target = metrics.density * 288;
int scalledW;
int scalledH;
if (params.width <= params.height) {
@@ -491,6 +518,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
.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(
@@ -585,7 +613,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
} 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().width > 0) {
+ if (message.getFileParams() != null && message.getFileParams().getWidth() > 0) { // TODO Use FileParams.getMimeType()
displayImageMessage(viewHolder,message);
} else {
displayOpenableMessage(viewHolder, message);
@@ -612,12 +640,20 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}
} else if (message.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
displayDecryptionFailed(viewHolder,darkBackground);
- } else {
+ } 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);
+ } else {
+ displayOpenableMessage(viewHolder, message);
+ }
+ } else {
if (GeoHelper.isGeoUri(message.getBody())) {
displayLocationMessage(viewHolder,message);
- } else if (message.treatAsDownloadable() == Message.Decision.MUST) {
+ } else if (MessageUtil.needsDownload(message)) {
try {
- URL url = new URL(message.getBody());
+ URL url = new URL(message.getFileParams().getUrl());
displayDownloadableMessage(viewHolder,
message,
activity.getString(R.string.check_x_filesize_on_host,
@@ -724,7 +760,8 @@ public class MessageAdapter extends ArrayAdapter<Message> {
protected ImageView contact_picture;
protected TextView status_message;
protected TextView encryption;
- }
+ public TextView remoteFileStatus;
+ }
class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
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 e1c30a56..4ec8e3eb 100644
--- a/src/main/java/de/thedevstack/conversationsplus/ui/dialogs/MessageDetailsDialog.java
+++ b/src/main/java/de/thedevstack/conversationsplus/ui/dialogs/MessageDetailsDialog.java
@@ -11,6 +11,7 @@ import de.thedevstack.android.logcat.Logging;
import de.thedevstack.conversationsplus.ConversationsPlusColors;
import de.thedevstack.conversationsplus.R;
import de.thedevstack.conversationsplus.entities.Conversation;
+import de.thedevstack.conversationsplus.entities.FileParams;
import de.thedevstack.conversationsplus.entities.Message;
import de.thedevstack.conversationsplus.utils.UIHelper;
import de.thedevstack.conversationsplus.utils.ui.TextViewUtil;
@@ -59,10 +60,10 @@ public class MessageDetailsDialog extends AbstractAlertDialog {
Logging.d("messagedetailsfile", "File is stored in path: " + message.getRelativeFilePath());
view.findViewById(R.id.dlgMsgDetFileTable).setVisibility(View.VISIBLE);
if (null != message.getFileParams()) {
- Message.FileParams params = message.getFileParams();
- TextViewUtil.setText(view, R.id.dlgMsgDetFileSize, UIHelper.getHumanReadableFileSize(params.size));
+ FileParams params = message.getFileParams();
+ TextViewUtil.setText(view, R.id.dlgMsgDetFileSize, UIHelper.getHumanReadableFileSize(params.getSize()));
+ TextViewUtil.setText(view, R.id.dlgMsgDetFileMimeType, params.getMimeType());
}
- TextViewUtil.setText(view, R.id.dlgMsgDetFileMimeType, message.getMimeType());
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/ui/listeners/ResizePictureUserDecisionListener.java b/src/main/java/de/thedevstack/conversationsplus/ui/listeners/ResizePictureUserDecisionListener.java
index b7340ce0..1574bb85 100644
--- a/src/main/java/de/thedevstack/conversationsplus/ui/listeners/ResizePictureUserDecisionListener.java
+++ b/src/main/java/de/thedevstack/conversationsplus/ui/listeners/ResizePictureUserDecisionListener.java
@@ -14,6 +14,8 @@ import de.thedevstack.android.logcat.Logging;
import de.thedevstack.conversationsplus.ConversationsPlusApplication;
import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
import de.thedevstack.conversationsplus.crypto.PgpEngine;
+import de.thedevstack.conversationsplus.entities.FileParams;
+import de.thedevstack.conversationsplus.enums.FileStatus;
import de.thedevstack.conversationsplus.enums.UserDecision;
import de.thedevstack.conversationsplus.exceptions.UiException;
import de.thedevstack.conversationsplus.utils.FileUtils;
@@ -27,6 +29,7 @@ import de.thedevstack.conversationsplus.persistance.FileBackend;
import de.thedevstack.conversationsplus.services.XmppConnectionService;
import de.thedevstack.conversationsplus.ui.UiCallback;
import de.thedevstack.conversationsplus.ui.XmppActivity;
+import de.thedevstack.conversationsplus.utils.MimeUtils;
import de.thedevstack.conversationsplus.utils.StreamUtil;
/**
@@ -96,20 +99,18 @@ public class ResizePictureUserDecisionListener implements UserDecisionListener {
@Override
public void onYes() {
this.showPrepareFileToast();
- final Message message;
- if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
- message = new Message(conversation, "", Message.ENCRYPTION_DECRYPTED);
- } else {
- message = new Message(conversation, "", conversation.getNextEncryption());
- }
- message.setCounterpart(conversation.getNextCounterpart());
- message.setType(Message.TYPE_IMAGE);
+ final Message message = createMessage();
ConversationsPlusApplication.executeFileAdding(new OnYesRunnable(message, uri));
}
@Override
public void onNo() {
this.showPrepareFileToast();
+ final Message message = createMessage();
+ ConversationsPlusApplication.executeFileAdding(new OnNoRunnable(message, uri));
+ }
+
+ protected Message createMessage() {
final Message message;
if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
message = new Message(conversation, "", Message.ENCRYPTION_DECRYPTED);
@@ -117,8 +118,11 @@ public class ResizePictureUserDecisionListener implements UserDecisionListener {
message = new Message(conversation, "", conversation.getNextEncryption());
}
message.setCounterpart(conversation.getNextCounterpart());
- message.setType(Message.TYPE_IMAGE);
- ConversationsPlusApplication.executeFileAdding(new OnNoRunnable(message, uri));
+ //message.setType(Message.TYPE_IMAGE);
+ message.setFileParams(new FileParams());
+ message.getFileParams().setFileStatus(FileStatus.NEEDS_UPLOAD);
+
+ return message;
}
@Override
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/ConversationUtil.java b/src/main/java/de/thedevstack/conversationsplus/utils/ConversationUtil.java
index 77c2c728..55fd1b8e 100644
--- a/src/main/java/de/thedevstack/conversationsplus/utils/ConversationUtil.java
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/ConversationUtil.java
@@ -6,6 +6,7 @@ import de.thedevstack.conversationsplus.ConversationsPlusApplication;
import de.thedevstack.conversationsplus.crypto.PgpEngine;
import de.thedevstack.conversationsplus.entities.Conversation;
import de.thedevstack.conversationsplus.entities.Message;
+import de.thedevstack.conversationsplus.enums.FileStatus;
import de.thedevstack.conversationsplus.exceptions.FileCopyException;
import de.thedevstack.conversationsplus.persistance.FileBackend;
import de.thedevstack.conversationsplus.ui.UiCallback;
@@ -43,10 +44,12 @@ public class ConversationUtil {
message = new Message(conversation, "", conversation.getNextEncryption());
}
message.setCounterpart(conversation.getNextCounterpart());
- message.setType(Message.TYPE_FILE);
+ //message.setType(Message.TYPE_FILE);
+ message.getFileParams().setFileStatus(FileStatus.NEEDS_UPLOAD);
String path = FileUtils.getPath(uri);
if (path != null) {
- message.setRelativeFilePath(path);
+ message.getFileParams().setPath(path);
+ message.setRelativeFilePath(path); // TODO: Remove when everything is moved to fileparams
MessageUtil.updateFileParams(message);
if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
PgpEngine.getInstance().encrypt(message, callback);
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/FileUtils.java b/src/main/java/de/thedevstack/conversationsplus/utils/FileUtils.java
index 3ee15f70..ce289968 100644
--- a/src/main/java/de/thedevstack/conversationsplus/utils/FileUtils.java
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/FileUtils.java
@@ -16,6 +16,7 @@ import java.io.File;
import java.util.List;
import de.thedevstack.conversationsplus.ConversationsPlusApplication;
+import de.thedevstack.conversationsplus.entities.Transferable;
public final class FileUtils {
@@ -160,6 +161,27 @@ public final class FileUtils {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
+ public static String getRelevantExtension(String path) {
+ if (path == null || path.isEmpty()) {
+ return null;
+ }
+
+ String filename = path.substring(path.lastIndexOf('/') + 1).toLowerCase();
+
+ final String lastPart = FileUtils.getLastExtension(filename);
+
+ if (!lastPart.isEmpty()) {
+ // we want the real file extension, not the crypto one
+ final String secondToLastPart = FileUtils.getSecondToLastExtension(filename);
+ if (!secondToLastPart.isEmpty() && Transferable.VALID_CRYPTO_EXTENSIONS.contains(lastPart)) {
+ return secondToLastPart;
+ } else {
+ return lastPart;
+ }
+ }
+ return null;
+ }
+
/**
* @param filename The filename to extract extension from
* @return last extension or empty string
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/ImageUtil.java b/src/main/java/de/thedevstack/conversationsplus/utils/ImageUtil.java
index bf79d7e0..0b8ace95 100644
--- a/src/main/java/de/thedevstack/conversationsplus/utils/ImageUtil.java
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/ImageUtil.java
@@ -337,20 +337,28 @@ public final class ImageUtil {
public static int calcSampleSize(Uri image, int size) throws FileNotFoundException {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
- BitmapFactory.decodeStream(StreamUtil.openInputStreamFromContentResolver(image), null, options);
- return calcSampleSize(options, size);
+ Bitmap bmp = BitmapFactory.decodeStream(StreamUtil.openInputStreamFromContentResolver(image), null, options);
+ int height = options.outHeight;
+ int width = options.outWidth;
+ if (null != bmp) {
+ bmp.recycle();
+ }
+ return calcSampleSize(width, height, size);
}
public static int calcSampleSize(File image, int size) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
- BitmapFactory.decodeFile(image.getAbsolutePath(), options);
- return calcSampleSize(options, size);
- }
-
- public static int calcSampleSize(BitmapFactory.Options options, int size) {
+ Bitmap bmp = BitmapFactory.decodeFile(image.getAbsolutePath(), options);
int height = options.outHeight;
int width = options.outWidth;
+ if (null != bmp) {
+ bmp.recycle();
+ }
+ return calcSampleSize(width, height, size);
+ }
+
+ private static int calcSampleSize(int width, int height, int size) {
int inSampleSize = 1;
if (height > size || width > size) {
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java b/src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java
index 626b1bf3..dc9607f0 100644
--- a/src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java
@@ -1,5 +1,6 @@
package de.thedevstack.conversationsplus.utils;
+import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import java.net.URL;
@@ -9,7 +10,9 @@ import java.util.regex.Pattern;
import de.thedevstack.conversationsplus.ConversationsPlusApplication;
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.enums.FileStatus;
import de.thedevstack.conversationsplus.persistance.DatabaseBackend;
import de.thedevstack.conversationsplus.persistance.FileBackend;
@@ -18,6 +21,13 @@ import de.thedevstack.conversationsplus.persistance.FileBackend;
*/
public final class MessageUtil {
+ 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))
+ && message.treatAsDownloadable() != Message.Decision.NEVER;
+ }
+
public static boolean isMessageSent(Message message) {
switch (message.getStatus()) {
case Message.STATUS_SEND:
@@ -29,6 +39,12 @@ public final class MessageUtil {
}
}
+ public static void setAndSaveFileStatus(Message message, FileStatus fileStatus) {
+ message.getFileParams().setFileStatus(fileStatus);
+ DatabaseBackend.getInstance().updateMessage(message);
+ UiUpdateHelper.updateConversationUi();
+ }
+
public static boolean markMessage(Conversation conversation, String uuid, int status) {
if (uuid == null) {
return false;
@@ -76,7 +92,7 @@ public final class MessageUtil {
public static void updateMessageWithImageDetails(Message message, String filePath, long size, int imageWidth, int imageHeight) {
message.setRelativeFilePath(filePath);
- MessageUtil.updateMessageBodyWithImageParams(message, size, imageWidth, imageHeight);
+ MessageUtil.updateMessageWithFileParams(message, null, size, imageWidth, imageHeight);
}
public static void updateFileParams(Message message) {
@@ -90,42 +106,37 @@ public final class MessageUtil {
if (message.getType() == Message.TYPE_IMAGE || file.getMimeType().startsWith("image/")) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
- BitmapFactory.decodeFile(file.getAbsolutePath(), options);
+ Bitmap bmp = BitmapFactory.decodeFile(file.getAbsolutePath(), options);
imageHeight = options.outHeight;
imageWidth = options.outWidth;
+ if (null != bmp) {
+ bmp.recycle();
+ }
}
- MessageUtil.updateMessageBodyWithFileParams(message, url, file.getSize(), imageWidth, imageHeight);
- }
-
- private static void updateMessageBodyWithFileParams(Message message, URL url, long fileSize, int imageWidth, int imageHeight) {
- message.setBody(MessageUtil.getMessageBodyWithImageParams(url, fileSize, imageWidth, imageHeight));
- }
-
- private static void updateMessageBodyWithImageParams(Message message, long size, int imageWidth, int imageHeight) {
- MessageUtil.updateMessageBodyWithImageParams(message, null, size, imageWidth, imageHeight);
+ MessageUtil.updateMessageWithFileParams(message, url, file.getSize(), imageWidth, imageHeight);
}
- private static void updateMessageBodyWithImageParams(Message message, URL url, long size, int imageWidth, int imageHeight) {
- message.setBody(MessageUtil.getMessageBodyWithImageParams(url, size, imageWidth, imageHeight));
- }
-
- private static String getMessageBodyWithImageParams(URL url, long size, int imageWidth, int imageHeight) {
- StringBuilder sb = new StringBuilder();
+ private static void updateMessageWithFileParams(Message message, URL url, long size, int imageWidth, int imageHeight) {
+ FileParams fileParams = message.getFileParams();
+ if (null == fileParams) {
+ fileParams = new FileParams();
+ }
+ fileParams.setSize(size);
if (null != url) {
- sb.append(url.toString());
- sb.append('|');
+ fileParams.setUrl(url.toString());
}
- sb.append(size);
if (-1 < imageWidth) {
- sb.append('|');
- sb.append(imageWidth);
+ fileParams.setWidth(imageWidth);
}
if (-1 < imageHeight) {
- sb.append('|');
- sb.append(imageHeight);
+ fileParams.setHeight(imageHeight);
+ }
+ String relativeFilePathFromMessage = message.getRelativeFilePath();
+ if (null != relativeFilePathFromMessage && relativeFilePathFromMessage.startsWith("/")) {
+ fileParams.setPath(relativeFilePathFromMessage);
}
- return sb.toString();
+ message.setFileParams(fileParams);
}
private MessageUtil() {
diff --git a/src/main/res/layout/message_sent.xml b/src/main/res/layout/message_sent.xml
index 0edbab51..30003309 100644
--- a/src/main/res/layout/message_sent.xml
+++ b/src/main/res/layout/message_sent.xml
@@ -103,6 +103,18 @@
android:gravity="center_vertical"
android:src="@drawable/ic_received_indicator" />
</LinearLayout>
+
+ <TextView
+ android:id="@+id/remote_file_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right"
+ android:gravity="right"
+ android:text=""
+ android:textColor="@color/secondaryText"
+ android:textSize="?attr/TextSizeInfo"
+ android:visibility="gone"
+ android:textStyle="italic"/>
</LinearLayout>
</LinearLayout>
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index 8136a84f..e356a947 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -676,4 +676,7 @@
<string name="share_uri_with">Share URI with…</string>
<string name="delete_remote_file_x">Delete remote file</string>
<string name="cplus_are_you_sure">Are you sure?</string>
+ <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>
</resources>