aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authoriNPUTmice <daniel@gultsch.de>2014-11-13 21:04:05 +0100
committeriNPUTmice <daniel@gultsch.de>2014-11-15 15:34:11 +0100
commit7a90ca429bb46fae4cbd600bd4c2274f4a731a16 (patch)
tree0ec0d753e32f0c361e43bf2bac627bda39a7899d /src
parent4c504dea7ac7b0413df89ec630408b91f4abddb4 (diff)
basic arbitrary file transfer
Diffstat (limited to 'src')
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Downloadable.java6
-rw-r--r--src/main/java/eu/siacs/conversations/entities/DownloadableFile.java12
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Message.java25
-rw-r--r--src/main/java/eu/siacs/conversations/http/HttpConnection.java15
-rw-r--r--src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java7
-rw-r--r--src/main/java/eu/siacs/conversations/persistance/FileBackend.java73
-rw-r--r--src/main/java/eu/siacs/conversations/services/XmppConnectionService.java39
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConversationActivity.java46
-rw-r--r--src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java97
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java131
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java8
-rw-r--r--src/main/res/menu/attachment_choices.xml3
-rw-r--r--src/main/res/values/strings.xml6
13 files changed, 332 insertions, 136 deletions
diff --git a/src/main/java/eu/siacs/conversations/entities/Downloadable.java b/src/main/java/eu/siacs/conversations/entities/Downloadable.java
index e4c85336..cc0dc157 100644
--- a/src/main/java/eu/siacs/conversations/entities/Downloadable.java
+++ b/src/main/java/eu/siacs/conversations/entities/Downloadable.java
@@ -2,7 +2,7 @@ package eu.siacs.conversations.entities;
public interface Downloadable {
- public final String[] VALID_EXTENSIONS = {"webp", "jpeg", "jpg", "png", "jpe"};
+ public final String[] VALID_IMAGE_EXTENSIONS = {"webp", "jpeg", "jpg", "png", "jpe"};
public final String[] VALID_CRYPTO_EXTENSIONS = {"pgp", "gpg", "otr"};
public static final int STATUS_UNKNOWN = 0x200;
@@ -18,4 +18,8 @@ public interface Downloadable {
public int getStatus();
public long getFileSize();
+
+ public int getProgress();
+
+ public String getMimeType();
}
diff --git a/src/main/java/eu/siacs/conversations/entities/DownloadableFile.java b/src/main/java/eu/siacs/conversations/entities/DownloadableFile.java
index 1605c75b..6aff643a 100644
--- a/src/main/java/eu/siacs/conversations/entities/DownloadableFile.java
+++ b/src/main/java/eu/siacs/conversations/entities/DownloadableFile.java
@@ -6,6 +6,7 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
+import java.net.URLConnection;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
@@ -28,6 +29,7 @@ public class DownloadableFile extends File {
private long expectedSize = 0;
private String sha1sum;
private Key aeskey;
+ private String mime;
private byte[] iv = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0xf };
@@ -52,6 +54,16 @@ public class DownloadableFile extends File {
}
}
+ public String getMimeType() {
+ if (mime==null) {
+ mime = URLConnection.guessContentTypeFromName(this.getAbsolutePath());
+ if (mime == null) {
+ mime = "";
+ }
+ }
+ return mime;
+ }
+
public void setExpectedSize(long size) {
this.expectedSize = size;
}
diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java
index 5b44435e..33f3443b 100644
--- a/src/main/java/eu/siacs/conversations/entities/Message.java
+++ b/src/main/java/eu/siacs/conversations/entities/Message.java
@@ -32,7 +32,7 @@ public class Message extends AbstractEntity {
public static final int TYPE_TEXT = 0;
public static final int TYPE_IMAGE = 1;
- public static final int TYPE_AUDIO = 2;
+ public static final int TYPE_FILE = 2;
public static final int TYPE_STATUS = 3;
public static final int TYPE_PRIVATE = 4;
@@ -45,6 +45,7 @@ public class Message extends AbstractEntity {
public static String STATUS = "status";
public static String TYPE = "type";
public static String REMOTE_MSG_ID = "remoteMsgId";
+ public static String RELATIVE_FILE_PATH = "relativeFilePath";
public boolean markable = false;
protected String conversationUuid;
protected Jid counterpart;
@@ -55,6 +56,7 @@ public class Message extends AbstractEntity {
protected int encryption;
protected int status;
protected int type;
+ protected String relativeFilePath;
protected boolean read = true;
protected String remoteMsgId = null;
protected Conversation conversation = null;
@@ -74,13 +76,13 @@ public class Message extends AbstractEntity {
this(java.util.UUID.randomUUID().toString(), conversation.getUuid(),
conversation.getContactJid().toBareJid(), null, body, System
.currentTimeMillis(), encryption,
- status, TYPE_TEXT, null);
+ status, TYPE_TEXT, null,null);
this.conversation = conversation;
}
public Message(final String uuid, final String conversationUUid, final Jid counterpart,
final String trueCounterpart, final String body, final long timeSent,
- final int encryption, final int status, final int type, final String remoteMsgId) {
+ final int encryption, final int status, final int type, final String remoteMsgId, final String relativeFilePath) {
this.uuid = uuid;
this.conversationUuid = conversationUUid;
this.counterpart = counterpart;
@@ -91,6 +93,7 @@ public class Message extends AbstractEntity {
this.status = status;
this.type = type;
this.remoteMsgId = remoteMsgId;
+ this.relativeFilePath = relativeFilePath;
}
public static Message fromCursor(Cursor cursor) {
@@ -114,7 +117,8 @@ public class Message extends AbstractEntity {
cursor.getInt(cursor.getColumnIndex(ENCRYPTION)),
cursor.getInt(cursor.getColumnIndex(STATUS)),
cursor.getInt(cursor.getColumnIndex(TYPE)),
- cursor.getString(cursor.getColumnIndex(REMOTE_MSG_ID)));
+ cursor.getString(cursor.getColumnIndex(REMOTE_MSG_ID)),
+ cursor.getString(cursor.getColumnIndex(RELATIVE_FILE_PATH)));
}
public static Message createStatusMessage(Conversation conversation) {
@@ -141,6 +145,7 @@ public class Message extends AbstractEntity {
values.put(STATUS, status);
values.put(TYPE, type);
values.put(REMOTE_MSG_ID, remoteMsgId);
+ values.put(RELATIVE_FILE_PATH, relativeFilePath);
return values;
}
@@ -205,6 +210,14 @@ public class Message extends AbstractEntity {
this.status = status;
}
+ public void setRelativeFilePath(String path) {
+ this.relativeFilePath = path;
+ }
+
+ public String getRelativeFilePath() {
+ return this.relativeFilePath;
+ }
+
public String getRemoteMsgId() {
return this.remoteMsgId;
}
@@ -376,14 +389,14 @@ public class Message extends AbstractEntity {
}
String[] extensionParts = filename.split("\\.");
if (extensionParts.length == 2
- && Arrays.asList(Downloadable.VALID_EXTENSIONS).contains(
+ && Arrays.asList(Downloadable.VALID_IMAGE_EXTENSIONS).contains(
extensionParts[extensionParts.length - 1])) {
return true;
} else if (extensionParts.length == 3
&& Arrays
.asList(Downloadable.VALID_CRYPTO_EXTENSIONS)
.contains(extensionParts[extensionParts.length - 1])
- && Arrays.asList(Downloadable.VALID_EXTENSIONS).contains(
+ && Arrays.asList(Downloadable.VALID_IMAGE_EXTENSIONS).contains(
extensionParts[extensionParts.length - 2])) {
return true;
} else {
diff --git a/src/main/java/eu/siacs/conversations/http/HttpConnection.java b/src/main/java/eu/siacs/conversations/http/HttpConnection.java
index 147ac42f..fd6b9c1a 100644
--- a/src/main/java/eu/siacs/conversations/http/HttpConnection.java
+++ b/src/main/java/eu/siacs/conversations/http/HttpConnection.java
@@ -37,6 +37,7 @@ public class HttpConnection implements Downloadable {
private DownloadableFile file;
private int mStatus = Downloadable.STATUS_UNKNOWN;
private boolean acceptedAutomatically = false;
+ private int mProgress = 0;
public HttpConnection(HttpConnectionManager manager) {
this.mHttpConnectionManager = manager;
@@ -235,10 +236,14 @@ public class HttpConnection implements Downloadable {
if (os == null) {
throw new IOException();
}
+ long transmitted = 0;
+ long expected = file.getExpectedSize();
int count = -1;
byte[] buffer = new byte[1024];
while ((count = is.read(buffer)) != -1) {
+ transmitted += count;
os.write(buffer, 0, count);
+ mProgress = (int) (expected * 100 / transmitted);
}
os.flush();
os.close();
@@ -272,4 +277,14 @@ public class HttpConnection implements Downloadable {
return 0;
}
}
+
+ @Override
+ public int getProgress() {
+ return this.mProgress;
+ }
+
+ @Override
+ public String getMimeType() {
+ return "";
+ }
} \ No newline at end of file
diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
index 310c8f42..38f4fdf1 100644
--- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
+++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
@@ -22,7 +22,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
private static DatabaseBackend instance = null;
private static final String DATABASE_NAME = "history";
- private static final int DATABASE_VERSION = 9;
+ private static final int DATABASE_VERSION = 10;
private static String CREATE_CONTATCS_STATEMENT = "create table "
+ Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, "
@@ -64,6 +64,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
+ " TEXT, " + Message.TRUE_COUNTERPART + " TEXT,"
+ Message.BODY + " TEXT, " + Message.ENCRYPTION + " NUMBER, "
+ Message.STATUS + " NUMBER," + Message.TYPE + " NUMBER, "
+ + Message.RELATIVE_FILE_PATH + " TEXT, "
+ Message.REMOTE_MSG_ID + " TEXT, FOREIGN KEY("
+ Message.CONVERSATION + ") REFERENCES "
+ Conversation.TABLENAME + "(" + Conversation.UUID
@@ -110,6 +111,10 @@ public class DatabaseBackend extends SQLiteOpenHelper {
db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
+ Contact.LAST_PRESENCE + " TEXT");
}
+ if (oldVersion < 10 && newVersion >= 10) {
+ db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
+ + Message.RELATIVE_FILE_PATH + " TEXT");
+ }
}
public static synchronized DatabaseBackend getInstance(Context context) {
diff --git a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java
index 0241b77e..f052bd53 100644
--- a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java
+++ b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java
@@ -2,11 +2,13 @@ package eu.siacs.conversations.persistance;
import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.net.URLConnection;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@@ -14,6 +16,7 @@ import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
+import android.content.ContentResolver;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -53,25 +56,34 @@ public class FileBackend {
}
public DownloadableFile getFile(Message message, boolean decrypted) {
- StringBuilder filename = new StringBuilder();
- filename.append(getConversationsDirectory());
- filename.append(message.getUuid());
- if ((decrypted) || (message.getEncryption() == Message.ENCRYPTION_NONE)) {
- filename.append(".webp");
+ String path = message.getRelativeFilePath();
+ if (path != null && !path.isEmpty()) {
+ if (path.startsWith("/")) {
+ return new DownloadableFile(path);
+ } else {
+ return new DownloadableFile(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)+"/"+path);
+ }
} else {
- if (message.getEncryption() == Message.ENCRYPTION_OTR) {
+ StringBuilder filename = new StringBuilder();
+ filename.append(getConversationsDirectory());
+ filename.append(message.getUuid());
+ if ((decrypted) || (message.getEncryption() == Message.ENCRYPTION_NONE)) {
filename.append(".webp");
} else {
- filename.append(".webp.pgp");
+ if (message.getEncryption() == Message.ENCRYPTION_OTR) {
+ filename.append(".webp");
+ } else {
+ filename.append(".webp.pgp");
+ }
}
+ return new DownloadableFile(filename.toString());
}
- return new DownloadableFile(filename.toString());
}
public static String getConversationsDirectory() {
return Environment.getExternalStoragePublicDirectory(
- Environment.DIRECTORY_PICTURES).getAbsolutePath()
- + "/Conversations/";
+ Environment.DIRECTORY_PICTURES).getAbsolutePath()
+ + "/Conversations/";
}
public Bitmap resize(Bitmap originalBitmap, int size) {
@@ -103,13 +115,34 @@ public class FileBackend {
return Bitmap.createBitmap(bitmap, 0, 0, w, h, mtx, true);
}
+ public String getOriginalPath(Uri uri) {
+ String path = null;
+ if (uri.getScheme().equals("file")) {
+ path = uri.getPath();
+ } else {
+ String[] projection = {MediaStore.MediaColumns.DATA};
+ Cursor metaCursor = mXmppConnectionService.getContentResolver().query(uri,
+ projection, null, null, null);
+ if (metaCursor != null) {
+ try {
+ if (metaCursor.moveToFirst()) {
+ path = metaCursor.getString(0);
+ }
+ } finally {
+ metaCursor.close();
+ }
+ }
+ }
+ return path;
+ }
+
public DownloadableFile copyImageToPrivateStorage(Message message, Uri image)
- throws ImageCopyException {
+ throws FileCopyException {
return this.copyImageToPrivateStorage(message, image, 0);
}
private DownloadableFile copyImageToPrivateStorage(Message message,
- Uri image, int sampleSize) throws ImageCopyException {
+ Uri image, int sampleSize) throws FileCopyException {
try {
InputStream is = mXmppConnectionService.getContentResolver()
.openInputStream(image);
@@ -125,7 +158,7 @@ public class FileBackend {
originalBitmap = BitmapFactory.decodeStream(is, null, options);
is.close();
if (originalBitmap == null) {
- throw new ImageCopyException(R.string.error_not_an_image_file);
+ throw new FileCopyException(R.string.error_not_an_image_file);
}
Bitmap scalledBitmap = resize(originalBitmap, IMAGE_SIZE);
originalBitmap = null;
@@ -137,7 +170,7 @@ public class FileBackend {
boolean success = scalledBitmap.compress(
Bitmap.CompressFormat.WEBP, 75, os);
if (!success) {
- throw new ImageCopyException(R.string.error_compressing_image);
+ throw new FileCopyException(R.string.error_compressing_image);
}
os.flush();
os.close();
@@ -147,18 +180,18 @@ public class FileBackend {
message.setBody(Long.toString(size) + ',' + width + ',' + height);
return file;
} catch (FileNotFoundException e) {
- throw new ImageCopyException(R.string.error_file_not_found);
+ throw new FileCopyException(R.string.error_file_not_found);
} catch (IOException e) {
- throw new ImageCopyException(R.string.error_io_exception);
+ throw new FileCopyException(R.string.error_io_exception);
} catch (SecurityException e) {
- throw new ImageCopyException(
+ throw new FileCopyException(
R.string.error_security_exception_during_image_copy);
} catch (OutOfMemoryError e) {
++sampleSize;
if (sampleSize <= 3) {
return copyImageToPrivateStorage(message, image, sampleSize);
} else {
- throw new ImageCopyException(R.string.error_out_of_memory);
+ throw new FileCopyException(R.string.error_out_of_memory);
}
}
}
@@ -400,11 +433,11 @@ public class FileBackend {
return Uri.parse("file://" + file.getAbsolutePath());
}
- public class ImageCopyException extends Exception {
+ public class FileCopyException extends Exception {
private static final long serialVersionUID = -1010013599132881427L;
private int resId;
- public ImageCopyException(int resId) {
+ public FileCopyException(int resId) {
this.resId = resId;
}
diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
index 831a54e1..36ccc632 100644
--- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
@@ -56,6 +56,7 @@ import eu.siacs.conversations.entities.Bookmark;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Downloadable;
+import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.entities.MucOptions.OnRenameListener;
@@ -294,6 +295,27 @@ public class XmppConnectionService extends Service {
return this.mAvatarService;
}
+ public Message attachFileToConversation(Conversation conversation, final Uri uri) {
+ String path = getFileBackend().getOriginalPath(uri);
+ if (path!=null) {
+ Log.d(Config.LOGTAG,"file path : "+path);
+ Message message;
+ if (conversation.getNextEncryption(forceEncryption()) == Message.ENCRYPTION_PGP) {
+ message = new Message(conversation, "",
+ Message.ENCRYPTION_DECRYPTED);
+ } else {
+ message = new Message(conversation, "",
+ conversation.getNextEncryption(forceEncryption()));
+ }
+ message.setCounterpart(conversation.getNextCounterpart());
+ message.setType(Message.TYPE_FILE);
+ message.setStatus(Message.STATUS_OFFERED);
+ message.setRelativeFilePath(path);
+ return message;
+ }
+ return null;
+ }
+
public Message attachImageToConversation(final Conversation conversation,
final Uri uri, final UiCallback<Message> callback) {
final Message message;
@@ -312,13 +334,14 @@ public class XmppConnectionService extends Service {
@Override
public void run() {
try {
- getFileBackend().copyImageToPrivateStorage(message, uri);
+ DownloadableFile file = getFileBackend().copyImageToPrivateStorage(message, uri);
+ message.setRelativeFilePath(file.getName());
if (conversation.getNextEncryption(forceEncryption()) == Message.ENCRYPTION_PGP) {
getPgpEngine().encrypt(message, callback);
} else {
callback.success(message);
}
- } catch (FileBackend.ImageCopyException e) {
+ } catch (FileBackend.FileCopyException e) {
callback.error(e.getResId(), message);
}
}
@@ -552,7 +575,7 @@ public class XmppConnectionService extends Service {
boolean send = false;
if (account.getStatus() == Account.STATUS_ONLINE
&& account.getXmppConnection() != null) {
- if (message.getType() == Message.TYPE_IMAGE) {
+ if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) {
if (message.getCounterpart() != null) {
if (message.getEncryption() == Message.ENCRYPTION_OTR) {
if (!conv.hasValidOtrSession()) {
@@ -1988,5 +2011,15 @@ public class XmppConnectionService extends Service {
return 0;
}
+ @Override
+ public int getProgress() {
+ return 0;
+ }
+
+ @Override
+ public String getMimeType() {
+ return "";
+ }
+
}
}
diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
index 5d11bb59..8c2f9dab 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
@@ -15,6 +15,7 @@ import android.os.SystemClock;
import android.provider.MediaStore;
import android.support.v4.widget.SlidingPaneLayout;
import android.support.v4.widget.SlidingPaneLayout.PanelSlideListener;
+import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
@@ -31,6 +32,7 @@ import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
+import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
@@ -52,13 +54,14 @@ public class ConversationActivity extends XmppActivity implements
public static final int REQUEST_SEND_MESSAGE = 0x0201;
public static final int REQUEST_DECRYPT_PGP = 0x0202;
public static final int REQUEST_ENCRYPT_MESSAGE = 0x0207;
- private static final int REQUEST_ATTACH_FILE_DIALOG = 0x0203;
+ private static final int REQUEST_ATTACH_IMAGE_DIALOG = 0x0203;
private static final int REQUEST_IMAGE_CAPTURE = 0x0204;
private static final int REQUEST_RECORD_AUDIO = 0x0205;
private static final int REQUEST_SEND_PGP_IMAGE = 0x0206;
+ private static final int REQUEST_ATTACH_FILE_DIALOG = 0x0208;
private static final int ATTACHMENT_CHOICE_CHOOSE_IMAGE = 0x0301;
private static final int ATTACHMENT_CHOICE_TAKE_PHOTO = 0x0302;
- private static final int ATTACHMENT_CHOICE_RECORD_VOICE = 0x0303;
+ private static final int ATTACHMENT_CHOICE_CHOOSE_FILE = 0x0303;
private static final String STATE_OPEN_CONVERSATION = "state_open_conversation";
private static final String STATE_PANEL_OPEN = "state_panel_open";
private static final String STATE_PENDING_URI = "state_pending_uri";
@@ -66,6 +69,7 @@ public class ConversationActivity extends XmppActivity implements
private String mOpenConverstaion = null;
private boolean mPanelOpen = true;
private Uri mPendingImageUri = null;
+ private Uri mPendingFileUri = null;
private View mContentView;
@@ -308,11 +312,14 @@ public class ConversationActivity extends XmppActivity implements
attachFileIntent.setAction(Intent.ACTION_GET_CONTENT);
Intent chooser = Intent.createChooser(attachFileIntent,
getString(R.string.attach_file));
+ startActivityForResult(chooser, REQUEST_ATTACH_IMAGE_DIALOG);
+ } else if (attachmentChoice == ATTACHMENT_CHOICE_CHOOSE_FILE) {
+ Intent attachFileIntent = new Intent();
+ attachFileIntent.setType("file/*");
+ attachFileIntent.setAction(Intent.ACTION_GET_CONTENT);
+ Intent chooser = Intent.createChooser(attachFileIntent,
+ getString(R.string.attach_file));
startActivityForResult(chooser, REQUEST_ATTACH_FILE_DIALOG);
- } else if (attachmentChoice == ATTACHMENT_CHOICE_RECORD_VOICE) {
- Intent intent = new Intent(
- MediaStore.Audio.Media.RECORD_SOUND_ACTION);
- startActivityForResult(intent, REQUEST_RECORD_AUDIO);
}
}
});
@@ -483,7 +490,7 @@ public class ConversationActivity extends XmppActivity implements
attachFile(ATTACHMENT_CHOICE_TAKE_PHOTO);
break;
case R.id.attach_record_voice:
- attachFile(ATTACHMENT_CHOICE_RECORD_VOICE);
+ attachFile(ATTACHMENT_CHOICE_CHOOSE_FILE);
break;
}
return false;
@@ -675,14 +682,17 @@ public class ConversationActivity extends XmppActivity implements
} else {
showConversationsOverview();
mPendingImageUri = null;
+ mPendingFileUri = null;
setSelectedConversation(conversationList.get(0));
this.mConversationFragment.reInit(getSelectedConversation());
}
if (mPendingImageUri != null) {
- attachImageToConversation(getSelectedConversation(),
- mPendingImageUri);
+ attachImageToConversation(getSelectedConversation(),mPendingImageUri);
mPendingImageUri = null;
+ } else if (mPendingFileUri != null) {
+ attachFileToConversation(getSelectedConversation(),mPendingFileUri);
+ mPendingFileUri = null;
}
ExceptionHelper.checkForCrash(this, this.xmppConnectionService);
setIntent(new Intent());
@@ -726,13 +736,20 @@ public class ConversationActivity extends XmppActivity implements
selectedFragment.hideSnackbar();
selectedFragment.updateMessages();
}
- } else if (requestCode == REQUEST_ATTACH_FILE_DIALOG) {
+ } else if (requestCode == REQUEST_ATTACH_IMAGE_DIALOG) {
mPendingImageUri = data.getData();
if (xmppConnectionServiceBound) {
attachImageToConversation(getSelectedConversation(),
mPendingImageUri);
mPendingImageUri = null;
}
+ } else if (requestCode == REQUEST_ATTACH_FILE_DIALOG) {
+ mPendingFileUri = data.getData();
+ if (xmppConnectionServiceBound) {
+ attachFileToConversation(getSelectedConversation(),
+ mPendingFileUri);
+ mPendingFileUri = null;
+ }
} else if (requestCode == REQUEST_SEND_PGP_IMAGE) {
} else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_IMAGE) {
@@ -754,9 +771,6 @@ public class ConversationActivity extends XmppActivity implements
Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intent.setData(mPendingImageUri);
sendBroadcast(intent);
- } else if (requestCode == REQUEST_RECORD_AUDIO) {
- attachAudioToConversation(getSelectedConversation(),
- data.getData());
}
} else {
if (requestCode == REQUEST_IMAGE_CAPTURE) {
@@ -765,8 +779,10 @@ public class ConversationActivity extends XmppActivity implements
}
}
- private void attachAudioToConversation(Conversation conversation, Uri uri) {
-
+ private void attachFileToConversation(Conversation conversation, Uri uri) {
+ Log.d(Config.LOGTAG, "attachFileToConversation");
+ Message message = xmppConnectionService.attachFileToConversation(conversation,uri);
+ xmppConnectionService.sendMessage(message);
}
private void attachImageToConversation(Conversation conversation, Uri uri) {
diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
index 3ebb9390..6449a6f9 100644
--- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
+++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
@@ -2,15 +2,18 @@ package eu.siacs.conversations.ui.adapter;
import android.content.Intent;
import android.graphics.Typeface;
+import android.net.Uri;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.util.DisplayMetrics;
+import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
+import android.webkit.MimeTypeMap;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
@@ -18,6 +21,9 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
import java.util.List;
import eu.siacs.conversations.Config;
@@ -25,6 +31,7 @@ import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Downloadable;
+import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.Message.ImageParams;
import eu.siacs.conversations.ui.ConversationActivity;
@@ -181,13 +188,13 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}
}
- private void displayInfoMessage(ViewHolder viewHolder, int r) {
+ private void displayInfoMessage(ViewHolder viewHolder, String text) {
if (viewHolder.download_button != null) {
viewHolder.download_button.setVisibility(View.GONE);
}
viewHolder.image.setVisibility(View.GONE);
viewHolder.messageBody.setVisibility(View.VISIBLE);
- viewHolder.messageBody.setText(getContext().getString(r));
+ viewHolder.messageBody.setText(text);
viewHolder.messageBody.setTextColor(activity.getSecondaryTextColor());
viewHolder.messageBody.setTypeface(null, Typeface.ITALIC);
viewHolder.messageBody.setTextIsSelectable(false);
@@ -252,11 +259,11 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}
private void displayDownloadableMessage(ViewHolder viewHolder,
- final Message message, int resid) {
+ final Message message, String text) {
viewHolder.image.setVisibility(View.GONE);
viewHolder.messageBody.setVisibility(View.GONE);
viewHolder.download_button.setVisibility(View.VISIBLE);
- viewHolder.download_button.setText(resid);
+ viewHolder.download_button.setText(text);
viewHolder.download_button.setOnClickListener(new OnClickListener() {
@Override
@@ -267,6 +274,21 @@ public class MessageAdapter extends ArrayAdapter<Message> {
viewHolder.download_button.setOnLongClickListener(openContextMenu);
}
+ private void displayOpenableMessage(ViewHolder viewHolder,final Message message) {
+ viewHolder.image.setVisibility(View.GONE);
+ viewHolder.messageBody.setVisibility(View.GONE);
+ viewHolder.download_button.setVisibility(View.VISIBLE);
+ viewHolder.download_button.setText(activity.getString(R.string.open_file,activity.xmppConnectionService.getFileBackend().getFile(message).getMimeType()));
+ viewHolder.download_button.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ openDonwloadable(message);
+ }
+ });
+ viewHolder.download_button.setOnLongClickListener(openContextMenu);
+ }
+
private void displayImageMessage(ViewHolder viewHolder,
final Message message) {
if (viewHolder.download_button != null) {
@@ -455,42 +477,46 @@ public class MessageAdapter extends ArrayAdapter<Message> {
});
}
- if (item.getType() == Message.TYPE_IMAGE
- || item.getDownloadable() != null) {
+ if (item.getDownloadable() != null) {
Downloadable d = item.getDownloadable();
- if (d != null && d.getStatus() == Downloadable.STATUS_DOWNLOADING) {
- displayInfoMessage(viewHolder, R.string.receiving_image);
- } else if (d != null
- && d.getStatus() == Downloadable.STATUS_CHECKING) {
- displayInfoMessage(viewHolder, R.string.checking_image);
- } else if (d != null
- && d.getStatus() == Downloadable.STATUS_DELETED) {
- displayInfoMessage(viewHolder, R.string.image_file_deleted);
- } else if (d != null && d.getStatus() == Downloadable.STATUS_OFFER) {
- displayDownloadableMessage(viewHolder, item,
- R.string.download_image);
- } else if (d != null
- && d.getStatus() == Downloadable.STATUS_OFFER_CHECK_FILESIZE) {
- displayDownloadableMessage(viewHolder, item,
- R.string.check_image_filesize);
- } else if (d != null && d.getStatus() == Downloadable.STATUS_FAILED) {
- displayInfoMessage(viewHolder, R.string.image_transmission_failed);
- } else if ((item.getEncryption() == Message.ENCRYPTION_DECRYPTED)
- || (item.getEncryption() == Message.ENCRYPTION_NONE)
- || (item.getEncryption() == Message.ENCRYPTION_OTR)) {
- displayImageMessage(viewHolder, item);
- } else if (item.getEncryption() == Message.ENCRYPTION_PGP) {
- displayInfoMessage(viewHolder, R.string.encrypted_message);
- } else {
+ if (d.getStatus() == Downloadable.STATUS_DOWNLOADING) {
+ if (item.getType() == Message.TYPE_FILE) {
+ displayInfoMessage(viewHolder,activity.getString(R.string.receiving_file,d.getMimeType(),d.getProgress()));
+ } else {
+ displayInfoMessage(viewHolder,activity.getString(R.string.receiving_image,d.getProgress()));
+ }
+ } else if (d.getStatus() == Downloadable.STATUS_CHECKING) {
+ displayInfoMessage(viewHolder,activity.getString(R.string.checking_image));
+ } else if (d.getStatus() == Downloadable.STATUS_DELETED) {
+ displayInfoMessage(viewHolder,activity.getString(R.string.image_file_deleted));
+ } else if (d.getStatus() == Downloadable.STATUS_OFFER) {
+ if (item.getType() == Message.TYPE_FILE) {
+ displayDownloadableMessage(viewHolder,item,activity.getString(R.string.download_file,d.getMimeType()));
+ } else {
+ displayDownloadableMessage(viewHolder, item,activity.getString(R.string.download_image));
+ }
+ } else if (d.getStatus() == Downloadable.STATUS_OFFER_CHECK_FILESIZE) {
+ displayDownloadableMessage(viewHolder, item,activity.getString(R.string.check_image_filesize));
+ } else if (d.getStatus() == Downloadable.STATUS_FAILED) {
+ displayInfoMessage(viewHolder, activity.getString(R.string.image_transmission_failed));
+ }
+ } else if (item.getType() == Message.TYPE_IMAGE) {
+ if (item.getEncryption() == Message.ENCRYPTION_PGP) {
+ displayInfoMessage(viewHolder,activity.getString(R.string.encrypted_message));
+ } else if (item.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
displayDecryptionFailed(viewHolder);
+ } else {
+ displayImageMessage(viewHolder, item);
}
+ } else if (item.getType() == Message.TYPE_FILE) {
+ displayOpenableMessage(viewHolder,item);
} else {
if (item.getEncryption() == Message.ENCRYPTION_PGP) {
if (activity.hasPgp()) {
- displayInfoMessage(viewHolder, R.string.encrypted_message);
+ displayInfoMessage(viewHolder,activity.getString(R.string.encrypted_message));
} else {
displayInfoMessage(viewHolder,
- R.string.install_openkeychain);
+ activity.getString(R.string.install_openkeychain));
if (viewHolder != null) {
viewHolder.message_box
.setOnClickListener(new OnClickListener() {
@@ -524,6 +550,13 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}
}
+ public void openDonwloadable(Message message) {
+ DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message);
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setDataAndType(Uri.fromFile(file), file.getMimeType());
+ getContext().startActivity(intent);
+ }
+
public interface OnContactPictureClicked {
public void onContactPictureClicked(Message message);
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
index 30e1c7da..3208cab4 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
@@ -11,6 +11,7 @@ import java.util.concurrent.ConcurrentHashMap;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.net.Uri;
+import android.os.SystemClock;
import android.util.Log;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account;
@@ -29,9 +30,6 @@ import eu.siacs.conversations.xmpp.stanzas.IqPacket;
public class JingleConnection implements Downloadable {
- private final String[] extensions = { "webp", "jpeg", "jpg", "png" };
- private final String[] cryptoExtensions = { "pgp", "gpg", "otr" };
-
private JingleConnectionManager mJingleConnectionManager;
private XmppConnectionService mXmppConnectionService;
@@ -62,6 +60,9 @@ public class JingleConnection implements Downloadable {
private String contentName;
private String contentCreator;
+ private int mProgress = 0;
+ private long mLastGuiRefresh = 0;
+
private boolean receivedCandidate = false;
private boolean sentCandidate = false;
@@ -258,7 +259,6 @@ public class JingleConnection implements Downloadable {
packet.getFrom().toBareJid(), false);
this.message = new Message(conversation, "", Message.ENCRYPTION_NONE);
this.message.setStatus(Message.STATUS_RECEIVED);
- this.message.setType(Message.TYPE_IMAGE);
this.mStatus = Downloadable.STATUS_OFFER;
this.message.setDownloadable(this);
final Jid from = packet.getFrom();
@@ -278,68 +278,71 @@ public class JingleConnection implements Downloadable {
Element fileSize = fileOffer.findChild("size");
Element fileNameElement = fileOffer.findChild("name");
if (fileNameElement != null) {
- boolean supportedFile = false;
String[] filename = fileNameElement.getContent()
.toLowerCase(Locale.US).split("\\.");
- if (Arrays.asList(this.extensions).contains(
+ if (Arrays.asList(VALID_IMAGE_EXTENSIONS).contains(
filename[filename.length - 1])) {
- supportedFile = true;
- } else if (Arrays.asList(this.cryptoExtensions).contains(
+ message.setType(Message.TYPE_IMAGE);
+ } else if (Arrays.asList(VALID_CRYPTO_EXTENSIONS).contains(
filename[filename.length - 1])) {
if (filename.length == 3) {
- if (Arrays.asList(this.extensions).contains(
+ if (Arrays.asList(VALID_IMAGE_EXTENSIONS).contains(
filename[filename.length - 2])) {
- supportedFile = true;
- if (filename[filename.length - 1].equals("otr")) {
- Log.d(Config.LOGTAG, "receiving otr file");
- this.message
- .setEncryption(Message.ENCRYPTION_OTR);
- } else {
- this.message
- .setEncryption(Message.ENCRYPTION_PGP);
- }
+ message.setType(Message.TYPE_IMAGE);
+ } else {
+ message.setType(Message.TYPE_FILE);
}
- }
- }
- if (supportedFile) {
- long size = Long.parseLong(fileSize.getContent());
- message.setBody(Long.toString(size));
- conversation.add(message);
- mXmppConnectionService.updateConversationUi();
- if (size <= this.mJingleConnectionManager
- .getAutoAcceptFileSize()) {
- Log.d(Config.LOGTAG, "auto accepting file from "
- + packet.getFrom());
- this.acceptedAutomatically = true;
- this.sendAccept();
- } else {
- message.markUnread();
- Log.d(Config.LOGTAG,
- "not auto accepting new file offer with size: "
- + size
- + " allowed size:"
- + this.mJingleConnectionManager
- .getAutoAcceptFileSize());
- this.mXmppConnectionService.getNotificationService()
- .push(message);
- }
- this.file = this.mXmppConnectionService.getFileBackend()
- .getFile(message, false);
- if (message.getEncryption() == Message.ENCRYPTION_OTR) {
- byte[] key = conversation.getSymmetricKey();
- if (key == null) {
- this.sendCancel();
- this.cancel();
- return;
+ if (filename[filename.length - 1].equals("otr")) {
+ message.setEncryption(Message.ENCRYPTION_OTR);
} else {
- this.file.setKey(key);
+ message.setEncryption(Message.ENCRYPTION_PGP);
}
}
- this.file.setExpectedSize(size);
} else {
- this.sendCancel();
- this.cancel();
+ message.setType(Message.TYPE_FILE);
+ }
+ if (message.getType() == Message.TYPE_FILE) {
+ String suffix = "";
+ if (!fileNameElement.getContent().isEmpty()) {
+ String parts[] = fileNameElement.getContent().split("/");
+ suffix = parts[parts.length - 1];
+ }
+ message.setRelativeFilePath(message.getUuid()+"_"+suffix);
}
+ long size = Long.parseLong(fileSize.getContent());
+ message.setBody(Long.toString(size));
+ conversation.add(message);
+ mXmppConnectionService.updateConversationUi();
+ if (size <= this.mJingleConnectionManager
+ .getAutoAcceptFileSize()) {
+ Log.d(Config.LOGTAG, "auto accepting file from "
+ + packet.getFrom());
+ this.acceptedAutomatically = true;
+ this.sendAccept();
+ } else {
+ message.markUnread();
+ Log.d(Config.LOGTAG,
+ "not auto accepting new file offer with size: "
+ + size
+ + " allowed size:"
+ + this.mJingleConnectionManager
+ .getAutoAcceptFileSize());
+ this.mXmppConnectionService.getNotificationService()
+ .push(message);
+ }
+ this.file = this.mXmppConnectionService.getFileBackend()
+ .getFile(message, false);
+ if (message.getEncryption() == Message.ENCRYPTION_OTR) {
+ byte[] key = conversation.getSymmetricKey();
+ if (key == null) {
+ this.sendCancel();
+ this.cancel();
+ return;
+ } else {
+ this.file.setKey(key);
+ }
+ }
+ this.file.setExpectedSize(size);
} else {
this.sendCancel();
this.cancel();
@@ -354,7 +357,7 @@ public class JingleConnection implements Downloadable {
this.mXmppConnectionService.markMessage(this.message, Message.STATUS_OFFERED);
JinglePacket packet = this.bootstrapPacket("session-initiate");
Content content = new Content(this.contentCreator, this.contentName);
- if (message.getType() == Message.TYPE_IMAGE) {
+ if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) {
content.setTransportId(this.transportId);
this.file = this.mXmppConnectionService.getFileBackend().getFile(
message, false);
@@ -856,6 +859,14 @@ public class JingleConnection implements Downloadable {
return null;
}
+ public void updateProgress(int i) {
+ this.mProgress = i;
+ if (SystemClock.elapsedRealtime() - this.mLastGuiRefresh > 1000) {
+ this.mLastGuiRefresh = SystemClock.elapsedRealtime();
+ mXmppConnectionService.updateConversationUi();
+ }
+ }
+
interface OnProxyActivated {
public void success();
@@ -900,4 +911,14 @@ public class JingleConnection implements Downloadable {
return 0;
}
}
+
+ @Override
+ public int getProgress() {
+ return this.mProgress;
+ }
+
+ @Override
+ public String getMimeType() {
+ return this.file.getMimeType();
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java
index 83b597eb..8d0a188d 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java
@@ -15,6 +15,7 @@ import eu.siacs.conversations.utils.CryptoHelper;
public class JingleSocks5Transport extends JingleTransport {
private JingleCandidate candidate;
+ private JingleConnection connection;
private String destination;
private OutputStream outputStream;
private InputStream inputStream;
@@ -25,6 +26,7 @@ public class JingleSocks5Transport extends JingleTransport {
public JingleSocks5Transport(JingleConnection jingleConnection,
JingleCandidate candidate) {
this.candidate = candidate;
+ this.connection = jingleConnection;
try {
MessageDigest mDigest = MessageDigest.getInstance("SHA-1");
StringBuilder destBuilder = new StringBuilder();
@@ -102,11 +104,15 @@ public class JingleSocks5Transport extends JingleTransport {
callback.onFileTransferAborted();
return;
}
+ long size = file.getSize();
+ double transmitted = 0;
int count;
byte[] buffer = new byte[8192];
while ((count = fileInputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, count);
digest.update(buffer, 0, count);
+ transmitted += count;
+ connection.updateProgress((int) (((transmitted) / size) * 100));
}
outputStream.flush();
file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
@@ -151,6 +157,7 @@ public class JingleSocks5Transport extends JingleTransport {
callback.onFileTransferAborted();
return;
}
+ double size = file.getExpectedSize();
long remainingSize = file.getExpectedSize();
byte[] buffer = new byte[8192];
int count = buffer.length;
@@ -164,6 +171,7 @@ public class JingleSocks5Transport extends JingleTransport {
digest.update(buffer, 0, count);
remainingSize -= count;
}
+ connection.updateProgress((int) (((size - remainingSize) / size) * 100));
}
fileOutputStream.flush();
fileOutputStream.close();
diff --git a/src/main/res/menu/attachment_choices.xml b/src/main/res/menu/attachment_choices.xml
index 20932489..12b37c08 100644
--- a/src/main/res/menu/attachment_choices.xml
+++ b/src/main/res/menu/attachment_choices.xml
@@ -9,7 +9,6 @@
android:title="@string/attach_take_picture"/>
<item
android:id="@+id/attach_record_voice"
- android:title="@string/attach_record_voice"
- android:visible="false"/>
+ android:title="@string/choose_file"/>
</menu> \ No newline at end of file
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index 414de73b..e51fbd18 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -58,7 +58,7 @@
<string name="add_contact">Add contact</string>
<string name="send_failed">delivery failed</string>
<string name="send_rejected">rejected</string>
- <string name="receiving_image">Receiving image file. Please wait…</string>
+ <string name="receiving_image">Receiving image file (%1$d%%)</string>
<string name="preparing_image">Preparing image for transmission</string>
<string name="action_clear_history">Clear history</string>
<string name="clear_conversation_history">Clear Conversation History</string>
@@ -311,4 +311,8 @@
<string name="scan_qr_code">Scan QR code</string>
<string name="show_qr_code">Show QR code</string>
<string name="account_details">Account details</string>
+ <string name="choose_file">Choose file</string>
+ <string name="receiving_file">Receiving %1$s file (%2$d%%)</string>
+ <string name="download_file">Download %s file</string>
+ <string name="open_file">Open %s file</string>
</resources>