basic arbitrary file transfer
This commit is contained in:
parent
4c504dea7a
commit
7a90ca429b
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 "";
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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,6 +56,14 @@ public class FileBackend {
|
|||
}
|
||||
|
||||
public DownloadableFile getFile(Message message, boolean decrypted) {
|
||||
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 {
|
||||
StringBuilder filename = new StringBuilder();
|
||||
filename.append(getConversationsDirectory());
|
||||
filename.append(message.getUuid());
|
||||
|
@ -67,6 +78,7 @@ public class FileBackend {
|
|||
}
|
||||
return new DownloadableFile(filename.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public static String getConversationsDirectory() {
|
||||
return Environment.getExternalStoragePublicDirectory(
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 "";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -306,13 +310,16 @@ public class ConversationActivity extends XmppActivity implements
|
|||
Intent attachFileIntent = new Intent();
|
||||
attachFileIntent.setType("image/*");
|
||||
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) {
|
||||
|
|
|
@ -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);
|
||||
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 {
|
||||
displayDecryptionFailed(viewHolder);
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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,30 +278,37 @@ 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);
|
||||
message.setType(Message.TYPE_IMAGE);
|
||||
} else {
|
||||
this.message
|
||||
.setEncryption(Message.ENCRYPTION_PGP);
|
||||
message.setType(Message.TYPE_FILE);
|
||||
}
|
||||
if (filename[filename.length - 1].equals("otr")) {
|
||||
message.setEncryption(Message.ENCRYPTION_OTR);
|
||||
} else {
|
||||
message.setEncryption(Message.ENCRYPTION_PGP);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
message.setType(Message.TYPE_FILE);
|
||||
}
|
||||
if (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);
|
||||
}
|
||||
if (supportedFile) {
|
||||
long size = Long.parseLong(fileSize.getContent());
|
||||
message.setBody(Long.toString(size));
|
||||
conversation.add(message);
|
||||
|
@ -344,17 +351,13 @@ public class JingleConnection implements Downloadable {
|
|||
this.sendCancel();
|
||||
this.cancel();
|
||||
}
|
||||
} else {
|
||||
this.sendCancel();
|
||||
this.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
private void sendInitRequest() {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue