Implements FS#245: Implement FiletransferHttp (upload and delete), some minor bug fixes including to fail a JingleTransfer in case criterias are not met

This commit is contained in:
steckbrief 2017-07-31 08:44:32 +02:00
parent 2d41916750
commit 11e2b1accd
30 changed files with 278 additions and 207 deletions

View file

@ -73,7 +73,7 @@ ext {
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
buildToolsVersion '23.0.2'
defaultConfig {
minSdkVersion 14

View file

@ -1,6 +1,6 @@
#Sat Apr 09 13:15:52 CEST 2016
#Wed May 31 19:49:33 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=http\://services.gradle.org/distributions/gradle-2.12-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-2.12-all.zip

BIN
libs/3rdParty/picasso-2.5.2.jar vendored Normal file

Binary file not shown.

View file

@ -11,7 +11,7 @@ apply plugin: 'android-library'
android {
compileSdkVersion 19
buildToolsVersion "19.1"
buildToolsVersion '19.1'
defaultConfig {
minSdkVersion 7
targetSdkVersion 19

View file

@ -2,7 +2,7 @@ apply plugin: 'com.android.library'
android {
compileSdkVersion 21
buildToolsVersion "21.1.2"
buildToolsVersion '21.1.2'
defaultConfig {
minSdkVersion 7

View file

@ -2,7 +2,7 @@ apply plugin: 'com.android.library'
android {
compileSdkVersion 21
buildToolsVersion "21.1.2"
buildToolsVersion '21.1.2'
defaultConfig {
minSdkVersion 8

View file

@ -2,7 +2,7 @@ apply plugin: 'com.android.library'
android {
compileSdkVersion 22
buildToolsVersion "22.0.1"
buildToolsVersion '22.0.1'
defaultConfig {
minSdkVersion 14

View file

@ -16,5 +16,5 @@ public enum FileStatus {
DELETING,
NOT_FOUND,
DOWNLOADING,
CHECKING_FILE_SIZE;
CHECKING_FILE_SIZE, UPLOAD_FAILED;
}

View file

@ -23,6 +23,7 @@ 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.FileUtils;
import de.thedevstack.conversationsplus.utils.StreamUtil;
import de.thedevstack.conversationsplus.Config;
import de.thedevstack.conversationsplus.R;
@ -44,10 +45,25 @@ public class FileBackend {
if (null == INSTANCE) {
INSTANCE = new FileBackend();
}
INSTANCE.checkIfDirectoriesExistAndCreateIfNot();
INSTANCE.initFileObservers();
INSTANCE.createNoMedia();
}
private void checkIfDirectoriesExistAndCreateIfNot() {
this.checkIfDirectoryExistsAndCreateIfNot(FileBackend.getPrivateFileDirectoryPath());
this.checkIfDirectoryExistsAndCreateIfNot(FileBackend.getConversationsFileDirectory());
this.checkIfDirectoryExistsAndCreateIfNot(FileBackend.getConversationsImageDirectory());
this.checkIfDirectoryExistsAndCreateIfNot(FileBackend.getPrivateImageDirectoryPath());
}
private void checkIfDirectoryExistsAndCreateIfNot(String directoryPath) {
File directory = new File(directoryPath);
if (!directory.exists()) {
directory.mkdirs();
}
}
private void initFileObservers() {
this.privateFilesDirectoryObserver = new FileDeletionObserver(FileBackend.getPrivateFileDirectoryPath());
this.privateFilesImageDirectoryObserver = new FileDeletionObserver(FileBackend.getConversationsFileDirectory());
@ -72,6 +88,7 @@ public class FileBackend {
}
public static void onFileTransferFolderChanged() {
INSTANCE.checkIfDirectoryExistsAndCreateIfNot(FileBackend.getConversationsFileDirectory());
INSTANCE.conversationsFilesDirectoryObserver.stopWatching();
INSTANCE.conversationsFilesDirectoryObserver = new FileDeletionObserver(FileBackend.getConversationsFileDirectory());
INSTANCE.conversationsFilesDirectoryObserver.startWatching();
@ -79,6 +96,7 @@ public class FileBackend {
}
public static void onImageTransferFolderChanged() {
INSTANCE.checkIfDirectoryExistsAndCreateIfNot(FileBackend.getConversationsImageDirectory());
INSTANCE.conversationsImagesDirectoryObserver.stopWatching();
INSTANCE.conversationsImagesDirectoryObserver = new FileDeletionObserver(FileBackend.getConversationsImageDirectory());
INSTANCE.conversationsImagesDirectoryObserver.startWatching();
@ -107,7 +125,7 @@ public class FileBackend {
}
public static DownloadableFile getFile(Message message, boolean decrypted) {
DownloadableFile downloadableFile = new DownloadableFile(getFilePath(message, decrypted));
DownloadableFile downloadableFile = new DownloadableFile(getFilePath(message, decrypted, null));
FileParams fileParams = message.getFileParams();
if (null != fileParams) {
if (null != fileParams.getKey()) {
@ -122,10 +140,7 @@ public class FileBackend {
}
protected static String getFilePath(Message message, String extension) {
String path = FileBackend.getFilePath(message, true);
if (!path.endsWith(extension)) {
path += "." + extension;
}
String path = FileBackend.getFilePath(message, true, extension);
return path;
}
@ -137,7 +152,7 @@ public class FileBackend {
return getFilePath(message, extension);
}
protected static String getFilePath(Message message, boolean decrypted) {
protected static String getFilePath(Message message, boolean decrypted, String fallbackExtension) {
final boolean encrypted = !decrypted
&& (message.getEncryption() == Message.ENCRYPTION_PGP
|| message.getEncryption() == Message.ENCRYPTION_DECRYPTED);
@ -157,7 +172,11 @@ public class FileBackend {
path = getConversationsFileDirectory() + path;
}
String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mime);
path += "." + extension;
if (null != extension) {
path += "." + extension;
} else if (null != fallbackExtension) {
path += "." +fallbackExtension;
}
fileParams.setPath(path);
}
@ -218,9 +237,11 @@ public class FileBackend {
}
public static DownloadableFile compressImageAndCopyToPrivateStorage(Message message, Bitmap scaledBitmap) throws FileCopyException {
String path = getFilePath(message, "jpg");
message.getFileParams().setPath(path);
message.setRelativeFilePath(path); // TODO: Remove
if (null == message.getFileParams() || null == message.getFileParams().getPath()) {
String path = getFilePath(message, "jpg");
message.setRelativeFilePath(path); // TODO: Remove
}
DownloadableFile file = getFile(message);
file.getParentFile().mkdirs();
OutputStream os = null;

View file

@ -8,6 +8,7 @@ import java.util.TreeSet;
import de.thedevstack.android.logcat.Logging;
import de.thedevstack.conversationsplus.Config;
import de.thedevstack.conversationsplus.entities.Message;
import de.thedevstack.conversationsplus.enums.FileStatus;
import de.thedevstack.conversationsplus.services.FileTransferService;
import de.thedevstack.conversationsplus.services.filetransfer.http.upload.HttpUploadFileTransferEntity;
import de.thedevstack.conversationsplus.utils.MessageUtil;
@ -124,6 +125,7 @@ public class FileTransferManager implements FileTransferStatusListener {
}
}
if (!retransferStarted) {
entity.getMessage().getFileParams().setFileStatus(FileStatus.UPLOAD_FAILED);
MessageUtil.markMessage(entity.getMessage(), Message.STATUS_SEND_FAILED);
}
}

View file

@ -1,16 +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;
import de.thedevstack.conversationsplus.xmpp.filetransfer.http.delete.FileTransferHttpDeleteRequestPacketGenerator;
import de.thedevstack.conversationsplus.xmpp.jid.Jid;
import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket;
@ -36,9 +33,9 @@ public class DeleteRemoteFileService implements SimpleUserDecisionCallback {
Account account = this.message.getConversation().getAccount();
Jid host = account.getXmppConnection().findDiscoItemByFeature(FileTransferHttp.NAMESPACE);
if (null != host) {
IqPacket request = FileTransferHttpDeleteSlotRequestPacketGenerator.generate(host, path);
IqPacket request = FileTransferHttpDeleteRequestPacketGenerator.generate(host, path);
MessageUtil.setAndSaveFileStatus(this.message, FileStatus.DELETING);
XmppSendUtil.sendIqPacket(account, request, new DeleteTokenReceived(remoteFile));
XmppSendUtil.sendIqPacket(account, request, new DeletedIqPacketReceived(remoteFile));
}
}
}

View file

@ -1,97 +0,0 @@
package de.thedevstack.conversationsplus.services.filetransfer.http.delete;
import android.widget.Toast;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import de.thedevstack.android.logcat.Logging;
import de.thedevstack.conversationsplus.R;
import de.thedevstack.conversationsplus.entities.Account;
import de.thedevstack.conversationsplus.enums.FileStatus;
import de.thedevstack.conversationsplus.http.HttpClient;
import de.thedevstack.conversationsplus.utils.MessageUtil;
import de.thedevstack.conversationsplus.utils.ui.ConversationsPlusToast;
import de.thedevstack.conversationsplus.xmpp.OnIqPacketReceived;
import de.thedevstack.conversationsplus.xmpp.exceptions.ServiceUnavailableException;
import de.thedevstack.conversationsplus.xmpp.exceptions.XmppException;
import de.thedevstack.conversationsplus.xmpp.filetransfer.http.delete.DeleteSlotPacketParser;
import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
/**
*
*/
public class DeleteTokenReceived implements OnIqPacketReceived {
private static final String HEADER_NAME_DELETE_TOKEN = "X-FILETRANSFER-HTTP-DELETE-TOKEN";
private final DeleteRemoteFile remoteFile;
public DeleteTokenReceived(DeleteRemoteFile remoteFile) {
this.remoteFile = remoteFile;
}
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
try {
String url = this.remoteFile.getPath();
String deleteToken = DeleteSlotPacketParser.parseDeleteToken(packet);
Logging.d("filetransfer.http.delete", "Got delete token '" + deleteToken + "' for remote file '" + remoteFile.getPath() + "'");
OkHttpClient client = HttpClient.getOkHttpClient(true);
Request request = new Request.Builder()
.url(url)
.addHeader(HEADER_NAME_DELETE_TOKEN, deleteToken)
.delete()
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Logging.e("filetransfer.http.delete", "Error while connecting to '" + call.request().url() + "': " + e.getMessage());
MessageUtil.setAndSaveFileStatus(remoteFile.getMessage(), FileStatus.DELETE_FAILED);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
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 500:
try {
JSONObject jsonObject = new JSONObject(detailedMessage);
detailedMessage = jsonObject.getString("msg");
} catch (JSONException e) {
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.setAndSaveFileStatus(remoteFile.getMessage(), fileStatus);
}
}
});
} catch (XmppException e) {
Logging.e("filetransfer.http.delete", "Error while trying to get the delete token: " + e.getMessage());
int messageResId = R.string.cplus_remote_file_delete_failed;
if (e instanceof ServiceUnavailableException) {
messageResId = R.string.cplus_remote_file_delete_service_unavailable;
}
ConversationsPlusToast.makeErrorToast(messageResId, Toast.LENGTH_LONG);
MessageUtil.setAndSaveFileStatus(remoteFile.getMessage(), FileStatus.DELETE_FAILED);
}
}
}

View file

@ -0,0 +1,47 @@
package de.thedevstack.conversationsplus.services.filetransfer.http.delete;
import android.widget.Toast;
import de.thedevstack.android.logcat.Logging;
import de.thedevstack.conversationsplus.R;
import de.thedevstack.conversationsplus.entities.Account;
import de.thedevstack.conversationsplus.enums.FileStatus;
import de.thedevstack.conversationsplus.utils.MessageUtil;
import de.thedevstack.conversationsplus.utils.ui.ConversationsPlusToast;
import de.thedevstack.conversationsplus.xmpp.OnIqPacketReceived;
import de.thedevstack.conversationsplus.xmpp.exceptions.ServiceUnavailableException;
import de.thedevstack.conversationsplus.xmpp.exceptions.XmppException;
import de.thedevstack.conversationsplus.xmpp.filetransfer.http.delete.DeletedPacketParser;
import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket;
/**
*
*/
public class DeletedIqPacketReceived implements OnIqPacketReceived {
private final DeleteRemoteFile remoteFile;
public DeletedIqPacketReceived(DeleteRemoteFile remoteFile) {
this.remoteFile = remoteFile;
}
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
try {
boolean fileIsDeleted = DeletedPacketParser.parseDeleteToken(packet);
if (fileIsDeleted) {
MessageUtil.setAndSaveFileStatus(remoteFile.getMessage(), FileStatus.DELETED);
Logging.i("filetransfer.http.delete", "Remote file successfully deleted '" + remoteFile.getPath() + "'");
} else {
Logging.e("filetransfer.http.delete", "Unexpectedly failed to delete remote file.");
}
} catch (XmppException e) {
Logging.e("filetransfer.http.delete", "Error while trying to delete remote file: " + e.getMessage());
int messageResId = R.string.cplus_remote_file_delete_failed;
if (e instanceof ServiceUnavailableException) {
messageResId = R.string.cplus_remote_file_delete_service_unavailable;
}
ConversationsPlusToast.makeErrorToast(messageResId, Toast.LENGTH_LONG);
MessageUtil.setAndSaveFileStatus(remoteFile.getMessage(), FileStatus.DELETE_FAILED);
}
}
}

View file

@ -15,10 +15,7 @@ import de.thedevstack.conversationsplus.services.filetransfer.AbstractFileTransf
import de.thedevstack.conversationsplus.utils.AccountUtil;
import de.thedevstack.conversationsplus.utils.MessageUtil;
import de.thedevstack.conversationsplus.utils.XmppSendUtil;
import de.thedevstack.conversationsplus.xmpp.filetransfer.http.FileTransferHttp;
import de.thedevstack.conversationsplus.xmpp.filetransfer.http.upload.HttpUpload;
import de.thedevstack.conversationsplus.xmpp.filetransfer.http.upload.HttpUploadRequestSlotPacketGenerator;
import de.thedevstack.conversationsplus.xmpp.jid.Jid;
import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket;
/**
@ -63,29 +60,18 @@ public class HttpUploadFileTransferService extends AbstractFileTransferService i
file.setExpectedSize(inputStreamAndExpectedSize.second);
Logging.d("httpupload", "Requesting upload slot for file upload");
Jid host = this.getHost(account);
if (null != host) {
IqPacket request = HttpUploadRequestSlotPacketGenerator.generate(host, file.getName(), file.getSize(), file.getMimeType());
XmppSendUtil.sendIqPacket(account, request, new HttpUploadSlotRequestReceived(entity));
MessageUtil.markMessage(message, Message.STATUS_UNSEND);
IqPacket request = HttpUploadRequestSlotPacketGenerator.generate(account, message.getContact().getJid(), file.getName(), file.getSize(), file.getMimeType());
XmppSendUtil.sendIqPacket(account, request, new HttpUploadSlotRequestReceived(entity));
MessageUtil.markMessage(message, Message.STATUS_UNSEND);
Logging.d("httpupload", "Upload slot for file upload requested");
started = true;
}
Logging.d("httpupload", "Upload slot for file upload requested");
started = true;
} catch (FileNotFoundException e) {
Logging.e("httpupload", "Could not find file, exception message: " + e.getMessage());
}
return started;
}
private Jid getHost(Account account) {
Jid host = account.getXmppConnection().findDiscoItemByFeature(FileTransferHttp.NAMESPACE);
if (null == host) {
host = account.getXmppConnection().findDiscoItemByFeature(HttpUpload.NAMESPACE);
}
return host;
}
/**
* Checks whether a message can be sent using this service or not.
*
@ -99,6 +85,7 @@ public class HttpUploadFileTransferService extends AbstractFileTransferService i
&& null != message.getConversation().getAccount()
&& null != message.getFileParams()
&& message.needsUploading()
&& AccountUtil.isHttpUploadAvailable(message.getConversation().getAccount(), message.getFileParams().getSize());
&& (AccountUtil.isHttpUploadAvailable(message.getConversation().getAccount(), message.getFileParams().getSize())
|| AccountUtil.isFileTransferHttpAvailable(message.getConversation().getAccount()));
}
}

View file

@ -24,6 +24,7 @@ import de.thedevstack.conversationsplus.ConversationsPlusColors;
import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
import de.thedevstack.conversationsplus.ui.listeners.ShowResourcesListDialogListener;
import de.thedevstack.conversationsplus.utils.ImageUtil;
import de.thedevstack.conversationsplus.utils.MessageUtil;
import de.tzur.conversations.Settings;
import de.thedevstack.conversationsplus.R;
import de.thedevstack.conversationsplus.entities.Account;
@ -108,10 +109,7 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
imagePreview.setVisibility(View.GONE);
CharSequence msgText = preview.first;
String msgPrefix = null;
if (message.getStatus() == Message.STATUS_SEND
|| message.getStatus() == Message.STATUS_SEND_DISPLAYED
|| message.getStatus() == Message.STATUS_SEND_FAILED
|| message.getStatus() == Message.STATUS_SEND_RECEIVED) {
if (MessageUtil.isMessageSent(message)) {
msgPrefix = activity.getString(R.string.cplus_me);
} else if (conversation.getMode() == Conversation.MODE_MULTI) {
msgPrefix = UIHelper.getMessageDisplayName(message);

View file

@ -496,14 +496,31 @@ public class MessageAdapter extends ArrayAdapter<Message> {
private void displayFileMessage(final Message message, ViewHolder viewHolder) {
Transferable transferable = message.getTransferable();
if (null != transferable) {
if (FileStatus.CHECKING_FILE_SIZE == message.getFileParams().getFileStatus()) {
displayInfoMessage(viewHolder, activity.getString(R.string.checking_remote_filesize));
} else if (MessageUtil.isAttachedFileAnImage(message)
&& (FileStatus.DOWNLOADED == message.getFileParams().getFileStatus())
|| FileStatus.DELETED == message.getFileParams().getFileStatus()
|| FileStatus.DELETING == message.getFileParams().getFileStatus()
|| FileStatus.DELETE_FAILED == message.getFileParams().getFileStatus()
|| FileStatus.UPLOADED == message.getFileParams().getFileStatus()) {
displayImageMessage(viewHolder, message);
} else if ((MessageUtil.isTypeFileAndDecrypted(message) || FileStatus.DOWNLOADED == message.getFileParams().getFileStatus())
&& !MessageUtil.needsDownload(message)) {
displayOpenableMessage(viewHolder, message);
} else if (Message.Decision.NEVER == message.treatAsDownloadable() || !MessageUtil.mayFileRemoteAvailable(message)) {
displayTextMessage(viewHolder, message);
} else if (FileStatus.UPLOAD_FAILED == message.getFileParams().getFileStatus()) {
displayImageMessage(viewHolder, message); // TODO Show failed status
} else if (null != transferable) {
switch (transferable.getStatus()) {
case Transferable.STATUS_OFFER:
case Transferable.STATUS_OFFER_CHECK_FILESIZE:
displayDownloadableMessage(viewHolder, message);
break;
case Transferable.STATUS_UPLOADING:
displayFileMessage(message, viewHolder);
// Should not happen, since this is now covered by the other if-statements
// TODO Maybe in Jingle File Transfer?? Needs to be checked!
break;
case Transferable.STATUS_DELETED:
case Transferable.STATUS_CHECKING:
@ -513,15 +530,6 @@ public class MessageAdapter extends ArrayAdapter<Message> {
displayInfoMessage(viewHolder, UIHelper.getMessagePreview(activity, message).first);
break;
}
} else if (FileStatus.CHECKING_FILE_SIZE == message.getFileParams().getFileStatus()) {
displayInfoMessage(viewHolder, activity.getString(R.string.checking_remote_filesize));
} else if (MessageUtil.isAttachedFileAnImage(message) && FileStatus.DOWNLOADED == message.getFileParams().getFileStatus()) {
displayImageMessage(viewHolder, message);
} else if ((MessageUtil.isTypeFileAndDecrypted(message) || FileStatus.DOWNLOADED == message.getFileParams().getFileStatus())
&& !MessageUtil.needsDownload(message)) {
displayOpenableMessage(viewHolder, message);
} else if (Message.Decision.NEVER == message.treatAsDownloadable() || !MessageUtil.mayFileRemoteAvailable(message)) {
displayTextMessage(viewHolder, message);
} else {
displayDownloadableMessage(viewHolder, message);
}

View file

@ -56,7 +56,7 @@ public class MessageDetailsDialog extends AbstractAlertDialog {
* @param message the message to display in dialog
*/
protected void displayFileInfo(View view, Message message) {
if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE || message.getTransferable() != null) {
if (message.isHttpUploaded() || message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE || message.getTransferable() != null) {
Logging.d("messagedetailsfile", "File is stored in path: " + message.getRelativeFilePath());
view.findViewById(R.id.dlgMsgDetFileTable).setVisibility(View.VISIBLE);
if (null != message.getFileParams()) {

View file

@ -31,6 +31,7 @@ import de.thedevstack.conversationsplus.ui.UiCallback;
import de.thedevstack.conversationsplus.ui.XmppActivity;
import de.thedevstack.conversationsplus.utils.MimeUtils;
import de.thedevstack.conversationsplus.utils.StreamUtil;
import de.thedevstack.conversationsplus.utils.UiUpdateHelper;
/**
* Listener to let the user decide whether to resize a picture before sending or not.
@ -57,6 +58,7 @@ public class ResizePictureUserDecisionListener implements UserDecisionListener {
@Override
public void success(Message message) {
UiUpdateHelper.updateConversationUi();
ResizePictureUserDecisionListener.this.xmppConnectionService.sendMessage(message);
}

View file

@ -181,6 +181,7 @@ public final class MessageUtil {
switch (message.getStatus()) {
case Message.STATUS_SEND:
case Message.STATUS_SEND_DISPLAYED:
case Message.STATUS_SEND_FAILED:
case Message.STATUS_SEND_RECEIVED:
return true;
default:
@ -283,7 +284,7 @@ public final class MessageUtil {
fileParams.setHeight(imageHeight);
}
String relativeFilePathFromMessage = message.getRelativeFilePath();
if (null != relativeFilePathFromMessage && relativeFilePathFromMessage.startsWith("/")) {
if (null != relativeFilePathFromMessage && relativeFilePathFromMessage.startsWith("/") && (null == fileParams.getPath() || !relativeFilePathFromMessage.equals(fileParams.getPath()))) {
fileParams.setPath(relativeFilePathFromMessage);
}
}

View file

@ -1208,6 +1208,7 @@ public class XmppConnection implements Runnable {
disconnect(true);
return;
}
Logging.d("SendIqPacket", "Outgoing stanza: " + packet.toString());
tagWriter.writeStanzaAsync(packet);
if (packet instanceof AbstractAcknowledgeableStanza) {
AbstractAcknowledgeableStanza stanza = (AbstractAcknowledgeableStanza) packet;

View file

@ -1,8 +1,9 @@
package de.thedevstack.conversationsplus.xmpp.filetransfer.http;
/**
* Created by steckbrief on 21.08.2016.
*
*/
public interface FileTransferHttp {
String NAMESPACE = "urn:xmpp:filetransfer:http";
String DEFAULT_MIME_TYPE = "application/octet-stream";
}

View file

@ -7,19 +7,19 @@ import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket;
/**
* Created by steckbrief on 21.08.2016.
*/
public class DeleteSlotRequestPacket extends IqPacket {
public class DeleteRequestPacket extends IqPacket {
public static final String ELEMENT_NAME = "request";
public static final String FILEURL_ELEMENT_NAME = "fileurl";
private Element requestElement;
private String fileurl;
private DeleteSlotRequestPacket() {
private DeleteRequestPacket() {
super(TYPE.GET);
this.requestElement = super.addChild(DeleteSlotRequestPacket.ELEMENT_NAME, FileTransferHttp.NAMESPACE);
this.requestElement = super.addChild(DeleteRequestPacket.ELEMENT_NAME, FileTransferHttp.NAMESPACE);
this.requestElement.setAttribute("type", "delete");
}
public DeleteSlotRequestPacket(String fileurl) {
public DeleteRequestPacket(String fileurl) {
this();
this.setFileURL(fileurl);
}

View file

@ -1,29 +0,0 @@
package de.thedevstack.conversationsplus.xmpp.filetransfer.http.delete;
import de.thedevstack.conversationsplus.xml.Element;
import de.thedevstack.conversationsplus.xmpp.IqPacketParser;
import de.thedevstack.conversationsplus.xmpp.exceptions.UnexpectedIqPacketTypeException;
import de.thedevstack.conversationsplus.xmpp.exceptions.XmppException;
import de.thedevstack.conversationsplus.xmpp.filetransfer.http.FileTransferHttp;
import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket;
import de.thedevstack.conversationsplus.xmpp.utils.ErrorIqPacketExceptionHelper;
/**
* Created by steckbrief on 21.08.2016.
*/
public class DeleteSlotPacketParser extends IqPacketParser {
public static String parseDeleteToken(IqPacket packet) throws XmppException {
String deletetoken = null;
if (packet.getType() == IqPacket.TYPE.RESULT) {
Element slot = findRequiredChild(packet, "slot", FileTransferHttp.NAMESPACE);
deletetoken = findRequiredChildContent(slot, "deletetoken");
} else if (packet.getType() == IqPacket.TYPE.ERROR) {
ErrorIqPacketExceptionHelper.throwIqErrorException(packet);
} else {
throw new UnexpectedIqPacketTypeException(packet, packet.getType(), IqPacket.TYPE.RESULT, IqPacket.TYPE.ERROR);
}
return deletetoken;
}
}

View file

@ -0,0 +1,43 @@
package de.thedevstack.conversationsplus.xmpp.filetransfer.http.delete;
import de.thedevstack.conversationsplus.xml.Element;
import de.thedevstack.conversationsplus.xmpp.IqPacketParser;
import de.thedevstack.conversationsplus.xmpp.exceptions.UnexpectedIqPacketTypeException;
import de.thedevstack.conversationsplus.xmpp.exceptions.XmppException;
import de.thedevstack.conversationsplus.xmpp.filetransfer.http.FileTransferHttp;
import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket;
import de.thedevstack.conversationsplus.xmpp.utils.ErrorIqPacketExceptionHelper;
/**
* IqPacketParser to parse the response of a remote file delete request.
* This parser parses a IqPacket according to the specification 'filetransfer for XMPP over http".
*/
public class DeletedPacketParser extends IqPacketParser {
/**
* Parses an IqPacket.
* <pre>
* <iq from='montague.tld'
* id='delete-file_002'
* to='romeo@montague.tld/garden'
* type='result'>
* <deleted xmlns='urn:xmpp:filetransfer:http'/>
* </iq>
* </pre>
* @param packet the packet to parse
* @return <code>true</code> if the result packet contains a deleted element of namespace <code>urn:xmpp:filetransfer:http</code>
* @throws XmppException in case of IqPacket type error or {@link UnexpectedIqPacketTypeException} in case of an unexpected IqPacket type.
*/
public static boolean parseDeleteToken(IqPacket packet) throws XmppException {
boolean successfullyDeleted = false;
if (packet.getType() == IqPacket.TYPE.RESULT) {
Element deletedElement = findRequiredChild(packet, "deleted", FileTransferHttp.NAMESPACE);
successfullyDeleted = null != deletedElement;
} else if (packet.getType() == IqPacket.TYPE.ERROR) {
ErrorIqPacketExceptionHelper.throwIqErrorException(packet);
} else {
throw new UnexpectedIqPacketTypeException(packet, packet.getType(), IqPacket.TYPE.RESULT, IqPacket.TYPE.ERROR);
}
return successfullyDeleted;
}
}

View file

@ -6,7 +6,7 @@ import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket;
/**
* Created by steckbrief on 21.08.2016.
*/
public final class FileTransferHttpDeleteSlotRequestPacketGenerator {
public final class FileTransferHttpDeleteRequestPacketGenerator {
/**
* Generates the IqPacket to request a slot to delete a file previously uploaded via http upload.
* The attributes from and id are not set in here - this is added while sending the packet.
@ -25,7 +25,7 @@ public final class FileTransferHttpDeleteSlotRequestPacketGenerator {
* @return the IqPacket
*/
public static IqPacket generate(Jid host, String fileurl) {
DeleteSlotRequestPacket packet = new DeleteSlotRequestPacket(fileurl);
DeleteRequestPacket packet = new DeleteRequestPacket(fileurl);
packet.setTo(host);
return packet;
}
@ -33,7 +33,7 @@ public final class FileTransferHttpDeleteSlotRequestPacketGenerator {
/**
* Utility class - avoid instantiation
*/
private FileTransferHttpDeleteSlotRequestPacketGenerator() {
private FileTransferHttpDeleteRequestPacketGenerator() {
// Helper class - avoid instantiation
}
}

View file

@ -0,0 +1,53 @@
package de.thedevstack.conversationsplus.xmpp.filetransfer.http.upload;
import de.thedevstack.conversationsplus.xml.Element;
import de.thedevstack.conversationsplus.xmpp.filetransfer.http.FileTransferHttp;
import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket;
/**
*
*/
public class FileTransferHttpUploadSlotRequestPacket extends IqPacket {
public static final String ELEMENT_NAME = "request";
public static final String FILENAME_ELEMENT_NAME = "filename";
public static final String FILESIZE_ELEMENT_NAME = "size";
public static final String MIME_ELEMENT_NAME = "content-type";
private static final String RECIPIENT_ATTRIBUTE_NAME = "recipient";
private Element requestElement;
private String filename;
private long filesize;
private String mime;
public FileTransferHttpUploadSlotRequestPacket(String recipient, String filename, long filesize, String mime) {
super(TYPE.GET);
this.requestElement = super.addChild(FileTransferHttpUploadSlotRequestPacket.ELEMENT_NAME, FileTransferHttp.NAMESPACE);
this.requestElement.setAttribute(RECIPIENT_ATTRIBUTE_NAME, recipient);
this.setFilename(filename);
this.setFilesize(filesize);
this.setMime(mime);
}
public void setFilename(String filename) {
if (null == filename || filename.isEmpty()) {
throw new IllegalArgumentException("filename must not be null or empty.");
}
this.filename = filename;
this.requestElement.addChild(FILENAME_ELEMENT_NAME).setContent(filename);
}
public void setFilesize(long filesize) {
if (0 >= filesize) {
throw new IllegalArgumentException("filesize must not be null or empty.");
}
this.filesize = filesize;
this.requestElement.addChild(FILESIZE_ELEMENT_NAME).setContent(String.valueOf(filesize));
}
public void setMime(String mime) {
if (null == mime || mime.isEmpty()) {
mime = FileTransferHttp.DEFAULT_MIME_TYPE;
}
this.mime = mime;
this.requestElement.addChild(MIME_ELEMENT_NAME).setContent(mime);
}
}

View file

@ -1,5 +1,7 @@
package de.thedevstack.conversationsplus.xmpp.filetransfer.http.upload;
import de.thedevstack.conversationsplus.entities.Account;
import de.thedevstack.conversationsplus.xmpp.filetransfer.http.FileTransferHttp;
import de.thedevstack.conversationsplus.xmpp.jid.Jid;
import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket;
@ -24,17 +26,38 @@ public final class HttpUploadRequestSlotPacketGenerator {
* </request>
* </iq>
* </pre>
* @param host the jid of the host to request a slot from
* @param account the account requesting a slot
* @param filename the filename of the file which will be transferred
* @param filesize the filesize of the file which will be transferred
* @param mime the mime type of the file which will be transferred - <code>optional</code> and therefore nullable
* @return the IqPacket
*/
public static IqPacket generate(Jid host, String filename, long filesize, String mime) {
SlotRequestPacket packet = new SlotRequestPacket(filename, filesize);
packet.setTo(host);
packet.setMime((mime == null || mime.isEmpty()) ? HttpUpload.DEFAULT_MIME_TYPE : mime);
return packet;
public static IqPacket generate(Account account, Jid recipient, String filename, long filesize, String mime) {
String namespace = getNamespace(account);
Jid host = getHost(account, namespace);
IqPacket requestPacket;
switch (namespace) {
case HttpUpload.NAMESPACE:
requestPacket = new HttpUploadSlotRequestPacket(filename, filesize, mime);
break;
case FileTransferHttp.NAMESPACE:
default:
requestPacket = new FileTransferHttpUploadSlotRequestPacket(recipient.toString(), filename, filesize, mime);
}
requestPacket.setTo(host);
return requestPacket;
}
private static String getNamespace(Account account) {
if (null != account.getXmppConnection().findDiscoItemByFeature(FileTransferHttp.NAMESPACE)) {
return FileTransferHttp.NAMESPACE;
} else {
return HttpUpload.NAMESPACE;
}
}
private static Jid getHost(Account account, String namespace) {
return account.getXmppConnection().findDiscoItemByFeature(namespace);
}
/**

View file

@ -6,7 +6,7 @@ import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket;
/**
*
*/
public class SlotRequestPacket extends IqPacket {
public class HttpUploadSlotRequestPacket extends IqPacket {
public static final String ELEMENT_NAME = "request";
public static final String FILENAME_ELEMENT_NAME = "filename";
public static final String FILESIZE_ELEMENT_NAME = "size";
@ -16,15 +16,16 @@ public class SlotRequestPacket extends IqPacket {
private long filesize;
private String mime;
private SlotRequestPacket() {
private HttpUploadSlotRequestPacket() {
super(TYPE.GET);
this.requestElement = super.addChild(SlotRequestPacket.ELEMENT_NAME, HttpUpload.NAMESPACE);
this.requestElement = super.addChild(HttpUploadSlotRequestPacket.ELEMENT_NAME, HttpUpload.NAMESPACE);
}
public SlotRequestPacket(String filename, long filesize) {
public HttpUploadSlotRequestPacket(String filename, long filesize, String mime) {
this();
this.setFilename(filename);
this.setFilesize(filesize);
this.setMime(mime);
}
public void setFilename(String filename) {
@ -45,7 +46,7 @@ public class SlotRequestPacket extends IqPacket {
public void setMime(String mime) {
if (null == mime || mime.isEmpty()) {
throw new IllegalArgumentException("mime type must not be null or empty.");
mime = HttpUpload.DEFAULT_MIME_TYPE;
}
this.mime = mime;
this.requestElement.addChild(MIME_ELEMENT_NAME).setContent(mime);

View file

@ -2,8 +2,10 @@ package de.thedevstack.conversationsplus.xmpp.filetransfer.http.upload;
import de.thedevstack.conversationsplus.xml.Element;
import de.thedevstack.conversationsplus.xmpp.IqPacketParser;
import de.thedevstack.conversationsplus.xmpp.exceptions.MissingRequiredElementException;
import de.thedevstack.conversationsplus.xmpp.exceptions.UnexpectedIqPacketTypeException;
import de.thedevstack.conversationsplus.xmpp.exceptions.XmppException;
import de.thedevstack.conversationsplus.xmpp.filetransfer.http.FileTransferHttp;
import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket;
import de.thedevstack.conversationsplus.xmpp.utils.ErrorIqPacketExceptionHelper;
@ -11,10 +13,18 @@ import de.thedevstack.conversationsplus.xmpp.utils.ErrorIqPacketExceptionHelper;
*
*/
public final class SlotPacketParser extends IqPacketParser {
private static final String SLOT_ELEMENT_NAME = "slot";
public static HttpUploadSlot parseGetAndPutUrl(IqPacket packet) throws XmppException {
HttpUploadSlot httpUploadSlot = null;
if (packet.getType() == IqPacket.TYPE.RESULT) {
Element slot = findRequiredChild(packet, "slot", HttpUpload.NAMESPACE);
Element slot = findChild(packet, SLOT_ELEMENT_NAME, FileTransferHttp.NAMESPACE);
if (null == slot) {
slot = findChild(packet, SLOT_ELEMENT_NAME, HttpUpload.NAMESPACE);
}
if (null == slot) {
throw new MissingRequiredElementException(SLOT_ELEMENT_NAME, "neither " + FileTransferHttp.NAMESPACE + " nor " + HttpUpload.NAMESPACE, packet);
}
String getUrl = findRequiredChildContent(slot, "get");
String putUrl = findRequiredChildContent(slot, "put");

View file

@ -464,6 +464,8 @@ public class JingleConnection implements Transferable {
}
});
} else {
fail();
}
}