aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/eu/siacs
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/eu/siacs')
-rw-r--r--src/main/java/eu/siacs/conversations/Config.java10
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Contact.java2
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Conversation.java4
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Message.java8
-rw-r--r--src/main/java/eu/siacs/conversations/parser/MessageParser.java8
-rw-r--r--src/main/java/eu/siacs/conversations/persistance/FileBackend.java60
-rw-r--r--src/main/java/eu/siacs/conversations/services/NotificationService.java25
-rw-r--r--src/main/java/eu/siacs/conversations/services/XmppConnectionService.java55
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConversationActivity.java138
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConversationFragment.java59
-rw-r--r--src/main/java/eu/siacs/conversations/ui/EditMessage.java7
-rw-r--r--src/main/java/eu/siacs/conversations/ui/SettingsActivity.java2
-rw-r--r--src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java32
-rw-r--r--src/main/java/eu/siacs/conversations/utils/CryptoHelper.java16
-rw-r--r--src/main/java/eu/siacs/conversations/utils/GeoHelper.java71
-rw-r--r--src/main/java/eu/siacs/conversations/utils/UIHelper.java8
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java7
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java30
18 files changed, 409 insertions, 133 deletions
diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java
index 41e8971f..f38bcbfc 100644
--- a/src/main/java/eu/siacs/conversations/Config.java
+++ b/src/main/java/eu/siacs/conversations/Config.java
@@ -27,6 +27,7 @@ public final class Config {
public static final int REFRESH_UI_INTERVAL = 500;
public static final boolean NO_PROXY_LOOKUP = false; //useful to debug ibb
+ public static final boolean DISABLE_STRING_PREP = false; // setting to true might increase startup performance
public static final long MILLISECONDS_IN_DAY = 24 * 60 * 60 * 1000;
public static final long MAM_MAX_CATCHUP = MILLISECONDS_IN_DAY / 2;
@@ -63,6 +64,15 @@ public final class Config {
"TLS_RSA_WITH_AES_256_CBC_SHA",
};
+ public static final String WEAK_CIPHER_PATTERNS[] = {
+ "_NULL_",
+ "_EXPORT_",
+ "_anon_",
+ "_RC4_",
+ "_DES_",
+ "_MD5",
+ };
+
private Config() {
}
diff --git a/src/main/java/eu/siacs/conversations/entities/Contact.java b/src/main/java/eu/siacs/conversations/entities/Contact.java
index 698e0322..cef03ebe 100644
--- a/src/main/java/eu/siacs/conversations/entities/Contact.java
+++ b/src/main/java/eu/siacs/conversations/entities/Contact.java
@@ -80,7 +80,7 @@ public class Contact implements ListItem, Blockable {
cursor.getLong(cursor.getColumnIndex(LAST_TIME)));
final Jid jid;
try {
- jid = Jid.fromString(cursor.getString(cursor.getColumnIndex(JID)));
+ jid = Jid.fromString(cursor.getString(cursor.getColumnIndex(JID)), true);
} catch (final InvalidJidException e) {
// TODO: Borked DB... handle this somehow?
return null;
diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java
index 99823e4e..c150fb91 100644
--- a/src/main/java/eu/siacs/conversations/entities/Conversation.java
+++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java
@@ -2,10 +2,8 @@ package eu.siacs.conversations.entities;
import android.content.ContentValues;
import android.database.Cursor;
-import android.os.SystemClock;
import net.java.otr4j.OtrException;
-import net.java.otr4j.crypto.OtrCryptoEngineImpl;
import net.java.otr4j.crypto.OtrCryptoException;
import net.java.otr4j.session.SessionID;
import net.java.otr4j.session.SessionImpl;
@@ -371,7 +369,7 @@ public class Conversation extends AbstractEntity implements Blockable {
public static Conversation fromCursor(Cursor cursor) {
Jid jid;
try {
- jid = Jid.fromString(cursor.getString(cursor.getColumnIndex(CONTACTJID)));
+ jid = Jid.fromString(cursor.getString(cursor.getColumnIndex(CONTACTJID)), true);
} catch (final InvalidJidException e) {
// Borked DB..
jid = null;
diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java
index 7ad43d53..8015eead 100644
--- a/src/main/java/eu/siacs/conversations/entities/Message.java
+++ b/src/main/java/eu/siacs/conversations/entities/Message.java
@@ -8,6 +8,7 @@ import java.net.URL;
import java.util.Arrays;
import eu.siacs.conversations.Config;
+import eu.siacs.conversations.utils.GeoHelper;
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid;
@@ -49,6 +50,7 @@ public class Message extends AbstractEntity {
public static final String RELATIVE_FILE_PATH = "relativeFilePath";
public static final String ME_COMMAND = "/me ";
+
public boolean markable = false;
protected String conversationUuid;
protected Jid counterpart;
@@ -115,7 +117,7 @@ public class Message extends AbstractEntity {
try {
String value = cursor.getString(cursor.getColumnIndex(COUNTERPART));
if (value != null) {
- jid = Jid.fromString(value);
+ jid = Jid.fromString(value, true);
} else {
jid = null;
}
@@ -126,7 +128,7 @@ public class Message extends AbstractEntity {
try {
String value = cursor.getString(cursor.getColumnIndex(TRUE_COUNTERPART));
if (value != null) {
- trueCounterpart = Jid.fromString(value);
+ trueCounterpart = Jid.fromString(value, true);
} else {
trueCounterpart = null;
}
@@ -368,6 +370,8 @@ public class Message extends AbstractEntity {
this.getCounterpart() != null &&
this.getCounterpart().equals(message.getCounterpart()) &&
(message.getTimeSent() - this.getTimeSent()) <= (Config.MESSAGE_MERGE_WINDOW * 1000) &&
+ !GeoHelper.isGeoUri(message.getBody()) &&
+ !GeoHelper.isGeoUri(this.body) &&
!message.bodyContainsDownloadable() &&
!this.bodyContainsDownloadable() &&
!message.getBody().startsWith(ME_COMMAND) &&
diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
index ced81520..6652842d 100644
--- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
@@ -143,7 +143,7 @@ public class MessageParser extends AbstractParser implements
finishedMessage.setRemoteMsgId(packet.getId());
finishedMessage.markable = isMarkable(packet);
finishedMessage.setCounterpart(from);
- extractChatState(conversation,packet);
+ extractChatState(conversation, packet);
return finishedMessage;
} catch (Exception e) {
conversation.resetOtrSession();
@@ -408,11 +408,11 @@ public class MessageParser extends AbstractParser implements
.getAttribute("id");
updateLastseen(packet, account, true);
final Message displayedMessage = mXmppConnectionService.markMessage(account, from.toBareJid(), id, Message.STATUS_SEND_DISPLAYED);
- Message message = displayedMessage.prev();
- while(message != null
+ Message message = displayedMessage == null ? null :displayedMessage.prev();
+ while (message != null
&& message.getStatus() == Message.STATUS_SEND_RECEIVED
&& message.getTimeSent() < displayedMessage.getTimeSent()) {
- mXmppConnectionService.markMessage(message,Message.STATUS_SEND_DISPLAYED);
+ mXmppConnectionService.markMessage(message, Message.STATUS_SEND_DISPLAYED);
message = message.prev();
}
} else if (from != null
diff --git a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java
index 62987aaa..c499d499 100644
--- a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java
+++ b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java
@@ -318,39 +318,41 @@ public class FileBackend {
}
public boolean save(Avatar avatar) {
+ File file;
if (isAvatarCached(avatar)) {
- return true;
- }
- String filename = getAvatarPath(avatar.getFilename());
- File file = new File(filename + ".tmp");
- file.getParentFile().mkdirs();
- try {
- file.createNewFile();
- FileOutputStream mFileOutputStream = new FileOutputStream(file);
- MessageDigest digest = MessageDigest.getInstance("SHA-1");
- digest.reset();
- DigestOutputStream mDigestOutputStream = new DigestOutputStream(
- mFileOutputStream, digest);
- mDigestOutputStream.write(avatar.getImageAsBytes());
- mDigestOutputStream.flush();
- mDigestOutputStream.close();
- avatar.size = file.length();
- String sha1sum = CryptoHelper.bytesToHex(digest.digest());
- if (sha1sum.equals(avatar.sha1sum)) {
- file.renameTo(new File(filename));
- return true;
- } else {
- Log.d(Config.LOGTAG, "sha1sum mismatch for " + avatar.owner);
- file.delete();
+ file = new File(getAvatarPath(avatar.getFilename()));
+ } else {
+ String filename = getAvatarPath(avatar.getFilename());
+ file = new File(filename + ".tmp");
+ file.getParentFile().mkdirs();
+ try {
+ file.createNewFile();
+ FileOutputStream mFileOutputStream = new FileOutputStream(file);
+ MessageDigest digest = MessageDigest.getInstance("SHA-1");
+ digest.reset();
+ DigestOutputStream mDigestOutputStream = new DigestOutputStream(
+ mFileOutputStream, digest);
+ mDigestOutputStream.write(avatar.getImageAsBytes());
+ mDigestOutputStream.flush();
+ mDigestOutputStream.close();
+ String sha1sum = CryptoHelper.bytesToHex(digest.digest());
+ if (sha1sum.equals(avatar.sha1sum)) {
+ file.renameTo(new File(filename));
+ } else {
+ Log.d(Config.LOGTAG, "sha1sum mismatch for " + avatar.owner);
+ file.delete();
+ return false;
+ }
+ } catch (FileNotFoundException e) {
+ return false;
+ } catch (IOException e) {
+ return false;
+ } catch (NoSuchAlgorithmException e) {
return false;
}
- } catch (FileNotFoundException e) {
- return false;
- } catch (IOException e) {
- return false;
- } catch (NoSuchAlgorithmException e) {
- return false;
}
+ avatar.size = file.length();
+ return true;
}
public String getAvatarPath(String avatar) {
diff --git a/src/main/java/eu/siacs/conversations/services/NotificationService.java b/src/main/java/eu/siacs/conversations/services/NotificationService.java
index 6e4f9981..7269a559 100644
--- a/src/main/java/eu/siacs/conversations/services/NotificationService.java
+++ b/src/main/java/eu/siacs/conversations/services/NotificationService.java
@@ -40,6 +40,7 @@ import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.ui.ConversationActivity;
import eu.siacs.conversations.ui.ManageAccountActivity;
import eu.siacs.conversations.ui.TimePreference;
+import eu.siacs.conversations.utils.GeoHelper;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.XmppConnection;
@@ -279,6 +280,11 @@ public class NotificationService {
createDownloadIntent(message)
);
}
+ if ((message = getFirstLocationMessage(messages)) != null) {
+ mBuilder.addAction(R.drawable.ic_room_white_24dp,
+ mXmppConnectionService.getString(R.string.show_location),
+ createShowLocationIntent(message));
+ }
mBuilder.setContentIntent(createContentIntent(conversation));
}
return mBuilder;
@@ -342,6 +348,15 @@ public class NotificationService {
return null;
}
+ private Message getFirstLocationMessage(final Iterable<Message> messages) {
+ for(final Message message : messages) {
+ if (GeoHelper.isGeoUri(message.getBody())) {
+ return message;
+ }
+ }
+ return null;
+ }
+
private CharSequence getMergedBodies(final ArrayList<Message> messages) {
final StringBuilder text = new StringBuilder();
for (int i = 0; i < messages.size(); ++i) {
@@ -353,6 +368,16 @@ public class NotificationService {
return text.toString();
}
+ private PendingIntent createShowLocationIntent(final Message message) {
+ Iterable<Intent> intents = GeoHelper.createGeoIntentsFromMessage(message);
+ for(Intent intent : intents) {
+ if (intent.resolveActivity(mXmppConnectionService.getPackageManager()) != null) {
+ return PendingIntent.getActivity(mXmppConnectionService,18,intent,PendingIntent.FLAG_UPDATE_CURRENT);
+ }
+ }
+ return createOpenConversationsIntent();
+ }
+
private PendingIntent createContentIntent(final String conversationUuid, final String downloadMessageUuid) {
final TaskStackBuilder stackBuilder = TaskStackBuilder
.create(mXmppConnectionService);
diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
index f6e479c7..188f1eb9 100644
--- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
@@ -306,6 +306,24 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
return this.mAvatarService;
}
+ public void attachLocationToConversation(final Conversation conversation,
+ final Uri uri,
+ final UiCallback<Message> callback) {
+ int encryption = conversation.getNextEncryption(forceEncryption());
+ if (encryption == Message.ENCRYPTION_PGP) {
+ encryption = Message.ENCRYPTION_DECRYPTED;
+ }
+ Message message = new Message(conversation,uri.toString(),encryption);
+ if (conversation.getNextCounterpart() != null) {
+ message.setCounterpart(conversation.getNextCounterpart());
+ }
+ if (encryption == Message.ENCRYPTION_DECRYPTED) {
+ getPgpEngine().encrypt(message,callback);
+ } else {
+ callback.success(message);
+ }
+ }
+
public void attachFileToConversation(final Conversation conversation,
final Uri uri,
final UiCallback<Message> callback) {
@@ -1136,7 +1154,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
account.initOtrEngine(this);
databaseBackend.createAccount(account);
this.accounts.add(account);
- this.reconnectAccount(account, false);
+ this.reconnectAccountInBackground(account);
updateAccountUi();
}
@@ -1972,24 +1990,29 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
public void reconnectAccount(final Account account, final boolean force) {
- new Thread(new Runnable() {
+ synchronized (account) {
+ if (account.getXmppConnection() != null) {
+ disconnect(account, force);
+ }
+ if (!account.isOptionSet(Account.OPTION_DISABLED)) {
+ if (account.getXmppConnection() == null) {
+ account.setXmppConnection(createConnection(account));
+ }
+ Thread thread = new Thread(account.getXmppConnection());
+ thread.start();
+ scheduleWakeUpCall(Config.CONNECT_TIMEOUT, account.getUuid().hashCode());
+ } else {
+ account.getRoster().clearPresences();
+ account.setXmppConnection(null);
+ }
+ }
+ }
+ public void reconnectAccountInBackground(final Account account) {
+ new Thread(new Runnable() {
@Override
public void run() {
- if (account.getXmppConnection() != null) {
- disconnect(account, force);
- }
- if (!account.isOptionSet(Account.OPTION_DISABLED)) {
- if (account.getXmppConnection() == null) {
- account.setXmppConnection(createConnection(account));
- }
- Thread thread = new Thread(account.getXmppConnection());
- thread.start();
- scheduleWakeUpCall(Config.CONNECT_TIMEOUT, account.getUuid().hashCode());
- } else {
- account.getRoster().clearPresences();
- account.setXmppConnection(null);
- }
+ reconnectAccount(account,false);
}
}).start();
}
diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
index ad2aab56..82afda07 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
@@ -33,7 +33,6 @@ import java.util.ArrayList;
import java.util.List;
import eu.siacs.conversations.R;
-import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Blockable;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
@@ -63,6 +62,7 @@ public class ConversationActivity extends XmppActivity
private static final int ATTACHMENT_CHOICE_TAKE_PHOTO = 0x0302;
private static final int ATTACHMENT_CHOICE_CHOOSE_FILE = 0x0303;
private static final int ATTACHMENT_CHOICE_RECORD_VOICE = 0x0304;
+ private static final int ATTACHMENT_CHOICE_LOCATION = 0x0305;
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";
@@ -71,6 +71,7 @@ public class ConversationActivity extends XmppActivity
private boolean mPanelOpen = true;
private Uri mPendingImageUri = null;
private Uri mPendingFileUri = null;
+ private Uri mPendingGeoUri = null;
private View mContentView;
@@ -313,7 +314,6 @@ public class ConversationActivity extends XmppActivity
menuInviteContact.setVisible(getSelectedConversation().getMucOptions().canInvite());
} else {
menuMucDetails.setVisible(false);
- final Account account = this.getSelectedConversation().getAccount();
}
if (this.getSelectedConversation().isMuted()) {
menuMute.setVisible(false);
@@ -325,50 +325,60 @@ public class ConversationActivity extends XmppActivity
return true;
}
- private void selectPresenceToAttachFile(final int attachmentChoice) {
- selectPresence(getSelectedConversation(), new OnPresenceSelected() {
+ private void selectPresenceToAttachFile(final int attachmentChoice, final int encryption) {
+ if (attachmentChoice == ATTACHMENT_CHOICE_LOCATION && encryption != Message.ENCRYPTION_OTR) {
+ getSelectedConversation().setNextCounterpart(null);
+ Intent intent = new Intent("eu.siacs.conversations.location.request");
+ startActivityForResult(intent,attachmentChoice);
+ } else {
+ selectPresence(getSelectedConversation(), new OnPresenceSelected() {
- @Override
- public void onPresenceSelected() {
- Intent intent = new Intent();
- boolean chooser = false;
- switch (attachmentChoice) {
- case ATTACHMENT_CHOICE_CHOOSE_IMAGE:
- intent.setAction(Intent.ACTION_GET_CONTENT);
- intent.setType("image/*");
- chooser = true;
- break;
- case ATTACHMENT_CHOICE_TAKE_PHOTO:
- mPendingImageUri = xmppConnectionService.getFileBackend().getTakePhotoUri();
- intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
- intent.putExtra(MediaStore.EXTRA_OUTPUT,mPendingImageUri);
- break;
- case ATTACHMENT_CHOICE_CHOOSE_FILE:
- chooser = true;
- intent.setType("*/*");
- intent.addCategory(Intent.CATEGORY_OPENABLE);
- intent.setAction(Intent.ACTION_GET_CONTENT);
- break;
- case ATTACHMENT_CHOICE_RECORD_VOICE:
- intent.setAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
- break;
- }
- if (intent.resolveActivity(getPackageManager()) != null) {
- if (chooser) {
- startActivityForResult(
- Intent.createChooser(intent,getString(R.string.perform_action_with)),
- attachmentChoice);
- } else {
- startActivityForResult(intent, attachmentChoice);
+ @Override
+ public void onPresenceSelected() {
+ Intent intent = new Intent();
+ boolean chooser = false;
+ switch (attachmentChoice) {
+ case ATTACHMENT_CHOICE_CHOOSE_IMAGE:
+ intent.setAction(Intent.ACTION_GET_CONTENT);
+ intent.setType("image/*");
+ chooser = true;
+ break;
+ case ATTACHMENT_CHOICE_TAKE_PHOTO:
+ mPendingImageUri = xmppConnectionService.getFileBackend().getTakePhotoUri();
+ intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
+ intent.putExtra(MediaStore.EXTRA_OUTPUT, mPendingImageUri);
+ break;
+ case ATTACHMENT_CHOICE_CHOOSE_FILE:
+ chooser = true;
+ intent.setType("*/*");
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ intent.setAction(Intent.ACTION_GET_CONTENT);
+ break;
+ case ATTACHMENT_CHOICE_RECORD_VOICE:
+ intent.setAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
+ break;
+ case ATTACHMENT_CHOICE_LOCATION:
+ intent.setAction("eu.siacs.conversations.location.request");
+ break;
+ }
+ if (intent.resolveActivity(getPackageManager()) != null) {
+ if (chooser) {
+ startActivityForResult(
+ Intent.createChooser(intent, getString(R.string.perform_action_with)),
+ attachmentChoice);
+ } else {
+ startActivityForResult(intent, attachmentChoice);
+ }
}
}
- }
- });
+ });
+ }
}
private void attachFile(final int attachmentChoice) {
final Conversation conversation = getSelectedConversation();
- if (conversation.getNextEncryption(forceEncryption()) == Message.ENCRYPTION_PGP) {
+ final int encryption = conversation.getNextEncryption(forceEncryption());
+ if (encryption == Message.ENCRYPTION_PGP) {
if (hasPgp()) {
if (conversation.getContact().getPgpKeyId() != 0) {
xmppConnectionService.getPgpEngine().hasKey(
@@ -378,13 +388,12 @@ public class ConversationActivity extends XmppActivity
@Override
public void userInputRequried(PendingIntent pi,
Contact contact) {
- ConversationActivity.this.runIntent(pi,
- attachmentChoice);
+ ConversationActivity.this.runIntent(pi,attachmentChoice);
}
@Override
public void success(Contact contact) {
- selectPresenceToAttachFile(attachmentChoice);
+ selectPresenceToAttachFile(attachmentChoice,encryption);
}
@Override
@@ -406,7 +415,7 @@ public class ConversationActivity extends XmppActivity
.setNextEncryption(Message.ENCRYPTION_NONE);
xmppConnectionService.databaseBackend
.updateConversation(conversation);
- selectPresenceToAttachFile(attachmentChoice);
+ selectPresenceToAttachFile(attachmentChoice,Message.ENCRYPTION_NONE);
}
});
}
@@ -414,11 +423,8 @@ public class ConversationActivity extends XmppActivity
} else {
showInstallPgpDialog();
}
- } else if (getSelectedConversation().getNextEncryption(
- forceEncryption()) == Message.ENCRYPTION_NONE) {
- selectPresenceToAttachFile(attachmentChoice);
} else {
- selectPresenceToAttachFile(attachmentChoice);
+ selectPresenceToAttachFile(attachmentChoice,encryption);
}
}
@@ -526,6 +532,9 @@ public class ConversationActivity extends XmppActivity
if (new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION).resolveActivity(getPackageManager()) == null) {
attachFilePopup.getMenu().findItem(R.id.attach_record_voice).setVisible(false);
}
+ if (new Intent("eu.siacs.conversations.location.request").resolveActivity(getPackageManager()) == null) {
+ attachFilePopup.getMenu().findItem(R.id.attach_location).setVisible(false);
+ }
attachFilePopup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
@Override
@@ -543,6 +552,9 @@ public class ConversationActivity extends XmppActivity
case R.id.attach_record_voice:
attachFile(ATTACHMENT_CHOICE_RECORD_VOICE);
break;
+ case R.id.attach_location:
+ attachFile(ATTACHMENT_CHOICE_LOCATION);
+ break;
}
return false;
}
@@ -809,6 +821,7 @@ public class ConversationActivity extends XmppActivity
showConversationsOverview();
mPendingImageUri = null;
mPendingFileUri = null;
+ mPendingGeoUri = null;
setSelectedConversation(conversationList.get(0));
this.mConversationFragment.reInit(getSelectedConversation());
}
@@ -819,6 +832,9 @@ public class ConversationActivity extends XmppActivity
} else if (mPendingFileUri != null) {
attachFileToConversation(getSelectedConversation(),mPendingFileUri);
mPendingFileUri = null;
+ } else if (mPendingGeoUri != null) {
+ attachLocationToConversation(getSelectedConversation(),mPendingGeoUri);
+ mPendingGeoUri = null;
}
ExceptionHelper.checkForCrash(this, this.xmppConnectionService);
setIntent(new Intent());
@@ -897,6 +913,14 @@ public class ConversationActivity extends XmppActivity
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intent.setData(mPendingImageUri);
sendBroadcast(intent);
+ } else if (requestCode == ATTACHMENT_CHOICE_LOCATION) {
+ double latitude = data.getDoubleExtra("latitude",0);
+ double longitude = data.getDoubleExtra("longitude",0);
+ this.mPendingGeoUri = Uri.parse("geo:"+String.valueOf(latitude)+","+String.valueOf(longitude));
+ if (xmppConnectionServiceBound) {
+ attachLocationToConversation(getSelectedConversation(), mPendingGeoUri);
+ this.mPendingGeoUri = null;
+ }
}
} else {
if (requestCode == ATTACHMENT_CHOICE_TAKE_PHOTO) {
@@ -905,6 +929,26 @@ public class ConversationActivity extends XmppActivity
}
}
+ private void attachLocationToConversation(Conversation conversation, Uri uri) {
+ xmppConnectionService.attachLocationToConversation(conversation,uri, new UiCallback<Message>() {
+
+ @Override
+ public void success(Message message) {
+ xmppConnectionService.sendMessage(message);
+ }
+
+ @Override
+ public void error(int errorCode, Message object) {
+
+ }
+
+ @Override
+ public void userInputRequried(PendingIntent pi, Message object) {
+
+ }
+ });
+ }
+
private void attachFileToConversation(Conversation conversation, Uri uri) {
prepareFileToast = Toast.makeText(getApplicationContext(),
getText(R.string.preparing_file), Toast.LENGTH_LONG);
diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
index 3e0668be..d5f20e41 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
@@ -8,6 +8,7 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentSender;
import android.content.IntentSender.SendIntentException;
+import android.net.Uri;
import android.os.Bundle;
import android.text.InputType;
import android.view.ContextMenu;
@@ -58,6 +59,7 @@ import eu.siacs.conversations.ui.XmppActivity.OnValueEdited;
import eu.siacs.conversations.ui.adapter.MessageAdapter;
import eu.siacs.conversations.ui.adapter.MessageAdapter.OnContactPictureClicked;
import eu.siacs.conversations.ui.adapter.MessageAdapter.OnContactPictureLongClicked;
+import eu.siacs.conversations.utils.GeoHelper;
import eu.siacs.conversations.xmpp.chatstate.ChatState;
import eu.siacs.conversations.xmpp.jid.Jid;
@@ -410,19 +412,20 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
MenuItem downloadImage = menu.findItem(R.id.download_image);
MenuItem cancelTransmission = menu.findItem(R.id.cancel_transmission);
if ((m.getType() != Message.TYPE_TEXT && m.getType() != Message.TYPE_PRIVATE)
- || m.getDownloadable() != null) {
+ || m.getDownloadable() != null || GeoHelper.isGeoUri(m.getBody())) {
copyText.setVisible(false);
}
- if (m.getType() == Message.TYPE_TEXT
+ if ((m.getType() == Message.TYPE_TEXT
|| m.getType() == Message.TYPE_PRIVATE
- || m.getDownloadable() != null) {
+ || m.getDownloadable() != null)
+ && (!GeoHelper.isGeoUri(m.getBody()))) {
shareWith.setVisible(false);
- }
+ }
if (m.getStatus() != Message.STATUS_SEND_FAILED) {
sendAgain.setVisible(false);
}
- if ((m.getType() != Message.TYPE_IMAGE && m.getDownloadable() == null)
- || m.getImageParams().url == null) {
+ if (((m.getType() != Message.TYPE_IMAGE && m.getDownloadable() == null)
+ || m.getImageParams().url == null) && !GeoHelper.isGeoUri(m.getBody())) {
copyUrl.setVisible(false);
}
if (m.getType() != Message.TYPE_TEXT
@@ -467,16 +470,21 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
private void shareWith(Message message) {
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
- shareIntent.putExtra(Intent.EXTRA_STREAM,
- activity.xmppConnectionService.getFileBackend()
- .getJingleFileUri(message));
- shareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- String path = message.getRelativeFilePath();
- String mime = path == null ? null :URLConnection.guessContentTypeFromName(path);
- if (mime == null) {
- mime = "image/webp";
+ if (GeoHelper.isGeoUri(message.getBody())) {
+ shareIntent.putExtra(Intent.EXTRA_TEXT, message.getBody());
+ shareIntent.setType("text/plain");
+ } else {
+ shareIntent.putExtra(Intent.EXTRA_STREAM,
+ activity.xmppConnectionService.getFileBackend()
+ .getJingleFileUri(message));
+ shareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ String path = message.getRelativeFilePath();
+ String mime = path == null ? null : URLConnection.guessContentTypeFromName(path);
+ if (mime == null) {
+ mime = "image/webp";
+ }
+ shareIntent.setType(mime);
}
- shareIntent.setType(mime);
activity.startActivity(Intent.createChooser(shareIntent,getText(R.string.share_with)));
}
@@ -501,8 +509,16 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
}
private void copyUrl(Message message) {
- if (activity.copyTextToClipboard(
- message.getImageParams().url.toString(), R.string.image_url)) {
+ final String url;
+ final int resId;
+ if (GeoHelper.isGeoUri(message.getBody())) {
+ resId = R.string.location;
+ url = message.getBody();
+ } else {
+ resId = R.string.image_url;
+ url = message.getImageParams().url.toString();
+ }
+ if (activity.copyTextToClipboard(url, resId)) {
Toast.makeText(activity, R.string.url_copied_to_clipboard,
Toast.LENGTH_SHORT).show();
}
@@ -1012,8 +1028,13 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
}
@Override
- public void onEnterPressed() {
- sendMessage();
+ public boolean onEnterPressed() {
+ if (activity.enterIsSend()) {
+ sendMessage();
+ return true;
+ } else {
+ return false;
+ }
}
@Override
diff --git a/src/main/java/eu/siacs/conversations/ui/EditMessage.java b/src/main/java/eu/siacs/conversations/ui/EditMessage.java
index 57fa7285..a58cf2b8 100644
--- a/src/main/java/eu/siacs/conversations/ui/EditMessage.java
+++ b/src/main/java/eu/siacs/conversations/ui/EditMessage.java
@@ -37,10 +37,9 @@ public class EditMessage extends EditText {
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_ENTER) {
- if (keyboardListener != null) {
- keyboardListener.onEnterPressed();
+ if (keyboardListener != null && keyboardListener.onEnterPressed()) {
+ return true;
}
- return true;
}
return super.onKeyDown(keyCode, event);
}
@@ -70,7 +69,7 @@ public class EditMessage extends EditText {
}
public interface KeyboardListener {
- public void onEnterPressed();
+ public boolean onEnterPressed();
public void onTypingStarted();
public void onTypingStopped();
public void onTextDeleted();
diff --git a/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java
index 5bf3cdab..39e215f2 100644
--- a/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java
@@ -65,7 +65,7 @@ public class SettingsActivity extends XmppActivity implements
for (Account account : xmppConnectionService.getAccounts()) {
account.setResource(resource);
if (!account.isOptionSet(Account.OPTION_DISABLED)) {
- xmppConnectionService.reconnectAccount(account, false);
+ xmppConnectionService.reconnectAccountInBackground(account);
}
}
}
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 58713534..da92fb18 100644
--- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
+++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
@@ -34,6 +34,7 @@ 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;
+import eu.siacs.conversations.utils.GeoHelper;
import eu.siacs.conversations.utils.UIHelper;
public class MessageAdapter extends ArrayAdapter<Message> {
@@ -299,6 +300,21 @@ public class MessageAdapter extends ArrayAdapter<Message> {
viewHolder.download_button.setOnLongClickListener(openContextMenu);
}
+ private void displayLocationMessage(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(R.string.show_location);
+ viewHolder.download_button.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ showLocation(message);
+ }
+ });
+ viewHolder.download_button.setOnLongClickListener(openContextMenu);
+ }
+
private void displayImageMessage(ViewHolder viewHolder,
final Message message) {
if (viewHolder.download_button != null) {
@@ -509,7 +525,11 @@ public class MessageAdapter extends ArrayAdapter<Message> {
} else if (message.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
displayDecryptionFailed(viewHolder);
} else {
- displayTextMessage(viewHolder, message);
+ if (GeoHelper.isGeoUri(message.getBody())) {
+ displayLocationMessage(viewHolder,message);
+ } else {
+ displayTextMessage(viewHolder, message);
+ }
}
displayStatus(viewHolder, message);
@@ -544,6 +564,16 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}
}
+ public void showLocation(Message message) {
+ for(Intent intent : GeoHelper.createGeoIntentsFromMessage(message)) {
+ if (intent.resolveActivity(getContext().getPackageManager()) != null) {
+ getContext().startActivity(intent);
+ return;
+ }
+ }
+ Toast.makeText(activity,R.string.no_application_found_to_display_location,Toast.LENGTH_SHORT).show();
+ }
+
public interface OnContactPictureClicked {
public void onContactPictureClicked(Message message);
}
diff --git a/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java b/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java
index 31fe2c11..eb7e2c3c 100644
--- a/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java
+++ b/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java
@@ -4,6 +4,7 @@ import java.security.SecureRandom;
import java.text.Normalizer;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
@@ -103,6 +104,21 @@ public final class CryptoHelper {
final List<String> platformCiphers = Arrays.asList(platformSupportedCipherSuites);
cipherSuites.retainAll(platformCiphers);
cipherSuites.addAll(platformCiphers);
+ filterWeakCipherSuites(cipherSuites);
return cipherSuites.toArray(new String[cipherSuites.size()]);
}
+
+ private static void filterWeakCipherSuites(final Collection<String> cipherSuites) {
+ final Iterator<String> it = cipherSuites.iterator();
+ while (it.hasNext()) {
+ String cipherName = it.next();
+ // remove all ciphers with no or very weak encryption or no authentication
+ for (String weakCipherPattern : Config.WEAK_CIPHER_PATTERNS) {
+ if (cipherName.contains(weakCipherPattern)) {
+ it.remove();
+ break;
+ }
+ }
+ }
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/utils/GeoHelper.java b/src/main/java/eu/siacs/conversations/utils/GeoHelper.java
new file mode 100644
index 00000000..f7dda936
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/utils/GeoHelper.java
@@ -0,0 +1,71 @@
+package eu.siacs.conversations.utils;
+
+import android.content.Intent;
+import android.net.Uri;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.entities.Message;
+
+public class GeoHelper {
+ private static Pattern GEO_URI = Pattern.compile("geo:([\\-0-9.]+),([\\-0-9.]+)(?:,([\\-0-9.]+))?(?:\\?(.*))?", Pattern.CASE_INSENSITIVE);
+
+ public static boolean isGeoUri(String body) {
+ return body != null && GEO_URI.matcher(body).matches();
+ }
+
+ public static ArrayList<Intent> createGeoIntentsFromMessage(Message message) {
+ final ArrayList<Intent> intents = new ArrayList();
+ Matcher matcher = GEO_URI.matcher(message.getBody());
+ if (!matcher.matches()) {
+ return intents;
+ }
+ double latitude;
+ double longitude;
+ try {
+ latitude = Double.parseDouble(matcher.group(1));
+ if (latitude > 90.0 || latitude < -90.0) {
+ return intents;
+ }
+ longitude = Double.parseDouble(matcher.group(2));
+ if (longitude > 180.0 || longitude < -180.0) {
+ return intents;
+ }
+ } catch (NumberFormatException nfe) {
+ return intents;
+ }
+ final Conversation conversation = message.getConversation();
+ String label;
+ if (conversation.getMode() == Conversation.MODE_SINGLE && message.getStatus() == Message.STATUS_RECEIVED) {
+ try {
+ label = "(" + URLEncoder.encode(message.getConversation().getName(), "UTF-8") + ")";
+ } catch (UnsupportedEncodingException e) {
+ label = "";
+ }
+ } else {
+ label = "";
+ }
+
+ Intent locationPluginIntent = new Intent("eu.siacs.conversations.location.show");
+ locationPluginIntent.putExtra("latitude",latitude);
+ locationPluginIntent.putExtra("longitude",longitude);
+ if (conversation.getMode() == Conversation.MODE_SINGLE && message.getStatus() == Message.STATUS_RECEIVED) {
+ locationPluginIntent.putExtra("name",conversation.getName());
+ }
+ intents.add(locationPluginIntent);
+
+ Intent geoIntent = new Intent(Intent.ACTION_VIEW);
+ geoIntent.setData(Uri.parse("geo:" + String.valueOf(latitude) + "," + String.valueOf(longitude) + "?q=" + String.valueOf(latitude) + "," + String.valueOf(longitude) + label));
+ intents.add(geoIntent);
+
+ Intent httpIntent = new Intent(Intent.ACTION_VIEW);
+ httpIntent.setData(Uri.parse("https://maps.google.com/maps?q=loc:"+String.valueOf(latitude) + "," + String.valueOf(longitude) +label));
+ intents.add(httpIntent);
+ return intents;
+ }
+}
diff --git a/src/main/java/eu/siacs/conversations/utils/UIHelper.java b/src/main/java/eu/siacs/conversations/utils/UIHelper.java
index e36c169b..c3195d86 100644
--- a/src/main/java/eu/siacs/conversations/utils/UIHelper.java
+++ b/src/main/java/eu/siacs/conversations/utils/UIHelper.java
@@ -153,7 +153,13 @@ public class UIHelper {
if (message.getBody().startsWith(Message.ME_COMMAND)) {
return new Pair<>(message.getBody().replaceAll("^" + Message.ME_COMMAND,
UIHelper.getMessageDisplayName(message) + " "), false);
- } else {
+ } else if (GeoHelper.isGeoUri(message.getBody())) {
+ if (message.getStatus() == Message.STATUS_RECEIVED) {
+ return new Pair<>(context.getString(R.string.received_location),true);
+ } else {
+ return new Pair<>(context.getString(R.string.location), true);
+ }
+ } else{
return new Pair<>(message.getBody().trim(), false);
}
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
index 44c367a6..57a64513 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
@@ -242,6 +242,13 @@ public class XmppConnection implements Runnable {
@Override
public void run() {
+ try {
+ if (socket != null) {
+ socket.close();
+ }
+ } catch (final IOException ignored) {
+
+ }
connect();
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java b/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java
index b8be527b..295e067a 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java
@@ -1,9 +1,12 @@
package eu.siacs.conversations.xmpp.jid;
+import android.util.LruCache;
+
import net.java.otr4j.session.SessionID;
import java.net.IDN;
+import eu.siacs.conversations.Config;
import gnu.inet.encoding.Stringprep;
import gnu.inet.encoding.StringprepException;
@@ -12,6 +15,8 @@ import gnu.inet.encoding.StringprepException;
*/
public final class Jid {
+ private static LruCache<String,Jid> cache = new LruCache<>(1024);
+
private final String localpart;
private final String domainpart;
private final String resourcepart;
@@ -41,7 +46,11 @@ public final class Jid {
}
public static Jid fromString(final String jid) throws InvalidJidException {
- return new Jid(jid);
+ return Jid.fromString(jid, false);
+ }
+
+ public static Jid fromString(final String jid, final boolean safe) throws InvalidJidException {
+ return new Jid(jid, safe);
}
public static Jid fromParts(final String localpart,
@@ -56,12 +65,21 @@ public final class Jid {
if (resourcepart != null && !resourcepart.isEmpty()) {
out = out + "/" + resourcepart;
}
- return new Jid(out);
+ return new Jid(out, false);
}
- private Jid(final String jid) throws InvalidJidException {
+ private Jid(final String jid, final boolean safe) throws InvalidJidException {
if (jid == null) throw new InvalidJidException(InvalidJidException.IS_NULL);
+ Jid fromCache = Jid.cache.get(jid);
+ if (fromCache != null) {
+ displayjid = fromCache.displayjid;
+ localpart = fromCache.localpart;
+ domainpart = fromCache.domainpart;
+ resourcepart = fromCache.resourcepart;
+ return;
+ }
+
// Hackish Android way to count the number of chars in a string... should work everywhere.
final int atCount = jid.length() - jid.replace("@", "").length();
final int slashCount = jid.length() - jid.replace("/", "").length();
@@ -90,7 +108,7 @@ public final class Jid {
} else {
final String lp = jid.substring(0, atLoc);
try {
- localpart = Stringprep.nodeprep(lp);
+ localpart = Config.DISABLE_STRING_PREP || safe ? lp : Stringprep.nodeprep(lp);
} catch (final StringprepException e) {
throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e);
}
@@ -105,7 +123,7 @@ public final class Jid {
if (slashCount > 0) {
final String rp = jid.substring(slashLoc + 1, jid.length());
try {
- resourcepart = Stringprep.resourceprep(rp);
+ resourcepart = Config.DISABLE_STRING_PREP || safe ? rp : Stringprep.resourceprep(rp);
} catch (final StringprepException e) {
throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e);
}
@@ -141,6 +159,8 @@ public final class Jid {
throw new InvalidJidException(InvalidJidException.INVALID_PART_LENGTH);
}
+ Jid.cache.put(jid, this);
+
this.displayjid = finaljid;
}