aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/eu/siacs/conversations/crypto/OtrEngine.java9
-rw-r--r--src/eu/siacs/conversations/crypto/PgpEngine.java37
-rw-r--r--src/eu/siacs/conversations/entities/Account.java187
-rw-r--r--src/eu/siacs/conversations/entities/Contact.java32
-rw-r--r--src/eu/siacs/conversations/entities/Conversation.java49
-rw-r--r--src/eu/siacs/conversations/entities/Message.java67
-rw-r--r--src/eu/siacs/conversations/entities/MucOptions.java118
-rw-r--r--src/eu/siacs/conversations/generator/AbstractGenerator.java5
-rw-r--r--src/eu/siacs/conversations/generator/IqGenerator.java58
-rw-r--r--src/eu/siacs/conversations/generator/MessageGenerator.java3
-rw-r--r--src/eu/siacs/conversations/parser/AbstractParser.java14
-rw-r--r--src/eu/siacs/conversations/parser/IqParser.java30
-rw-r--r--src/eu/siacs/conversations/parser/MessageParser.java139
-rw-r--r--src/eu/siacs/conversations/parser/PresenceParser.java67
-rw-r--r--src/eu/siacs/conversations/persistance/DatabaseBackend.java80
-rw-r--r--src/eu/siacs/conversations/persistance/FileBackend.java270
-rw-r--r--src/eu/siacs/conversations/services/Defaults.java11
-rw-r--r--src/eu/siacs/conversations/services/ImageProvider.java17
-rw-r--r--src/eu/siacs/conversations/services/XmppConnectionService.java158
-rw-r--r--src/eu/siacs/conversations/ui/ConferenceDetailsActivity.java91
-rw-r--r--src/eu/siacs/conversations/ui/ConversationActivity.java222
-rw-r--r--src/eu/siacs/conversations/ui/ConversationFragment.java212
-rw-r--r--src/eu/siacs/conversations/ui/EditAccountActivity.java341
-rw-r--r--src/eu/siacs/conversations/ui/EditAccountDialog.java157
-rw-r--r--src/eu/siacs/conversations/ui/EditMessage.java39
-rw-r--r--src/eu/siacs/conversations/ui/ManageAccountActivity.java496
-rw-r--r--src/eu/siacs/conversations/ui/PublishProfilePictureActivity.java242
-rw-r--r--src/eu/siacs/conversations/ui/SettingsActivity.java8
-rw-r--r--src/eu/siacs/conversations/ui/StartConversationActivity.java69
-rw-r--r--src/eu/siacs/conversations/ui/XmppActivity.java62
-rw-r--r--src/eu/siacs/conversations/ui/adapter/AccountAdapter.java101
-rw-r--r--src/eu/siacs/conversations/ui/adapter/ConversationAdapter.java107
-rw-r--r--src/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java5
-rw-r--r--src/eu/siacs/conversations/ui/adapter/MessageAdapter.java151
-rw-r--r--src/eu/siacs/conversations/utils/PhoneHelper.java4
-rw-r--r--src/eu/siacs/conversations/utils/UIHelper.java24
-rw-r--r--src/eu/siacs/conversations/utils/Validator.java2
-rw-r--r--src/eu/siacs/conversations/xml/Element.java8
-rw-r--r--src/eu/siacs/conversations/xml/TagWriter.java10
-rw-r--r--src/eu/siacs/conversations/xml/XmlReader.java23
-rw-r--r--src/eu/siacs/conversations/xmpp/XmppConnection.java187
-rw-r--r--src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java636
-rw-r--r--src/eu/siacs/conversations/xmpp/pep/Avatar.java68
-rw-r--r--src/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java3
44 files changed, 3072 insertions, 1547 deletions
diff --git a/src/eu/siacs/conversations/crypto/OtrEngine.java b/src/eu/siacs/conversations/crypto/OtrEngine.java
index 01ba5e49..7960aa2b 100644
--- a/src/eu/siacs/conversations/crypto/OtrEngine.java
+++ b/src/eu/siacs/conversations/crypto/OtrEngine.java
@@ -154,13 +154,16 @@ public class OtrEngine implements OtrEngineHost {
@Override
public void injectMessage(SessionID session, String body) throws OtrException {
MessagePacket packet = new MessagePacket();
- packet.setFrom(account.getFullJid()); //sender
- packet.setTo(session.getAccountID()+"/"+session.getUserID()); //reciepient
+ packet.setFrom(account.getFullJid());
+ if (session.getUserID().isEmpty()) {
+ packet.setTo(session.getAccountID());
+ } else {
+ packet.setTo(session.getAccountID()+"/"+session.getUserID());
+ }
packet.setBody(body);
packet.addChild("private","urn:xmpp:carbons:2");
packet.addChild("no-copy","urn:xmpp:hints");
packet.setType(MessagePacket.TYPE_CHAT);
- //Log.d(LOGTAG,packet.toString());
account.getXmppConnection().sendMessagePacket(packet);
}
diff --git a/src/eu/siacs/conversations/crypto/PgpEngine.java b/src/eu/siacs/conversations/crypto/PgpEngine.java
index 2d0c56e1..65b7ccc7 100644
--- a/src/eu/siacs/conversations/crypto/PgpEngine.java
+++ b/src/eu/siacs/conversations/crypto/PgpEngine.java
@@ -54,9 +54,18 @@ public class PgpEngine {
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
OpenPgpApi.RESULT_CODE_ERROR)) {
case OpenPgpApi.RESULT_CODE_SUCCESS:
- message.setBody(os.toString());
- message.setEncryption(Message.ENCRYPTION_DECRYPTED);
- callback.success(message);
+ try {
+ os.flush();
+ if (message.getEncryption() == Message.ENCRYPTION_PGP) {
+ message.setBody(os.toString());
+ message.setEncryption(Message.ENCRYPTION_DECRYPTED);
+ callback.success(message);
+ }
+ } catch (IOException e) {
+ callback.error(R.string.openpgp_error, message);
+ return;
+ }
+
return;
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
callback.userInputRequried((PendingIntent) result
@@ -64,6 +73,8 @@ public class PgpEngine {
message);
return;
case OpenPgpApi.RESULT_CODE_ERROR:
+ OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
+ Log.d("xmppService",error.getMessage());
callback.error(R.string.openpgp_error, message);
return;
default:
@@ -153,14 +164,20 @@ public class PgpEngine {
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
OpenPgpApi.RESULT_CODE_ERROR)) {
case OpenPgpApi.RESULT_CODE_SUCCESS:
- StringBuilder encryptedMessageBody = new StringBuilder();
- String[] lines = os.toString().split("\n");
- for (int i = 3; i < lines.length - 1; ++i) {
- encryptedMessageBody.append(lines[i].trim());
+ try {
+ os.flush();
+ StringBuilder encryptedMessageBody = new StringBuilder();
+ String[] lines = os.toString().split("\n");
+ for (int i = 3; i < lines.length - 1; ++i) {
+ encryptedMessageBody.append(lines[i].trim());
+ }
+ message.setEncryptedBody(encryptedMessageBody
+ .toString());
+ callback.success(message);
+ } catch (IOException e) {
+ callback.error(R.string.openpgp_error, message);
}
- message.setEncryptedBody(encryptedMessageBody
- .toString());
- callback.success(message);
+
break;
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
callback.userInputRequried((PendingIntent) result
diff --git a/src/eu/siacs/conversations/entities/Account.java b/src/eu/siacs/conversations/entities/Account.java
index b19889bf..d31d2324 100644
--- a/src/eu/siacs/conversations/entities/Account.java
+++ b/src/eu/siacs/conversations/entities/Account.java
@@ -1,7 +1,6 @@
package eu.siacs.conversations.entities;
import java.security.interfaces.DSAPublicKey;
-import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -12,28 +11,33 @@ import net.java.otr4j.crypto.OtrCryptoException;
import org.json.JSONException;
import org.json.JSONObject;
+import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.OtrEngine;
+import eu.siacs.conversations.persistance.FileBackend;
+import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.XmppConnection;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
+import android.graphics.Bitmap;
+
+public class Account extends AbstractEntity {
-public class Account extends AbstractEntity{
-
public static final String TABLENAME = "accounts";
-
+
public static final String USERNAME = "username";
public static final String SERVER = "server";
public static final String PASSWORD = "password";
public static final String OPTIONS = "options";
public static final String ROSTERVERSION = "rosterversion";
public static final String KEYS = "keys";
-
+ public static final String AVATAR = "avatar";
+
public static final int OPTION_USETLS = 0;
public static final int OPTION_DISABLED = 1;
public static final int OPTION_REGISTER = 2;
public static final int OPTION_USECOMPRESSION = 3;
-
+
public static final int STATUS_CONNECTING = 0;
public static final int STATUS_DISABLED = -2;
public static final int STATUS_OFFLINE = -1;
@@ -42,13 +46,11 @@ public class Account extends AbstractEntity{
public static final int STATUS_UNAUTHORIZED = 3;
public static final int STATUS_SERVER_NOT_FOUND = 5;
- public static final int STATUS_SERVER_REQUIRES_TLS = 6;
-
public static final int STATUS_REGISTRATION_FAILED = 7;
public static final int STATUS_REGISTRATION_CONFLICT = 8;
public static final int STATUS_REGISTRATION_SUCCESSFULL = 9;
public static final int STATUS_REGISTRATION_NOT_SUPPORTED = 10;
-
+
protected String username;
protected String server;
protected String password;
@@ -57,30 +59,34 @@ public class Account extends AbstractEntity{
protected String resource = "mobile";
protected int status = -1;
protected JSONObject keys = new JSONObject();
-
+ protected String avatar;
+
protected boolean online = false;
-
+
transient OtrEngine otrEngine = null;
transient XmppConnection xmppConnection = null;
transient protected Presences presences = new Presences();
private String otrFingerprint;
-
+
private Roster roster = null;
- private List<Bookmark> bookmarks = new ArrayList<Bookmark>();
-
+ private List<Bookmark> bookmarks = new CopyOnWriteArrayList<Bookmark>();
+
public List<Conversation> pendingConferenceJoins = new CopyOnWriteArrayList<Conversation>();
public List<Conversation> pendingConferenceLeaves = new CopyOnWriteArrayList<Conversation>();
-
+
public Account() {
this.uuid = "0";
}
-
+
public Account(String username, String server, String password) {
- this(java.util.UUID.randomUUID().toString(),username,server,password,0,null,"");
+ this(java.util.UUID.randomUUID().toString(), username, server,
+ password, 0, null, "",null);
}
- public Account(String uuid, String username, String server,String password, int options, String rosterVersion, String keys) {
+
+ public Account(String uuid, String username, String server,
+ String password, int options, String rosterVersion, String keys, String avatar) {
this.uuid = uuid;
this.username = username;
this.server = server;
@@ -90,14 +96,15 @@ public class Account extends AbstractEntity{
try {
this.keys = new JSONObject(keys);
} catch (JSONException e) {
-
+
}
+ this.avatar = avatar;
}
-
+
public boolean isOptionSet(int option) {
return ((options & (1 << option)) != 0);
}
-
+
public void setOption(int option, boolean value) {
if (value) {
this.options |= 1 << option;
@@ -105,7 +112,7 @@ public class Account extends AbstractEntity{
this.options &= ~(1 << option);
}
}
-
+
public String getUsername() {
return username;
}
@@ -129,11 +136,11 @@ public class Account extends AbstractEntity{
public void setPassword(String password) {
this.password = password;
}
-
+
public void setStatus(int status) {
this.status = status;
}
-
+
public int getStatus() {
if (isOptionSet(OPTION_DISABLED)) {
return STATUS_DISABLED;
@@ -141,27 +148,34 @@ public class Account extends AbstractEntity{
return this.status;
}
}
-
+
+ public boolean errorStatus() {
+ int s = getStatus();
+ return (s == STATUS_REGISTRATION_FAILED || s == STATUS_REGISTRATION_CONFLICT || s == STATUS_REGISTRATION_NOT_SUPPORTED || s == STATUS_SERVER_NOT_FOUND || s == STATUS_UNAUTHORIZED);
+ }
+
public boolean hasErrorStatus() {
- return getStatus() > STATUS_NO_INTERNET && (getXmppConnection().getAttempt() >= 2);
+ return getStatus() > STATUS_NO_INTERNET
+ && (getXmppConnection().getAttempt() >= 2);
}
-
+
public void setResource(String resource) {
this.resource = resource;
}
-
+
public String getResource() {
return this.resource;
}
-
+
public String getJid() {
- return username.toLowerCase(Locale.getDefault())+"@"+server.toLowerCase(Locale.getDefault());
+ return username.toLowerCase(Locale.getDefault()) + "@"
+ + server.toLowerCase(Locale.getDefault());
}
-
+
public JSONObject getKeys() {
return keys;
}
-
+
public String getSSLFingerprint() {
if (keys.has("ssl_cert")) {
try {
@@ -173,11 +187,11 @@ public class Account extends AbstractEntity{
return null;
}
}
-
+
public void setSSLCertFingerprint(String fingerprint) {
this.setKey("ssl_cert", fingerprint);
}
-
+
public boolean setKey(String keyName, String keyValue) {
try {
this.keys.put(keyName, keyValue);
@@ -190,16 +204,17 @@ public class Account extends AbstractEntity{
@Override
public ContentValues getContentValues() {
ContentValues values = new ContentValues();
- values.put(UUID,uuid);
+ values.put(UUID, uuid);
values.put(USERNAME, username);
values.put(SERVER, server);
values.put(PASSWORD, password);
- values.put(OPTIONS,options);
- values.put(KEYS,this.keys.toString());
- values.put(ROSTERVERSION,rosterVersion);
+ values.put(OPTIONS, options);
+ values.put(KEYS, this.keys.toString());
+ values.put(ROSTERVERSION, rosterVersion);
+ values.put(AVATAR, avatar);
return values;
}
-
+
public static Account fromCursor(Cursor cursor) {
return new Account(cursor.getString(cursor.getColumnIndex(UUID)),
cursor.getString(cursor.getColumnIndex(USERNAME)),
@@ -207,14 +222,13 @@ public class Account extends AbstractEntity{
cursor.getString(cursor.getColumnIndex(PASSWORD)),
cursor.getInt(cursor.getColumnIndex(OPTIONS)),
cursor.getString(cursor.getColumnIndex(ROSTERVERSION)),
- cursor.getString(cursor.getColumnIndex(KEYS))
- );
+ cursor.getString(cursor.getColumnIndex(KEYS)),
+ cursor.getString(cursor.getColumnIndex(AVATAR)));
}
-
public OtrEngine getOtrEngine(Context context) {
- if (otrEngine==null) {
- otrEngine = new OtrEngine(context,this);
+ if (otrEngine == null) {
+ otrEngine = new OtrEngine(context, this);
}
return this.otrEngine;
}
@@ -228,37 +242,39 @@ public class Account extends AbstractEntity{
}
public String getFullJid() {
- return this.getJid()+"/"+this.resource;
+ return this.getJid() + "/" + this.resource;
}
-
+
public String getOtrFingerprint() {
if (this.otrFingerprint == null) {
try {
- DSAPublicKey pubkey = (DSAPublicKey) this.otrEngine.getPublicKey();
+ DSAPublicKey pubkey = (DSAPublicKey) this.otrEngine
+ .getPublicKey();
if (pubkey == null) {
return null;
}
- StringBuilder builder = new StringBuilder(new OtrCryptoEngineImpl().getFingerprint(pubkey));
+ StringBuilder builder = new StringBuilder(
+ new OtrCryptoEngineImpl().getFingerprint(pubkey));
builder.insert(8, " ");
builder.insert(17, " ");
builder.insert(26, " ");
builder.insert(35, " ");
this.otrFingerprint = builder.toString();
} catch (OtrCryptoException e) {
-
+
}
}
return this.otrFingerprint;
}
public String getRosterVersion() {
- if (this.rosterVersion==null) {
+ if (this.rosterVersion == null) {
return "";
} else {
return this.rosterVersion;
}
}
-
+
public void setRosterVersion(String version) {
this.rosterVersion = version;
}
@@ -267,7 +283,7 @@ public class Account extends AbstractEntity{
this.getOtrEngine(applicationContext);
return this.getOtrFingerprint();
}
-
+
public void updatePresence(String resource, int status) {
this.presences.updatePresence(resource, status);
}
@@ -275,7 +291,7 @@ public class Account extends AbstractEntity{
public void removePresence(String resource) {
this.presences.removePresence(resource);
}
-
+
public void clearPresences() {
this.presences = new Presences();
}
@@ -295,9 +311,9 @@ public class Account extends AbstractEntity{
return null;
}
}
-
+
public Roster getRoster() {
- if (this.roster==null) {
+ if (this.roster == null) {
this.roster = new Roster(this);
}
return this.roster;
@@ -306,17 +322,74 @@ public class Account extends AbstractEntity{
public void setBookmarks(List<Bookmark> bookmarks) {
this.bookmarks = bookmarks;
}
-
+
public List<Bookmark> getBookmarks() {
return this.bookmarks;
}
public boolean hasBookmarkFor(String conferenceJid) {
- for(Bookmark bmark : this.bookmarks) {
+ for (Bookmark bmark : this.bookmarks) {
if (bmark.getJid().equals(conferenceJid)) {
return true;
}
}
return false;
}
+
+ public Bitmap getImage(Context context, int size) {
+ if (this.avatar != null) {
+ Bitmap bm = FileBackend.getAvatar(this.avatar, size, context);
+ if (bm == null) {
+ return UIHelper.getContactPicture(getJid(), size, context,
+ false);
+ } else {
+ return bm;
+ }
+ } else {
+ return UIHelper.getContactPicture(getJid(), size, context, false);
+ }
+ }
+
+ public boolean setAvatar(String filename) {
+ if (this.avatar != null && this.avatar.equals(filename)) {
+ return false;
+ } else {
+ this.avatar = filename;
+ return true;
+ }
+ }
+
+ public String getAvatar() {
+ return this.avatar;
+ }
+
+ public int getReadableStatusId() {
+ switch (getStatus()) {
+
+ case Account.STATUS_DISABLED:
+ return R.string.account_status_disabled;
+ case Account.STATUS_ONLINE:
+ return R.string.account_status_online;
+ case Account.STATUS_CONNECTING:
+ return R.string.account_status_connecting;
+ case Account.STATUS_OFFLINE:
+ return R.string.account_status_offline;
+ case Account.STATUS_UNAUTHORIZED:
+ return R.string.account_status_unauthorized;
+ case Account.STATUS_SERVER_NOT_FOUND:
+ return R.string.account_status_not_found;
+ case Account.STATUS_NO_INTERNET:
+ return R.string.account_status_no_internet;
+ case Account.STATUS_REGISTRATION_FAILED:
+ return R.string.account_status_regis_fail;
+ case Account.STATUS_REGISTRATION_CONFLICT:
+ return R.string.account_status_regis_conflict;
+ case Account.STATUS_REGISTRATION_SUCCESSFULL:
+ return R.string.account_status_regis_success;
+ case Account.STATUS_REGISTRATION_NOT_SUPPORTED:
+ return R.string.account_status_regis_not_sup;
+ default:
+ return R.string.account_status_unknown;
+ }
+ }
}
diff --git a/src/eu/siacs/conversations/entities/Contact.java b/src/eu/siacs/conversations/entities/Contact.java
index 8f8e38a5..ab05b9d1 100644
--- a/src/eu/siacs/conversations/entities/Contact.java
+++ b/src/eu/siacs/conversations/entities/Contact.java
@@ -8,6 +8,7 @@ import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
+import eu.siacs.conversations.persistance.FileBackend;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xml.Element;
import android.content.ContentValues;
@@ -26,6 +27,7 @@ public class Contact implements ListItem {
public static final String PHOTOURI = "photouri";
public static final String KEYS = "pgpkey";
public static final String ACCOUNT = "accountUuid";
+ public static final String AVATAR = "avatar";
protected String accountUuid;
protected String systemName;
@@ -34,6 +36,7 @@ public class Contact implements ListItem {
protected int subscription = 0;
protected String systemAccount;
protected String photoUri;
+ protected String avatar;
protected JSONObject keys = new JSONObject();
protected Presences presences = new Presences();
@@ -45,7 +48,7 @@ public class Contact implements ListItem {
public Contact(String account, String systemName, String serverName,
String jid, int subscription, String photoUri,
- String systemAccount, String keys) {
+ String systemAccount, String keys, String avatar) {
this.accountUuid = account;
this.systemName = systemName;
this.serverName = serverName;
@@ -61,6 +64,7 @@ public class Contact implements ListItem {
} catch (JSONException e) {
this.keys = new JSONObject();
}
+ this.avatar = avatar;
}
public Contact(String jid) {
@@ -102,6 +106,7 @@ public class Contact implements ListItem {
values.put(SYSTEMACCOUNT, systemAccount);
values.put(PHOTOURI, photoUri);
values.put(KEYS, keys.toString());
+ values.put(AVATAR,avatar);
return values;
}
@@ -113,7 +118,8 @@ public class Contact implements ListItem {
cursor.getInt(cursor.getColumnIndex(OPTIONS)),
cursor.getString(cursor.getColumnIndex(PHOTOURI)),
cursor.getString(cursor.getColumnIndex(SYSTEMACCOUNT)),
- cursor.getString(cursor.getColumnIndex(KEYS)));
+ cursor.getString(cursor.getColumnIndex(KEYS)),
+ cursor.getString(cursor.getColumnIndex(AVATAR)));
}
public int getSubscription() {
@@ -316,7 +322,25 @@ public class Contact implements ListItem {
}
@Override
- public Bitmap getImage(int dpSize, Context context) {
- return UIHelper.getContactPicture(this, dpSize, context, false);
+ public Bitmap getImage(int size, Context context) {
+ if (this.avatar!=null) {
+ Bitmap bm = FileBackend.getAvatar(avatar, size, context);
+ if (bm==null) {
+ return UIHelper.getContactPicture(this, size, context, false);
+ } else {
+ return bm;
+ }
+ } else {
+ return UIHelper.getContactPicture(this, size, context, false);
+ }
+ }
+
+ public boolean setAvatar(String filename) {
+ if (this.avatar != null && this.avatar.equals(filename)) {
+ return false;
+ } else {
+ this.avatar = filename;
+ return true;
+ }
}
}
diff --git a/src/eu/siacs/conversations/entities/Conversation.java b/src/eu/siacs/conversations/entities/Conversation.java
index 76fe84cf..439f9f22 100644
--- a/src/eu/siacs/conversations/entities/Conversation.java
+++ b/src/eu/siacs/conversations/entities/Conversation.java
@@ -1,8 +1,10 @@
package eu.siacs.conversations.entities;
import java.security.interfaces.DSAPublicKey;
-import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import eu.siacs.conversations.utils.UIHelper;
import net.java.otr4j.OtrException;
import net.java.otr4j.crypto.OtrCryptoEngineImpl;
@@ -13,7 +15,7 @@ import net.java.otr4j.session.SessionStatus;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
-import android.net.Uri;
+import android.graphics.Bitmap;
public class Conversation extends AbstractEntity {
public static final String TABLENAME = "conversations";
@@ -43,7 +45,7 @@ public class Conversation extends AbstractEntity {
private String nextPresence;
- private transient List<Message> messages = null;
+ private transient CopyOnWriteArrayList<Message> messages = null;
private transient Account account = null;
private transient SessionImpl otrSession;
@@ -85,8 +87,9 @@ public class Conversation extends AbstractEntity {
}
public List<Message> getMessages() {
- if (messages == null)
- this.messages = new ArrayList<Message>(); // prevent null pointer
+ if (messages == null) {
+ this.messages = new CopyOnWriteArrayList<Message>(); // prevent null pointer
+ }
// populate with Conversation (this)
@@ -133,7 +136,7 @@ public class Conversation extends AbstractEntity {
}
}
- public void setMessages(List<Message> msgs) {
+ public void setMessages(CopyOnWriteArrayList<Message> msgs) {
this.messages = msgs;
}
@@ -173,13 +176,6 @@ public class Conversation extends AbstractEntity {
return this.contactJid;
}
- public Uri getProfilePhotoUri() {
- if (this.getProfilePhotoString() != null) {
- return Uri.parse(this.getProfilePhotoString());
- }
- return null;
- }
-
public int getStatus() {
return this.status;
}
@@ -339,6 +335,16 @@ public class Conversation extends AbstractEntity {
if ((latestEncryption == Message.ENCRYPTION_DECRYPTED)
|| (latestEncryption == Message.ENCRYPTION_DECRYPTION_FAILED)) {
return Message.ENCRYPTION_PGP;
+ } else if (latestEncryption == Message.ENCRYPTION_NONE) {
+ if (getContact().getPresences().size() == 1) {
+ if (getContact().getOtrFingerprints().size() >= 1) {
+ return Message.ENCRYPTION_OTR;
+ } else {
+ return latestEncryption;
+ }
+ } else {
+ return latestEncryption;
+ }
} else {
return latestEncryption;
}
@@ -395,4 +401,21 @@ public class Conversation extends AbstractEntity {
public Bookmark getBookmark() {
return this.bookmark;
}
+
+ public Bitmap getImage(Context context, int size) {
+ if (mode==MODE_SINGLE) {
+ return getContact().getImage(size, context);
+ } else {
+ return UIHelper.getContactPicture(this, size, context, false);
+ }
+ }
+
+ public boolean hasDuplicateMessage(Message message) {
+ for(int i = this.getMessages().size() -1; i >= 0; --i) {
+ if (this.messages.get(i).equals(message)) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/src/eu/siacs/conversations/entities/Message.java b/src/eu/siacs/conversations/entities/Message.java
index 49c5ce58..6d70b66d 100644
--- a/src/eu/siacs/conversations/entities/Message.java
+++ b/src/eu/siacs/conversations/entities/Message.java
@@ -33,17 +33,21 @@ public class Message extends AbstractEntity {
public static final int TYPE_IMAGE = 1;
public static final int TYPE_AUDIO = 2;
public static final int TYPE_STATUS = 3;
+ public static final int TYPE_PRIVATE = 4;
public static String CONVERSATION = "conversationUuid";
public static String COUNTERPART = "counterpart";
+ public static String TRUE_COUNTERPART = "trueCounterpart";
public static String BODY = "body";
public static String TIME_SENT = "timeSent";
public static String ENCRYPTION = "encryption";
public static String STATUS = "status";
public static String TYPE = "type";
+ public static String REMOTE_MSG_ID = "remoteMsgId";
protected String conversationUuid;
protected String counterpart;
+ protected String trueCounterpart;
protected String body;
protected String encryptedBody;
protected long timeSent;
@@ -51,6 +55,7 @@ public class Message extends AbstractEntity {
protected int status;
protected int type;
protected boolean read = true;
+ protected String remoteMsgId = null;
protected transient Conversation conversation = null;
@@ -62,26 +67,28 @@ public class Message extends AbstractEntity {
public Message(Conversation conversation, String body, int encryption) {
this(java.util.UUID.randomUUID().toString(), conversation.getUuid(),
- conversation.getContactJid(), body, System.currentTimeMillis(), encryption,
- Message.STATUS_UNSEND,TYPE_TEXT);
+ conversation.getContactJid(), null, body, System.currentTimeMillis(), encryption,
+ Message.STATUS_UNSEND,TYPE_TEXT,null);
this.conversation = conversation;
}
public Message(Conversation conversation, String counterpart, String body, int encryption, int status) {
- this(java.util.UUID.randomUUID().toString(), conversation.getUuid(),counterpart, body, System.currentTimeMillis(), encryption,status,TYPE_TEXT);
+ this(java.util.UUID.randomUUID().toString(), conversation.getUuid(),counterpart, null, body, System.currentTimeMillis(), encryption,status,TYPE_TEXT,null);
this.conversation = conversation;
}
- public Message(String uuid, String conversationUUid, String counterpart,
- String body, long timeSent, int encryption, int status, int type) {
+ public Message(String uuid, String conversationUUid, String counterpart, String trueCounterpart,
+ String body, long timeSent, int encryption, int status, int type, String remoteMsgId) {
this.uuid = uuid;
this.conversationUuid = conversationUUid;
this.counterpart = counterpart;
+ this.trueCounterpart = trueCounterpart;
this.body = body;
this.timeSent = timeSent;
this.encryption = encryption;
this.status = status;
this.type = type;
+ this.remoteMsgId = remoteMsgId;
}
@Override
@@ -90,11 +97,13 @@ public class Message extends AbstractEntity {
values.put(UUID, uuid);
values.put(CONVERSATION, conversationUuid);
values.put(COUNTERPART, counterpart);
+ values.put(TRUE_COUNTERPART,trueCounterpart);
values.put(BODY, body);
values.put(TIME_SENT, timeSent);
values.put(ENCRYPTION, encryption);
values.put(STATUS, status);
values.put(TYPE, type);
+ values.put(REMOTE_MSG_ID,remoteMsgId);
return values;
}
@@ -109,6 +118,24 @@ public class Message extends AbstractEntity {
public String getCounterpart() {
return counterpart;
}
+
+ public Contact getContact() {
+ if (this.conversation.getMode() == Conversation.MODE_SINGLE) {
+ return this.conversation.getContact();
+ } else {
+ if (this.trueCounterpart == null) {
+ return null;
+ } else {
+ Account account = this.conversation.getAccount();
+ Contact contact = account.getRoster().getContact(this.trueCounterpart);
+ if (contact.showInRoster()) {
+ return contact;
+ } else {
+ return null;
+ }
+ }
+ }
+ }
public String getBody() {
return body;
@@ -139,16 +166,26 @@ public class Message extends AbstractEntity {
public int getStatus() {
return status;
}
+
+ public String getRemoteMsgId() {
+ return this.remoteMsgId;
+ }
+
+ public void setRemoteMsgId(String id) {
+ this.remoteMsgId = id;
+ }
public static Message fromCursor(Cursor cursor) {
return new Message(cursor.getString(cursor.getColumnIndex(UUID)),
cursor.getString(cursor.getColumnIndex(CONVERSATION)),
cursor.getString(cursor.getColumnIndex(COUNTERPART)),
+ cursor.getString(cursor.getColumnIndex(TRUE_COUNTERPART)),
cursor.getString(cursor.getColumnIndex(BODY)),
cursor.getLong(cursor.getColumnIndex(TIME_SENT)),
cursor.getInt(cursor.getColumnIndex(ENCRYPTION)),
cursor.getInt(cursor.getColumnIndex(STATUS)),
- cursor.getInt(cursor.getColumnIndex(TYPE)));
+ cursor.getInt(cursor.getColumnIndex(TYPE)),
+ cursor.getString(cursor.getColumnIndex(REMOTE_MSG_ID)));
}
public void setConversation(Conversation conv) {
@@ -200,13 +237,17 @@ public class Message extends AbstractEntity {
}
public void setPresence(String presence) {
- if (presence == null) {
+ if (presence == null || presence.isEmpty()) {
this.counterpart = this.counterpart.split("/")[0];
} else {
this.counterpart = this.counterpart.split("/")[0] + "/" + presence;
}
}
+ public void setTrueCounterpart(String trueCounterpart) {
+ this.trueCounterpart = trueCounterpart;
+ }
+
public String getPresence() {
String[] counterparts = this.counterpart.split("/");
if (counterparts.length == 2) {
@@ -230,4 +271,16 @@ public class Message extends AbstractEntity {
message.setConversation(conversation);
return message;
}
+
+ public void setCounterpart(String counterpart) {
+ this.counterpart = counterpart;
+ }
+
+ public boolean equals(Message message) {
+ if ((this.remoteMsgId!=null) && (this.body != null) && (this.counterpart != null)) {
+ return this.remoteMsgId.equals(message.getRemoteMsgId()) && this.body.equals(message.getBody()) && this.counterpart.equals(message.getCounterpart());
+ } else {
+ return false;
+ }
+ }
}
diff --git a/src/eu/siacs/conversations/entities/MucOptions.java b/src/eu/siacs/conversations/entities/MucOptions.java
index 0bb9b295..61b2732d 100644
--- a/src/eu/siacs/conversations/entities/MucOptions.java
+++ b/src/eu/siacs/conversations/entities/MucOptions.java
@@ -2,6 +2,7 @@ package eu.siacs.conversations.entities;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
import eu.siacs.conversations.crypto.PgpEngine;
import eu.siacs.conversations.xml.Element;
@@ -13,11 +14,11 @@ public class MucOptions {
public static final int ERROR_NO_ERROR = 0;
public static final int ERROR_NICK_IN_USE = 1;
public static final int ERROR_ROOM_NOT_FOUND = 2;
-
+
public interface OnRenameListener {
public void onRename(boolean success);
}
-
+
public class User {
public static final int ROLE_MODERATOR = 3;
public static final int ROLE_NONE = 0;
@@ -28,22 +29,33 @@ public class MucOptions {
public static final int AFFILIATION_MEMBER = 2;
public static final int AFFILIATION_OUTCAST = 1;
public static final int AFFILIATION_NONE = 0;
-
+
private int role;
private int affiliation;
private String name;
+ private String jid;
private long pgpKeyId = 0;
-
+
public String getName() {
return name;
}
+
public void setName(String user) {
this.name = user;
}
-
+
+ public void setJid(String jid) {
+ this.jid = jid;
+ }
+
+ public String getJid() {
+ return this.jid;
+ }
+
public int getRole() {
return this.role;
}
+
public void setRole(String role) {
role = role.toLowerCase();
if (role.equals("moderator")) {
@@ -56,9 +68,11 @@ public class MucOptions {
this.role = ROLE_NONE;
}
}
+
public int getAffiliation() {
return this.affiliation;
}
+
public void setAffiliation(String affiliation) {
if (affiliation.equalsIgnoreCase("admin")) {
this.affiliation = AFFILIATION_ADMIN;
@@ -72,16 +86,18 @@ public class MucOptions {
this.affiliation = AFFILIATION_NONE;
}
}
+
public void setPgpKeyId(long id) {
this.pgpKeyId = id;
}
-
+
public long getPgpKeyId() {
return this.pgpKeyId;
}
}
+
private Account account;
- private ArrayList<User> users = new ArrayList<User>();
+ private List<User> users = new CopyOnWriteArrayList<User>();
private Conversation conversation;
private boolean isOnline = false;
private int error = ERROR_ROOM_NOT_FOUND;
@@ -94,44 +110,47 @@ public class MucOptions {
public MucOptions(Account account) {
this.account = account;
}
-
+
public void deleteUser(String name) {
- for(int i = 0; i < users.size(); ++i) {
+ for (int i = 0; i < users.size(); ++i) {
if (users.get(i).getName().equals(name)) {
users.remove(i);
return;
}
}
}
-
+
public void addUser(User user) {
- for(int i = 0; i < users.size(); ++i) {
+ for (int i = 0; i < users.size(); ++i) {
if (users.get(i).getName().equals(user.getName())) {
users.set(i, user);
return;
}
}
users.add(user);
- }
-
+ }
+
public void processPacket(PresencePacket packet, PgpEngine pgp) {
String[] fromParts = packet.getFrom().split("/");
- if (fromParts.length>=2) {
+ if (fromParts.length >= 2) {
String name = fromParts[1];
String type = packet.getAttribute("type");
- if (type==null) {
+ if (type == null) {
User user = new User();
- Element item = packet.findChild("x","http://jabber.org/protocol/muc#user").findChild("item");
+ Element item = packet.findChild("x",
+ "http://jabber.org/protocol/muc#user")
+ .findChild("item");
user.setName(name);
user.setAffiliation(item.getAttribute("affiliation"));
user.setRole(item.getAttribute("role"));
+ user.setJid(item.getAttribute("jid"));
user.setName(name);
if (name.equals(this.joinnick)) {
this.isOnline = true;
this.error = ERROR_NO_ERROR;
self = user;
if (aboutToRename) {
- if (renameListener!=null) {
+ if (renameListener != null) {
renameListener.onRename(true);
}
aboutToRename = false;
@@ -140,8 +159,7 @@ public class MucOptions {
addUser(user);
}
if (pgp != null) {
- Element x = packet.findChild("x",
- "jabber:x:signed");
+ Element x = packet.findChild("x", "jabber:x:signed");
if (x != null) {
Element status = packet.findChild("status");
String msg;
@@ -150,7 +168,8 @@ public class MucOptions {
} else {
msg = "";
}
- user.setPgpKeyId(pgp.fetchKeyId(account,msg, x.getContent()));
+ user.setPgpKeyId(pgp.fetchKeyId(account, msg,
+ x.getContent()));
}
}
} else if (type.equals("unavailable")) {
@@ -159,26 +178,27 @@ public class MucOptions {
Element error = packet.findChild("error");
if (error.hasChild("conflict")) {
if (aboutToRename) {
- if (renameListener!=null) {
+ if (renameListener != null) {
renameListener.onRename(false);
}
aboutToRename = false;
this.setJoinNick(getActualNick());
} else {
- this.error = ERROR_NICK_IN_USE;
+ this.error = ERROR_NICK_IN_USE;
}
}
}
}
}
-
+
public List<User> getUsers() {
return this.users;
}
-
+
public String getProposedNick() {
String[] mucParts = conversation.getContactJid().split("/");
- if (conversation.getBookmark() != null && conversation.getBookmark().getNick() != null) {
+ if (conversation.getBookmark() != null
+ && conversation.getBookmark().getNick() != null) {
return conversation.getBookmark().getNick();
} else {
if (mucParts.length == 2) {
@@ -188,27 +208,27 @@ public class MucOptions {
}
}
}
-
+
public String getActualNick() {
- if (this.self.getName()!=null) {
+ if (this.self.getName() != null) {
return this.self.getName();
} else {
return this.getProposedNick();
}
}
-
+
public void setJoinNick(String nick) {
this.joinnick = nick;
}
-
+
public void setConversation(Conversation conversation) {
this.conversation = conversation;
}
-
+
public boolean online() {
return this.isOnline;
}
-
+
public int getError() {
return this.error;
}
@@ -216,7 +236,7 @@ public class MucOptions {
public void setOnRenameListener(OnRenameListener listener) {
this.renameListener = listener;
}
-
+
public OnRenameListener getOnRenameListener() {
return this.renameListener;
}
@@ -234,7 +254,7 @@ public class MucOptions {
public void setSubject(String content) {
this.subject = content;
}
-
+
public String getSubject() {
return this.subject;
}
@@ -242,33 +262,33 @@ public class MucOptions {
public void flagAboutToRename() {
this.aboutToRename = true;
}
-
+
public long[] getPgpKeyIds() {
List<Long> ids = new ArrayList<Long>();
- for(User user : getUsers()) {
- if(user.getPgpKeyId()!=0) {
+ for (User user : getUsers()) {
+ if (user.getPgpKeyId() != 0) {
ids.add(user.getPgpKeyId());
}
}
long[] primitivLongArray = new long[ids.size()];
- for(int i = 0; i < ids.size(); ++i) {
+ for (int i = 0; i < ids.size(); ++i) {
primitivLongArray[i] = ids.get(i);
}
return primitivLongArray;
}
-
+
public boolean pgpKeysInUse() {
- for(User user : getUsers()) {
- if (user.getPgpKeyId()!=0) {
+ for (User user : getUsers()) {
+ if (user.getPgpKeyId() != 0) {
return true;
}
}
return false;
}
-
+
public boolean everybodyHasKeys() {
- for(User user : getUsers()) {
- if (user.getPgpKeyId()==0) {
+ for (User user : getUsers()) {
+ if (user.getPgpKeyId() == 0) {
return false;
}
}
@@ -276,6 +296,16 @@ public class MucOptions {
}
public String getJoinJid() {
- return this.conversation.getContactJid().split("/")[0]+"/"+this.joinnick;
+ return this.conversation.getContactJid().split("/")[0] + "/"
+ + this.joinnick;
+ }
+
+ public String getTrueCounterpart(String counterpart) {
+ for(User user : this.getUsers()) {
+ if (user.getName().equals(counterpart)) {
+ return user.getJid();
+ }
+ }
+ return null;
}
} \ No newline at end of file
diff --git a/src/eu/siacs/conversations/generator/AbstractGenerator.java b/src/eu/siacs/conversations/generator/AbstractGenerator.java
index 05d5799c..d9839572 100644
--- a/src/eu/siacs/conversations/generator/AbstractGenerator.java
+++ b/src/eu/siacs/conversations/generator/AbstractGenerator.java
@@ -18,7 +18,8 @@ public abstract class AbstractGenerator {
"http://jabber.org/protocol/muc",
"jabber:x:conference",
"http://jabber.org/protocol/caps",
- "http://jabber.org/protocol/disco#info"};
+ "http://jabber.org/protocol/disco#info",
+ "urn:xmpp:avatar:metadata+notify"};
public final String IDENTITY_NAME = "Conversations 0.5";
public final String IDENTITY_TYPE = "phone";
/*public final String[] FEATURES = { "http://jabber.org/protocol/muc","http://jabber.org/protocol/disco#info", "http://jabber.org/protocol/disco#items", "http://jabber.org/protocol/caps" };
@@ -45,6 +46,6 @@ public abstract class AbstractGenerator {
s.append(feature+"<");
}
byte[] sha1 = md.digest(s.toString().getBytes());
- return new String(Base64.encode(sha1, Base64.DEFAULT));
+ return new String(Base64.encode(sha1, Base64.DEFAULT)).trim();
}
}
diff --git a/src/eu/siacs/conversations/generator/IqGenerator.java b/src/eu/siacs/conversations/generator/IqGenerator.java
index 7b3350d4..259538c2 100644
--- a/src/eu/siacs/conversations/generator/IqGenerator.java
+++ b/src/eu/siacs/conversations/generator/IqGenerator.java
@@ -5,6 +5,7 @@ import java.util.Collections;
import java.util.List;
import eu.siacs.conversations.xml.Element;
+import eu.siacs.conversations.xmpp.pep.Avatar;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
public class IqGenerator extends AbstractGenerator {
@@ -28,4 +29,61 @@ public class IqGenerator extends AbstractGenerator {
}
return packet;
}
+
+ protected IqPacket publish(String node, Element item) {
+ IqPacket packet = new IqPacket(IqPacket.TYPE_SET);
+ Element pubsub = packet.addChild("pubsub", "http://jabber.org/protocol/pubsub");
+ Element publish = pubsub.addChild("publish");
+ publish.setAttribute("node", node);
+ publish.addChild(item);
+ return packet;
+ }
+
+ protected IqPacket retrieve(String node, Element item) {
+ IqPacket packet = new IqPacket(IqPacket.TYPE_GET);
+ Element pubsub = packet.addChild("pubsub", "http://jabber.org/protocol/pubsub");
+ Element items = pubsub.addChild("items");
+ items.setAttribute("node", node);
+ if (item!=null) {
+ items.addChild(item);
+ }
+ return packet;
+ }
+
+ public IqPacket publishAvatar(Avatar avatar) {
+ Element item = new Element("item");
+ item.setAttribute("id", avatar.sha1sum);
+ Element data = item.addChild("data","urn:xmpp:avatar:data");
+ data.setContent(avatar.image);
+ return publish("urn:xmpp:avatar:data", item);
+ }
+
+ public IqPacket publishAvatarMetadata(Avatar avatar) {
+ Element item = new Element("item");
+ item.setAttribute("id", avatar.sha1sum);
+ Element metadata = item.addChild("metadata","urn:xmpp:avatar:metadata");
+ Element info = metadata.addChild("info");
+ info.setAttribute("bytes",avatar.size);
+ info.setAttribute("id",avatar.sha1sum);
+ info.setAttribute("height",avatar.height);
+ info.setAttribute("width",avatar.height);
+ info.setAttribute("type", avatar.type);
+ return publish("urn:xmpp:avatar:metadata",item);
+ }
+
+ public IqPacket retrieveAvatar(Avatar avatar) {
+ Element item = new Element("item");
+ item.setAttribute("id", avatar.sha1sum);
+ IqPacket packet = retrieve("urn:xmpp:avatar:data", item);
+ packet.setTo(avatar.owner);
+ return packet;
+ }
+
+ public IqPacket retrieveAvatarMetaData(String to) {
+ IqPacket packet = retrieve("urn:xmpp:avatar:metadata", null);
+ if (to!=null) {
+ packet.setTo(to);
+ }
+ return packet;
+ }
}
diff --git a/src/eu/siacs/conversations/generator/MessageGenerator.java b/src/eu/siacs/conversations/generator/MessageGenerator.java
index 4449a7ec..26182aad 100644
--- a/src/eu/siacs/conversations/generator/MessageGenerator.java
+++ b/src/eu/siacs/conversations/generator/MessageGenerator.java
@@ -22,6 +22,9 @@ public class MessageGenerator {
packet.setTo(message.getCounterpart());
packet.setType(MessagePacket.TYPE_CHAT);
packet.addChild("markable", "urn:xmpp:chat-markers:0");
+ } else if (message.getType() == Message.TYPE_PRIVATE) {
+ packet.setTo(message.getCounterpart());
+ packet.setType(MessagePacket.TYPE_CHAT);
} else {
packet.setTo(message.getCounterpart().split("/")[0]);
packet.setType(MessagePacket.TYPE_GROUPCHAT);
diff --git a/src/eu/siacs/conversations/parser/AbstractParser.java b/src/eu/siacs/conversations/parser/AbstractParser.java
index c4c6720a..96d11508 100644
--- a/src/eu/siacs/conversations/parser/AbstractParser.java
+++ b/src/eu/siacs/conversations/parser/AbstractParser.java
@@ -63,6 +63,8 @@ public abstract class AbstractParser {
String presence = null;
if (fromParts.length >= 2) {
presence = fromParts[1];
+ } else {
+ presence = "";
}
Contact contact = account.getRoster().getContact(from);
long timestamp = getTimestamp(packet);
@@ -73,4 +75,16 @@ public abstract class AbstractParser {
}
}
}
+
+ protected String avatarData(Element items) {
+ Element item = items.findChild("item");
+ if (item==null) {
+ return null;
+ }
+ Element data = item.findChild("data","urn:xmpp:avatar:data");
+ if (data==null) {
+ return null;
+ }
+ return data.getContent();
+ }
}
diff --git a/src/eu/siacs/conversations/parser/IqParser.java b/src/eu/siacs/conversations/parser/IqParser.java
index 023fb4df..a22ff6a5 100644
--- a/src/eu/siacs/conversations/parser/IqParser.java
+++ b/src/eu/siacs/conversations/parser/IqParser.java
@@ -27,19 +27,33 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
if (!contact.getOption(Contact.Options.DIRTY_PUSH)) {
contact.setServerName(name);
}
- if (subscription.equals("remove")) {
- contact.resetOption(Contact.Options.IN_ROSTER);
- contact.resetOption(Contact.Options.DIRTY_DELETE);
- contact.resetOption(Contact.Options.PREEMPTIVE_GRANT);
- } else {
- contact.setOption(Contact.Options.IN_ROSTER);
- contact.resetOption(Contact.Options.DIRTY_PUSH);
- contact.parseSubscriptionFromElement(item);
+ if (subscription!=null) {
+ if (subscription.equals("remove")) {
+ contact.resetOption(Contact.Options.IN_ROSTER);
+ contact.resetOption(Contact.Options.DIRTY_DELETE);
+ contact.resetOption(Contact.Options.PREEMPTIVE_GRANT);
+ } else {
+ contact.setOption(Contact.Options.IN_ROSTER);
+ contact.resetOption(Contact.Options.DIRTY_PUSH);
+ contact.parseSubscriptionFromElement(item);
+ }
}
}
}
mXmppConnectionService.updateRosterUi();
}
+
+ public String avatarData(IqPacket packet) {
+ Element pubsub = packet.findChild("pubsub", "http://jabber.org/protocol/pubsub");
+ if (pubsub==null) {
+ return null;
+ }
+ Element items = pubsub.findChild("items");
+ if (items==null) {
+ return null;
+ }
+ return super.avatarData(items);
+ }
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
diff --git a/src/eu/siacs/conversations/parser/MessageParser.java b/src/eu/siacs/conversations/parser/MessageParser.java
index a4fcc810..ad1ce9b9 100644
--- a/src/eu/siacs/conversations/parser/MessageParser.java
+++ b/src/eu/siacs/conversations/parser/MessageParser.java
@@ -1,15 +1,18 @@
package eu.siacs.conversations.parser;
import android.os.SystemClock;
+import android.util.Log;
import net.java.otr4j.session.Session;
import net.java.otr4j.session.SessionStatus;
import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.OnMessagePacketReceived;
+import eu.siacs.conversations.xmpp.pep.Avatar;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
public class MessageParser extends AbstractParser implements
@@ -37,6 +40,18 @@ public class MessageParser extends AbstractParser implements
packet.getBody(), Message.ENCRYPTION_NONE,
Message.STATUS_RECIEVED);
}
+ finishedMessage.setRemoteMsgId(packet.getId());
+ if (conversation.getMode() == Conversation.MODE_MULTI
+ && fromParts.length >= 2) {
+ finishedMessage.setType(Message.TYPE_PRIVATE);
+ finishedMessage.setPresence(fromParts[1]);
+ finishedMessage.setTrueCounterpart(conversation.getMucOptions()
+ .getTrueCounterpart(fromParts[1]));
+ if (conversation.hasDuplicateMessage(finishedMessage)) {
+ return null;
+ }
+
+ }
finishedMessage.setTime(getTimestamp(packet));
return finishedMessage;
}
@@ -47,25 +62,31 @@ public class MessageParser extends AbstractParser implements
String[] fromParts = packet.getFrom().split("/");
Conversation conversation = mXmppConnectionService
.findOrCreateConversation(account, fromParts[0], false);
+ String presence;
+ if (fromParts.length >= 2) {
+ presence = fromParts[1];
+ } else {
+ presence = "";
+ }
updateLastseen(packet, account, true);
String body = packet.getBody();
if (!conversation.hasValidOtrSession()) {
if (properlyAddressed) {
conversation.startOtrSession(
mXmppConnectionService.getApplicationContext(),
- fromParts[1], false);
+ presence, false);
} else {
return null;
}
} else {
String foreignPresence = conversation.getOtrSession()
.getSessionID().getUserID();
- if (!foreignPresence.equals(fromParts[1])) {
+ if (!foreignPresence.equals(presence)) {
conversation.endOtrIfNeeded();
if (properlyAddressed) {
conversation.startOtrSession(
mXmppConnectionService.getApplicationContext(),
- fromParts[1], false);
+ presence, false);
} else {
return null;
}
@@ -96,6 +117,7 @@ public class MessageParser extends AbstractParser implements
packet.getFrom(), body, Message.ENCRYPTION_OTR,
Message.STATUS_RECIEVED);
finishedMessage.setTime(getTimestamp(packet));
+ finishedMessage.setRemoteMsgId(packet.getId());
return finishedMessage;
} catch (Exception e) {
String receivedId = packet.getId();
@@ -110,7 +132,8 @@ public class MessageParser extends AbstractParser implements
private Message parseGroupchat(MessagePacket packet, Account account) {
int status;
String[] fromParts = packet.getFrom().split("/");
- if (mXmppConnectionService.find(account.pendingConferenceLeaves,account,fromParts[0]) != null) {
+ if (mXmppConnectionService.find(account.pendingConferenceLeaves,
+ account, fromParts[0]) != null) {
return null;
}
Conversation conversation = mXmppConnectionService
@@ -145,6 +168,14 @@ public class MessageParser extends AbstractParser implements
finishedMessage = new Message(conversation, counterPart, pgpBody,
Message.ENCRYPTION_PGP, status);
}
+ finishedMessage.setRemoteMsgId(packet.getId());
+ if (status == Message.STATUS_RECIEVED) {
+ finishedMessage.setTrueCounterpart(conversation.getMucOptions()
+ .getTrueCounterpart(counterPart));
+ }
+ if (packet.hasChild("delay") && conversation.hasDuplicateMessage(finishedMessage)) {
+ return null;
+ }
finishedMessage.setTime(getTimestamp(packet));
return finishedMessage;
}
@@ -167,24 +198,29 @@ public class MessageParser extends AbstractParser implements
}
Element message = forwarded.findChild("message");
if ((message == null) || (!message.hasChild("body"))) {
- if (status == Message.STATUS_RECIEVED) {
+ if (status == Message.STATUS_RECIEVED && message.getAttribute("from")!=null) {
parseNormal(message, account);
}
return null;
}
if (status == Message.STATUS_RECIEVED) {
fullJid = message.getAttribute("from");
- updateLastseen(message, account, true);
+ if (fullJid == null) {
+ return null;
+ } else {
+ updateLastseen(message, account, true);
+ }
} else {
fullJid = message.getAttribute("to");
- }
- if (fullJid==null) {
- return null;
+ if (fullJid == null) {
+ return null;
+ }
}
String[] parts = fullJid.split("/");
Conversation conversation = mXmppConnectionService
.findOrCreateConversation(account, parts[0], false);
conversation.setLatestMarkableMessageId(getMarkableMessageId(packet));
+
String pgpBody = getPgpBody(message);
Message finishedMessage;
if (pgpBody != null) {
@@ -196,6 +232,18 @@ public class MessageParser extends AbstractParser implements
Message.ENCRYPTION_NONE, status);
}
finishedMessage.setTime(getTimestamp(message));
+ finishedMessage.setRemoteMsgId(message.getAttribute("id"));
+ if (conversation.getMode() == Conversation.MODE_MULTI
+ && parts.length >= 2) {
+ finishedMessage.setType(Message.TYPE_PRIVATE);
+ finishedMessage.setPresence(parts[1]);
+ finishedMessage.setTrueCounterpart(conversation.getMucOptions()
+ .getTrueCounterpart(parts[1]));
+ if (conversation.hasDuplicateMessage(finishedMessage)) {
+ return null;
+ }
+ }
+
return finishedMessage;
}
@@ -206,6 +254,11 @@ public class MessageParser extends AbstractParser implements
}
private void parseNormal(Element packet, Account account) {
+ if (packet.hasChild("event", "http://jabber.org/protocol/pubsub#event")) {
+ Element event = packet.findChild("event",
+ "http://jabber.org/protocol/pubsub#event");
+ parseEvent(event, packet.getAttribute("from"), account);
+ }
if (packet.hasChild("displayed", "urn:xmpp:chat-markers:0")) {
String id = packet
.findChild("displayed", "urn:xmpp:chat-markers:0")
@@ -221,8 +274,9 @@ public class MessageParser extends AbstractParser implements
updateLastseen(packet, account, false);
mXmppConnectionService.markMessage(account, fromParts[0], id,
Message.STATUS_SEND_RECEIVED);
- } else if (packet.hasChild("x","http://jabber.org/protocol/muc#user")) {
- Element x = packet.findChild("x","http://jabber.org/protocol/muc#user");
+ } else if (packet.hasChild("x", "http://jabber.org/protocol/muc#user")) {
+ Element x = packet.findChild("x",
+ "http://jabber.org/protocol/muc#user");
if (x.hasChild("invite")) {
Conversation conversation = mXmppConnectionService
.findOrCreateConversation(account,
@@ -230,15 +284,15 @@ public class MessageParser extends AbstractParser implements
if (!conversation.getMucOptions().online()) {
mXmppConnectionService.joinMuc(conversation);
mXmppConnectionService.updateConversationUi();
- }
+ }
}
} else if (packet.hasChild("x", "jabber:x:conference")) {
Element x = packet.findChild("x", "jabber:x:conference");
String jid = x.getAttribute("jid");
- if (jid!=null) {
+ if (jid != null) {
Conversation conversation = mXmppConnectionService
- .findOrCreateConversation(account,jid, true);
+ .findOrCreateConversation(account, jid, true);
if (!conversation.getMucOptions().online()) {
mXmppConnectionService.joinMuc(conversation);
mXmppConnectionService.updateConversationUi();
@@ -247,6 +301,37 @@ public class MessageParser extends AbstractParser implements
}
}
+ private void parseEvent(Element event, String from, Account account) {
+ Element items = event.findChild("items");
+ String node = items.getAttribute("node");
+ if (node != null) {
+ if (node.equals("urn:xmpp:avatar:metadata")) {
+ Avatar avatar = Avatar.parseMetadata(items);
+ if (avatar!=null) {
+ avatar.owner = from;
+ if (mXmppConnectionService.getFileBackend().isAvatarCached(
+ avatar)) {
+ if (account.getJid().equals(from)) {
+ if (account.setAvatar(avatar.getFilename())) {
+ mXmppConnectionService.databaseBackend.updateAccount(account);
+ }
+ } else {
+ Contact contact = account.getRoster().getContact(from);
+ contact.setAvatar(avatar.getFilename());
+ }
+ } else {
+ mXmppConnectionService.fetchAvatar(account, avatar);
+ }
+ }
+ } else {
+ Log.d("xmppService", account.getJid() + ": " + node + " from "
+ + from);
+ }
+ } else {
+ Log.d("xmppService", event.toString());
+ }
+ }
+
private String getPgpBody(Element message) {
Element child = message.findChild("x", "jabber:x:encrypted");
if (child == null) {
@@ -282,7 +367,9 @@ public class MessageParser extends AbstractParser implements
}
} else if (packet.hasChild("body")) {
message = this.parseChat(packet, account);
- message.markUnread();
+ if (message != null) {
+ message.markUnread();
+ }
} else if (packet.hasChild("received") || (packet.hasChild("sent"))) {
message = this.parseCarbonMessage(packet, account);
if (message != null) {
@@ -306,8 +393,7 @@ public class MessageParser extends AbstractParser implements
message.markUnread();
} else {
message.getConversation().markRead();
- lastCarbonMessageReceived = SystemClock
- .elapsedRealtime();
+ lastCarbonMessageReceived = SystemClock.elapsedRealtime();
notify = false;
}
}
@@ -317,6 +403,9 @@ public class MessageParser extends AbstractParser implements
} else if (packet.getType() == MessagePacket.TYPE_NORMAL) {
this.parseNormal(packet, account);
return;
+ } else if (packet.getType() == MessagePacket.TYPE_HEADLINE) {
+ this.parseHeadline(packet, account);
+ return;
}
if ((message == null) || (message.getBody() == null)) {
return;
@@ -324,11 +413,15 @@ public class MessageParser extends AbstractParser implements
if ((mXmppConnectionService.confirmMessages())
&& ((packet.getId() != null))) {
if (packet.hasChild("markable", "urn:xmpp:chat-markers:0")) {
- MessagePacket receipt = mXmppConnectionService.getMessageGenerator().received(account, packet, "urn:xmpp:chat-markers:0");
+ MessagePacket receipt = mXmppConnectionService
+ .getMessageGenerator().received(account, packet,
+ "urn:xmpp:chat-markers:0");
mXmppConnectionService.sendMessagePacket(account, receipt);
}
if (packet.hasChild("request", "urn:xmpp:receipts")) {
- MessagePacket receipt = mXmppConnectionService.getMessageGenerator().received(account, packet, "urn:xmpp:receipts");
+ MessagePacket receipt = mXmppConnectionService
+ .getMessageGenerator().received(account, packet,
+ "urn:xmpp:receipts");
mXmppConnectionService.sendMessagePacket(account, receipt);
}
}
@@ -339,4 +432,12 @@ public class MessageParser extends AbstractParser implements
}
mXmppConnectionService.notifyUi(conversation, notify);
}
+
+ private void parseHeadline(MessagePacket packet, Account account) {
+ if (packet.hasChild("event", "http://jabber.org/protocol/pubsub#event")) {
+ Element event = packet.findChild("event",
+ "http://jabber.org/protocol/pubsub#event");
+ parseEvent(event, packet.getFrom(), account);
+ }
+ }
}
diff --git a/src/eu/siacs/conversations/parser/PresenceParser.java b/src/eu/siacs/conversations/parser/PresenceParser.java
index 33f4185f..ea19df6f 100644
--- a/src/eu/siacs/conversations/parser/PresenceParser.java
+++ b/src/eu/siacs/conversations/parser/PresenceParser.java
@@ -13,7 +13,7 @@ import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
public class PresenceParser extends AbstractParser implements
OnPresencePacketReceived {
-
+
public PresenceParser(XmppConnectionService service) {
super(service);
}
@@ -21,23 +21,31 @@ public class PresenceParser extends AbstractParser implements
public void parseConferencePresence(PresencePacket packet, Account account) {
PgpEngine mPgpEngine = mXmppConnectionService.getPgpEngine();
if (packet.hasChild("x", "http://jabber.org/protocol/muc#user")) {
- Conversation muc = mXmppConnectionService.find(account,packet
+ Conversation muc = mXmppConnectionService.find(account, packet
.getAttribute("from").split("/")[0]);
if (muc != null) {
+ boolean before = muc.getMucOptions().online();
muc.getMucOptions().processPacket(packet, mPgpEngine);
+ if (before!=muc.getMucOptions().online()) {
+ mXmppConnectionService.updateConversationUi();
+ }
}
} else if (packet.hasChild("x", "http://jabber.org/protocol/muc")) {
- Conversation muc = mXmppConnectionService.find(account,packet
+ Conversation muc = mXmppConnectionService.find(account, packet
.getAttribute("from").split("/")[0]);
if (muc != null) {
+ boolean before = muc.getMucOptions().online();
muc.getMucOptions().processPacket(packet, mPgpEngine);
+ if (before!=muc.getMucOptions().online()) {
+ mXmppConnectionService.updateConversationUi();
+ }
}
}
- mXmppConnectionService.updateConversationUi();
}
public void parseContactPresence(PresencePacket packet, Account account) {
- PresenceGenerator mPresenceGenerator = mXmppConnectionService.getPresenceGenerator();
+ PresenceGenerator mPresenceGenerator = mXmppConnectionService
+ .getPresenceGenerator();
if (packet.getFrom() == null) {
return;
}
@@ -56,30 +64,34 @@ public class PresenceParser extends AbstractParser implements
} else {
Contact contact = account.getRoster().getContact(packet.getFrom());
if (type == null) {
- if (fromParts.length == 2) {
- int sizeBefore = contact.getPresences().size();
- contact.updatePresence(fromParts[1],
- Presences.parseShow(packet.findChild("show")));
- PgpEngine pgp = mXmppConnectionService.getPgpEngine();
- if (pgp != null) {
- Element x = packet.findChild("x", "jabber:x:signed");
- if (x != null) {
- Element status = packet.findChild("status");
- String msg;
- if (status != null) {
- msg = status.getContent();
- } else {
- msg = "";
- }
- contact.setPgpKeyId(pgp.fetchKeyId(account, msg,
- x.getContent()));
+ String presence;
+ if (fromParts.length >= 2) {
+ presence = fromParts[1];
+ } else {
+ presence = "";
+ }
+ int sizeBefore = contact.getPresences().size();
+ contact.updatePresence(presence,
+ Presences.parseShow(packet.findChild("show")));
+ PgpEngine pgp = mXmppConnectionService.getPgpEngine();
+ if (pgp != null) {
+ Element x = packet.findChild("x", "jabber:x:signed");
+ if (x != null) {
+ Element status = packet.findChild("status");
+ String msg;
+ if (status != null) {
+ msg = status.getContent();
+ } else {
+ msg = "";
}
+ contact.setPgpKeyId(pgp.fetchKeyId(account, msg,
+ x.getContent()));
}
- boolean online = sizeBefore < contact.getPresences().size();
- updateLastseen(packet, account, true);
- mXmppConnectionService.onContactStatusChanged
- .onContactStatusChanged(contact, online);
}
+ boolean online = sizeBefore < contact.getPresences().size();
+ updateLastseen(packet, account, true);
+ mXmppConnectionService.onContactStatusChanged
+ .onContactStatusChanged(contact, online);
} else if (type.equals("unavailable")) {
if (fromParts.length != 2) {
contact.clearPresences();
@@ -90,7 +102,8 @@ public class PresenceParser extends AbstractParser implements
.onContactStatusChanged(contact, false);
} else if (type.equals("subscribe")) {
if (contact.getOption(Contact.Options.PREEMPTIVE_GRANT)) {
- mXmppConnectionService.sendPresencePacket(account, mPresenceGenerator.sendPresenceUpdatesTo(contact));
+ mXmppConnectionService.sendPresencePacket(account,
+ mPresenceGenerator.sendPresenceUpdatesTo(contact));
} else {
contact.setOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST);
}
diff --git a/src/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/eu/siacs/conversations/persistance/DatabaseBackend.java
index 7643076a..62736ffa 100644
--- a/src/eu/siacs/conversations/persistance/DatabaseBackend.java
+++ b/src/eu/siacs/conversations/persistance/DatabaseBackend.java
@@ -20,15 +20,18 @@ public class DatabaseBackend extends SQLiteOpenHelper {
private static DatabaseBackend instance = null;
private static final String DATABASE_NAME = "history";
- private static final int DATABASE_VERSION = 5;
+ private static final int DATABASE_VERSION = 7;
private static String CREATE_CONTATCS_STATEMENT = "create table "
- + Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, " + Contact.SERVERNAME + " TEXT, "
- + Contact.SYSTEMNAME + " TEXT," + Contact.JID + " TEXT,"
- + Contact.KEYS + " TEXT," + Contact.PHOTOURI + " TEXT,"
- + Contact.OPTIONS + " NUMBER," + Contact.SYSTEMACCOUNT
- + " NUMBER, " + "FOREIGN KEY(" + Contact.ACCOUNT + ") REFERENCES "
- + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, UNIQUE("+Contact.ACCOUNT+", "+Contact.JID+") ON CONFLICT REPLACE);";
+ + Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, "
+ + Contact.SERVERNAME + " TEXT, " + Contact.SYSTEMNAME + " TEXT,"
+ + Contact.JID + " TEXT," + Contact.KEYS + " TEXT,"
+ + Contact.PHOTOURI + " TEXT," + Contact.OPTIONS + " NUMBER,"
+ + Contact.SYSTEMACCOUNT + " NUMBER, " + Contact.AVATAR + " TEXT, "
+ + "FOREIGN KEY(" + Contact.ACCOUNT + ") REFERENCES "
+ + Account.TABLENAME + "(" + Account.UUID
+ + ") ON DELETE CASCADE, UNIQUE(" + Contact.ACCOUNT + ", "
+ + Contact.JID + ") ON CONFLICT REPLACE);";
public DatabaseBackend(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
@@ -41,7 +44,8 @@ public class DatabaseBackend extends SQLiteOpenHelper {
+ " TEXT PRIMARY KEY," + Account.USERNAME + " TEXT,"
+ Account.SERVER + " TEXT," + Account.PASSWORD + " TEXT,"
+ Account.ROSTERVERSION + " TEXT," + Account.OPTIONS
- + " NUMBER, " + Account.KEYS + " TEXT)");
+ + " NUMBER, " + Account.AVATAR + " TEXT, " + Account.KEYS
+ + " TEXT)");
db.execSQL("create table " + Conversation.TABLENAME + " ("
+ Conversation.UUID + " TEXT PRIMARY KEY, " + Conversation.NAME
+ " TEXT, " + Conversation.CONTACT + " TEXT, "
@@ -54,11 +58,13 @@ public class DatabaseBackend extends SQLiteOpenHelper {
db.execSQL("create table " + Message.TABLENAME + "( " + Message.UUID
+ " TEXT PRIMARY KEY, " + Message.CONVERSATION + " TEXT, "
+ Message.TIME_SENT + " NUMBER, " + Message.COUNTERPART
- + " TEXT, " + Message.BODY + " TEXT, " + Message.ENCRYPTION
- + " NUMBER, " + Message.STATUS + " NUMBER," + Message.TYPE
- + " NUMBER, FOREIGN KEY(" + Message.CONVERSATION
- + ") REFERENCES " + Conversation.TABLENAME + "("
- + Conversation.UUID + ") ON DELETE CASCADE);");
+ + " TEXT, " + Message.TRUE_COUNTERPART + " TEXT,"
+ + Message.BODY + " TEXT, " + Message.ENCRYPTION + " NUMBER, "
+ + Message.STATUS + " NUMBER," + Message.TYPE + " NUMBER, "
+ + Message.REMOTE_MSG_ID + " TEXT, FOREIGN KEY("
+ + Message.CONVERSATION + ") REFERENCES "
+ + Conversation.TABLENAME + "(" + Conversation.UUID
+ + ") ON DELETE CASCADE);");
db.execSQL(CREATE_CONTATCS_STATEMENT);
}
@@ -74,9 +80,22 @@ public class DatabaseBackend extends SQLiteOpenHelper {
+ Message.TYPE + " NUMBER");
}
if (oldVersion < 5 && newVersion >= 5) {
- db.execSQL("DROP TABLE "+Contact.TABLENAME);
+ db.execSQL("DROP TABLE " + Contact.TABLENAME);
db.execSQL(CREATE_CONTATCS_STATEMENT);
- db.execSQL("UPDATE "+Account.TABLENAME+ " SET "+Account.ROSTERVERSION+" = NULL");
+ db.execSQL("UPDATE " + Account.TABLENAME + " SET "
+ + Account.ROSTERVERSION + " = NULL");
+ }
+ if (oldVersion < 6 && newVersion >= 6) {
+ db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
+ + Message.TRUE_COUNTERPART + " TEXT");
+ }
+ if (oldVersion < 7 && newVersion >= 7) {
+ db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
+ + Message.REMOTE_MSG_ID + " TEXT");
+ db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
+ + Contact.AVATAR + " TEXT");
+ db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN "
+ + Account.AVATAR + " TEXT");
}
}
@@ -128,24 +147,27 @@ public class DatabaseBackend extends SQLiteOpenHelper {
}
return list;
}
-
- public List<Message> getMessages(Conversation conversations, int limit) {
- return getMessages(conversations, limit,-1);
+
+ public CopyOnWriteArrayList<Message> getMessages(
+ Conversation conversations, int limit) {
+ return getMessages(conversations, limit, -1);
}
- public List<Message> getMessages(Conversation conversation, int limit, long timestamp) {
- List<Message> list = new CopyOnWriteArrayList<Message>();
+ public CopyOnWriteArrayList<Message> getMessages(Conversation conversation,
+ int limit, long timestamp) {
+ CopyOnWriteArrayList<Message> list = new CopyOnWriteArrayList<Message>();
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor;
- if (timestamp==-1) {
+ if (timestamp == -1) {
String[] selectionArgs = { conversation.getUuid() };
cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
- + "=?", selectionArgs, null, null, Message.TIME_SENT + " DESC",
- String.valueOf(limit));
+ + "=?", selectionArgs, null, null, Message.TIME_SENT
+ + " DESC", String.valueOf(limit));
} else {
- String[] selectionArgs = { conversation.getUuid() , ""+timestamp};
+ String[] selectionArgs = { conversation.getUuid(), "" + timestamp };
cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
- + "=? and "+Message.TIME_SENT+"<?", selectionArgs, null, null, Message.TIME_SENT + " DESC",
+ + "=? and " + Message.TIME_SENT + "<?", selectionArgs,
+ null, null, Message.TIME_SENT + " DESC",
String.valueOf(limit));
}
if (cursor.getCount() > 0) {
@@ -225,16 +247,16 @@ public class DatabaseBackend extends SQLiteOpenHelper {
roster.initContact(Contact.fromCursor(cursor));
}
}
-
+
public void writeRoster(Roster roster) {
Account account = roster.getAccount();
SQLiteDatabase db = this.getWritableDatabase();
- for(Contact contact : roster.getContacts()) {
+ for (Contact contact : roster.getContacts()) {
if (contact.getOption(Contact.Options.IN_ROSTER)) {
db.insert(Contact.TABLENAME, null, contact.getContentValues());
} else {
- String where = Contact.ACCOUNT + "=? AND "+Contact.JID+"=?";
- String[] whereArgs = {account.getUuid(), contact.getJid()};
+ String where = Contact.ACCOUNT + "=? AND " + Contact.JID + "=?";
+ String[] whereArgs = { account.getUuid(), contact.getJid() };
db.delete(Contact.TABLENAME, where, whereArgs);
}
}
diff --git a/src/eu/siacs/conversations/persistance/FileBackend.java b/src/eu/siacs/conversations/persistance/FileBackend.java
index 1ee68a27..8fdc7ee7 100644
--- a/src/eu/siacs/conversations/persistance/FileBackend.java
+++ b/src/eu/siacs/conversations/persistance/FileBackend.java
@@ -1,25 +1,42 @@
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.security.DigestOutputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
import android.content.Context;
+import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
import android.graphics.Matrix;
+import android.graphics.RectF;
import android.media.ExifInterface;
import android.net.Uri;
+import android.os.Environment;
+import android.provider.MediaStore;
+import android.util.Base64;
+import android.util.Base64OutputStream;
import android.util.Log;
import android.util.LruCache;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.services.ImageProvider;
+import eu.siacs.conversations.utils.CryptoHelper;
+import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.jingle.JingleFile;
+import eu.siacs.conversations.xmpp.pep.Avatar;
public class FileBackend {
@@ -27,6 +44,8 @@ public class FileBackend {
private Context context;
private LruCache<String, Bitmap> thumbnailCache;
+
+ private SimpleDateFormat imageDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS",Locale.US);
public FileBackend(Context context) {
this.context = context;
@@ -45,11 +64,11 @@ public class FileBackend {
return thumbnailCache;
}
- public JingleFile getJingleFile(Message message) {
- return getJingleFile(message, true);
+ public JingleFile getJingleFileLegacy(Message message) {
+ return getJingleFileLegacy(message, true);
}
- public JingleFile getJingleFile(Message message, boolean decrypted) {
+ public JingleFile getJingleFileLegacy(Message message, boolean decrypted) {
Conversation conversation = message.getConversation();
String prefix = context.getFilesDir().getAbsolutePath();
String path = prefix + "/" + conversation.getAccount().getJid() + "/"
@@ -66,7 +85,28 @@ public class FileBackend {
}
return new JingleFile(path + "/" + filename);
}
+
+ public JingleFile getJingleFile(Message message) {
+ return getJingleFile(message, true);
+ }
+ public JingleFile getJingleFile(Message message, boolean decrypted) {
+ StringBuilder filename = new StringBuilder();
+ filename.append(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath());
+ filename.append("/Conversations/");
+ filename.append(message.getUuid());
+ if ((decrypted) || (message.getEncryption() == Message.ENCRYPTION_NONE)) {
+ filename.append(".webp");
+ } else {
+ if (message.getEncryption() == Message.ENCRYPTION_OTR) {
+ filename.append(".webp");
+ } else {
+ filename.append(".webp.pgp");
+ }
+ }
+ return new JingleFile(filename.toString());
+ }
+
public Bitmap resize(Bitmap originalBitmap, int size) {
int w = originalBitmap.getWidth();
int h = originalBitmap.getHeight();
@@ -104,13 +144,7 @@ public class FileBackend {
private JingleFile copyImageToPrivateStorage(Message message, Uri image,
int sampleSize) throws ImageCopyException {
try {
- InputStream is;
- if (image != null) {
- is = context.getContentResolver().openInputStream(image);
- } else {
- is = new FileInputStream(getIncomingFile());
- image = getIncomingUri();
- }
+ InputStream is = context.getContentResolver().openInputStream(image);
JingleFile file = getJingleFile(message);
file.getParentFile().mkdirs();
file.createNewFile();
@@ -125,21 +159,11 @@ public class FileBackend {
if (originalBitmap == null) {
throw new ImageCopyException(R.string.error_not_an_image_file);
}
- if (image == null) {
- getIncomingFile().delete();
- }
Bitmap scalledBitmap = resize(originalBitmap, IMAGE_SIZE);
originalBitmap = null;
- ExifInterface exif = new ExifInterface(image.toString());
- if (exif.getAttribute(ExifInterface.TAG_ORIENTATION)
- .equalsIgnoreCase("6")) {
- scalledBitmap = rotate(scalledBitmap, 90);
- } else if (exif.getAttribute(ExifInterface.TAG_ORIENTATION)
- .equalsIgnoreCase("8")) {
- scalledBitmap = rotate(scalledBitmap, 270);
- } else if (exif.getAttribute(ExifInterface.TAG_ORIENTATION)
- .equalsIgnoreCase("3")) {
- scalledBitmap = rotate(scalledBitmap, 180);
+ int rotation = getRotation(image);
+ if (rotation > 0) {
+ scalledBitmap = rotate(scalledBitmap, rotation);
}
OutputStream os = new FileOutputStream(file);
boolean success = scalledBitmap.compress(
@@ -170,6 +194,38 @@ public class FileBackend {
}
}
}
+
+ private int getRotation(Uri image) {
+ if ("content".equals(image.getScheme())) {
+ Cursor cursor = context.getContentResolver().query(image,
+ new String[] { MediaStore.Images.ImageColumns.ORIENTATION }, null, null, null);
+
+ if (cursor.getCount() != 1) {
+ return -1;
+ }
+ cursor.moveToFirst();
+ return cursor.getInt(0);
+ } else {
+ ExifInterface exif;
+ try {
+ exif = new ExifInterface(image.toString());
+ if (exif.getAttribute(ExifInterface.TAG_ORIENTATION)
+ .equalsIgnoreCase("6")) {
+ return 90;
+ } else if (exif.getAttribute(ExifInterface.TAG_ORIENTATION)
+ .equalsIgnoreCase("8")) {
+ return 270;
+ } else if (exif.getAttribute(ExifInterface.TAG_ORIENTATION)
+ .equalsIgnoreCase("3")) {
+ return 180;
+ } else {
+ return 0;
+ }
+ } catch (IOException e) {
+ return -1;
+ }
+ }
+ }
public Bitmap getImageFromMessage(Message message) {
return BitmapFactory.decodeFile(getJingleFile(message)
@@ -180,8 +236,11 @@ public class FileBackend {
throws FileNotFoundException {
Bitmap thumbnail = thumbnailCache.get(message.getUuid());
if ((thumbnail == null) && (!cacheOnly)) {
- Bitmap fullsize = BitmapFactory.decodeFile(getJingleFile(message)
- .getAbsolutePath());
+ File file = getJingleFile(message);
+ if (!file.exists()) {
+ file = getJingleFileLegacy(message);
+ }
+ Bitmap fullsize = BitmapFactory.decodeFile(file.getAbsolutePath());
if (fullsize == null) {
throw new FileNotFoundException();
}
@@ -212,12 +271,155 @@ public class FileBackend {
f.delete();
}
- public File getIncomingFile() {
- return new File(context.getFilesDir().getAbsolutePath() + "/incoming");
+ public Uri getTakePhotoUri() {
+ StringBuilder pathBuilder = new StringBuilder();
+ pathBuilder.append(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM));
+ pathBuilder.append('/');
+ pathBuilder.append("Camera");
+ pathBuilder.append('/');
+ pathBuilder.append("IMG_"+this.imageDateFormat.format(new Date())+".jpg");
+ Uri uri = Uri.parse("file://"+pathBuilder.toString());
+ File file = new File(uri.toString());
+ file.getParentFile().mkdirs();
+ return uri;
+ }
+
+ public Avatar getPepAvatar(Uri image, int size, Bitmap.CompressFormat format) {
+ try {
+ Avatar avatar = new Avatar();
+ Bitmap bm = cropCenterSquare(image, size);
+ if (bm==null) {
+ return null;
+ }
+ ByteArrayOutputStream mByteArrayOutputStream = new ByteArrayOutputStream();
+ Base64OutputStream mBase64OutputSttream = new Base64OutputStream(mByteArrayOutputStream, Base64.DEFAULT);
+ MessageDigest digest = MessageDigest.getInstance("SHA-1");
+ DigestOutputStream mDigestOutputStream = new DigestOutputStream(mBase64OutputSttream, digest);
+ if (!bm.compress(format, 75, mDigestOutputStream)) {
+ return null;
+ }
+ mDigestOutputStream.flush();
+ mDigestOutputStream.close();
+ avatar.sha1sum = CryptoHelper.bytesToHex(digest.digest());
+ avatar.image = new String(mByteArrayOutputStream.toByteArray());
+ return avatar;
+ } catch (NoSuchAlgorithmException e) {
+ return null;
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ public boolean isAvatarCached(Avatar avatar) {
+ File file = new File(getAvatarPath(context, avatar.getFilename()));
+ return file.exists();
+ }
+
+ public boolean save(Avatar avatar) {
+ if (isAvatarCached(avatar)) {
+ return true;
+ }
+ String filename = getAvatarPath(context, 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("xmppService","sha1sum mismatch for "+avatar.owner);
+ file.delete();
+ return false;
+ }
+ } catch (FileNotFoundException e) {
+ return false;
+ } catch (IOException e) {
+ return false;
+ } catch (NoSuchAlgorithmException e) {
+ return false;
+ }
+ }
+
+ public static String getAvatarPath(Context context, String avatar) {
+ return context.getFilesDir().getAbsolutePath() + "/avatars/"+avatar;
+ }
+
+ public Bitmap cropCenterSquare(Uri image, int size) {
+ try {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inSampleSize = calcSampleSize(image, size);
+ InputStream is = context.getContentResolver()
+ .openInputStream(image);
+ Bitmap input = BitmapFactory.decodeStream(is, null, options);
+ if (input==null) {
+ return null;
+ } else {
+ return cropCenterSquare(input, size);
+ }
+ } catch (FileNotFoundException e) {
+ return null;
+ }
}
- public Uri getIncomingUri() {
- return Uri.parse(context.getFilesDir().getAbsolutePath() + "/incoming");
+ public static Bitmap cropCenterSquare(Bitmap input, int size) {
+ int w = input.getWidth();
+ int h = input.getHeight();
+
+ float scale = Math.max((float) size / h, (float) size / w);
+
+ float outWidth = scale * w;
+ float outHeight = scale * h;
+ float left = (size - outWidth) / 2;
+ float top = (size - outHeight) / 2;
+ RectF target = new RectF(left, top, left + outWidth, top
+ + outHeight);
+
+ Bitmap output = Bitmap.createBitmap(size, size, input.getConfig());
+ Canvas canvas = new Canvas(output);
+ canvas.drawBitmap(input, null, target, null);
+ return output;
+ }
+
+ private int calcSampleSize(Uri image, int size)
+ throws FileNotFoundException {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeStream(context.getContentResolver()
+ .openInputStream(image), null, options);
+ int height = options.outHeight;
+ int width = options.outWidth;
+ int inSampleSize = 1;
+
+ if (height > size || width > size) {
+ int halfHeight = height / 2;
+ int halfWidth = width / 2;
+
+ while ((halfHeight / inSampleSize) > size
+ && (halfWidth / inSampleSize) > size) {
+ inSampleSize *= 2;
+ }
+ }
+ return inSampleSize;
+
+ }
+
+ public Uri getJingleFileUri(Message message) {
+ File file = getJingleFile(message);
+ if (file.exists()) {
+ return Uri.parse("file://"+file.getAbsolutePath());
+ } else {
+ return ImageProvider.getProviderUri(message);
+ }
}
public class ImageCopyException extends Exception {
@@ -232,4 +434,12 @@ public class FileBackend {
return resId;
}
}
+
+ public static Bitmap getAvatar(String avatar, int size, Context context) {
+ Bitmap bm = BitmapFactory.decodeFile(FileBackend.getAvatarPath(context, avatar));
+ if (bm==null) {
+ return null;
+ }
+ return cropCenterSquare(bm, UIHelper.getRealPx(size, context));
+ }
}
diff --git a/src/eu/siacs/conversations/services/Defaults.java b/src/eu/siacs/conversations/services/Defaults.java
new file mode 100644
index 00000000..c942dd48
--- /dev/null
+++ b/src/eu/siacs/conversations/services/Defaults.java
@@ -0,0 +1,11 @@
+package eu.siacs.conversations.services;
+
+import android.graphics.Bitmap;
+
+public final class Defaults {
+ public static final int AVATAR_SIZE = 192;
+ public static final Bitmap.CompressFormat AVATAR_FORMAT = Bitmap.CompressFormat.WEBP;
+ private Defaults() {
+
+ }
+}
diff --git a/src/eu/siacs/conversations/services/ImageProvider.java b/src/eu/siacs/conversations/services/ImageProvider.java
index 80cfbd95..7ab57f5e 100644
--- a/src/eu/siacs/conversations/services/ImageProvider.java
+++ b/src/eu/siacs/conversations/services/ImageProvider.java
@@ -61,19 +61,10 @@ public class ImageProvider extends ContentProvider {
message.setConversation(conversation);
conversation.setAccount(account);
- File file = fileBackend.getJingleFile(message);
+ File file = fileBackend.getJingleFileLegacy(message);
pfd = ParcelFileDescriptor.open(file,
ParcelFileDescriptor.MODE_READ_ONLY);
return pfd;
- } else if ("w".equals(mode)){
- File file = fileBackend.getIncomingFile();
- try {
- file.createNewFile();
- } catch (IOException e) {
- throw new FileNotFoundException();
- }
- pfd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE);
- return pfd;
} else {
throw new FileNotFoundException();
}
@@ -110,7 +101,7 @@ public class ImageProvider extends ContentProvider {
return 0;
}
- public static Uri getContentUri(Message message) {
+ public static Uri getProviderUri(Message message) {
return Uri
.parse("content://eu.siacs.conversations.images/"
+ message.getConversationUuid()
@@ -118,8 +109,4 @@ public class ImageProvider extends ContentProvider {
+ message.getUuid()
+ ".webp");
}
-
- public static Uri getIncomingContentUri() {
- return Uri.parse("content://eu.siacs.conversations.images/incoming");
- }
} \ No newline at end of file
diff --git a/src/eu/siacs/conversations/services/XmppConnectionService.java b/src/eu/siacs/conversations/services/XmppConnectionService.java
index c9813d82..c535f1a3 100644
--- a/src/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/eu/siacs/conversations/services/XmppConnectionService.java
@@ -20,6 +20,7 @@ import de.duenndns.ssl.MemorizingTrustManager;
import net.java.otr4j.OtrException;
import net.java.otr4j.session.Session;
import net.java.otr4j.session.SessionStatus;
+import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.PgpEngine;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Bookmark;
@@ -53,6 +54,7 @@ import eu.siacs.conversations.xmpp.XmppConnection;
import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager;
import eu.siacs.conversations.xmpp.jingle.OnJinglePacketReceived;
import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
+import eu.siacs.conversations.xmpp.pep.Avatar;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
@@ -64,6 +66,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.ContentObserver;
+import android.graphics.Bitmap;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
@@ -109,6 +112,7 @@ public class XmppConnectionService extends Service {
private OnConversationUpdate mOnConversationUpdate = null;
private int convChangedListenerCount = 0;
private OnAccountUpdate mOnAccountUpdate = null;
+ private int accountChangedListenerCount = 0;
private OnRosterUpdate mOnRosterUpdate = null;
public OnContactStatusChanged onContactStatusChanged = new OnContactStatusChanged() {
@@ -173,13 +177,16 @@ public class XmppConnectionService extends Service {
reconnectAccount(account, true);
} else if ((account.getStatus() != Account.STATUS_CONNECTING)
&& (account.getStatus() != Account.STATUS_NO_INTERNET)) {
- int next = account.getXmppConnection().getTimeToNextAttempt();
- Log.d(LOGTAG, account.getJid()
- + ": error connecting account. try again in " + next
- + "s for the "
- + (account.getXmppConnection().getAttempt() + 1)
- + " time");
- scheduleWakeupCall(next, false);
+ XmppConnection connection = account.getXmppConnection();
+ if (connection!=null) {
+ int next = connection.getTimeToNextAttempt();
+ Log.d(LOGTAG, account.getJid()
+ + ": error connecting account. try again in " + next
+ + "s for the "
+ + (connection.getAttempt() + 1)
+ + " time");
+ scheduleWakeupCall((int) (next * 1.2), false);
+ }
}
UIHelper.showErrorNotification(getApplicationContext(),
getAccounts());
@@ -313,15 +320,13 @@ public class XmppConnectionService extends Service {
}
}
if (account.getStatus() == Account.STATUS_ONLINE) {
- long lastReceived = account.getXmppConnection().lastPaketReceived;
- long lastSent = account.getXmppConnection().lastPingSent;
+ long lastReceived = account.getXmppConnection().getLastPacketReceived();
+ long lastSent = account.getXmppConnection().getLastPingSent();
if (lastSent - lastReceived >= PING_TIMEOUT * 1000) {
Log.d(LOGTAG, account.getJid() + ": ping timeout");
this.reconnectAccount(account, true);
} else if (SystemClock.elapsedRealtime() - lastReceived >= PING_MIN_INTERVAL * 1000) {
account.getXmppConnection().sendPing();
- account.getXmppConnection().lastPingSent = SystemClock
- .elapsedRealtime();
this.scheduleWakeupCall(2, false);
}
} else if (account.getStatus() == Account.STATUS_OFFLINE) {
@@ -329,12 +334,10 @@ public class XmppConnectionService extends Service {
account.setXmppConnection(this
.createConnection(account));
}
- account.getXmppConnection().lastPingSent = SystemClock
- .elapsedRealtime();
new Thread(account.getXmppConnection()).start();
} else if ((account.getStatus() == Account.STATUS_CONNECTING)
&& ((SystemClock.elapsedRealtime() - account
- .getXmppConnection().lastConnect) / 1000 >= CONNECT_TIMEOUT)) {
+ .getXmppConnection().getLastConnect()) / 1000 >= CONNECT_TIMEOUT)) {
Log.d(LOGTAG, account.getJid()
+ ": time out during connect reconnecting");
reconnectAccount(account, true);
@@ -525,7 +528,7 @@ public class XmppConnectionService extends Service {
} else {
message.getConversation().endOtrIfNeeded();
failWaitingOtrMessages(message.getConversation());
- if (message.getConversation().getMode() == Conversation.MODE_SINGLE) {
+ if (message.getConversation().getMode() == Conversation.MODE_SINGLE || message.getType() == Message.TYPE_PRIVATE) {
message.setStatus(Message.STATUS_SEND);
}
packet = mMessageGenerator.generateChat(message);
@@ -668,7 +671,7 @@ public class XmppConnectionService extends Service {
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
Element query = packet.query();
- List<Bookmark> bookmarks = new ArrayList<Bookmark>();
+ List<Bookmark> bookmarks = new CopyOnWriteArrayList<Bookmark>();
Element storage = query.findChild("storage", "storage:bookmarks");
if (storage!=null) {
for(Element item : storage.getChildren()) {
@@ -921,10 +924,14 @@ public class XmppConnectionService extends Service {
public void setOnAccountListChangedListener(OnAccountUpdate listener) {
this.mOnAccountUpdate = listener;
+ this.accountChangedListenerCount++;
}
public void removeOnAccountListChangedListener() {
- this.mOnAccountUpdate = null;
+ this.accountChangedListenerCount--;
+ if (this.accountChangedListenerCount == 0) {
+ this.mOnAccountUpdate = null;
+ }
}
public void setOnRosterUpdateListener(OnRosterUpdate listener) {
@@ -1183,6 +1190,123 @@ public class XmppConnectionService extends Service {
}
}
+
+ public void publishAvatar(Account account, Uri image, final UiCallback<Avatar> callback) {
+ final Bitmap.CompressFormat format = Defaults.AVATAR_FORMAT;
+ final int size = Defaults.AVATAR_SIZE;
+ final Avatar avatar = getFileBackend().getPepAvatar(image, size, format);
+ if (avatar!=null) {
+ avatar.height = size;
+ avatar.width = size;
+ if (format.equals(Bitmap.CompressFormat.WEBP)) {
+ avatar.type = "image/webp";
+ } else if (format.equals(Bitmap.CompressFormat.JPEG)) {
+ avatar.type = "image/jpeg";
+ } else if (format.equals(Bitmap.CompressFormat.PNG)) {
+ avatar.type = "image/png";
+ }
+ if (!getFileBackend().save(avatar)) {
+ callback.error(R.string.error_saving_avatar, avatar);
+ return;
+ }
+ IqPacket packet = this.mIqGenerator.publishAvatar(avatar);
+ this.sendIqPacket(account, packet, new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket result) {
+ if (result.getType() == IqPacket.TYPE_RESULT) {
+ IqPacket packet = XmppConnectionService.this.mIqGenerator.publishAvatarMetadata(avatar);
+ sendIqPacket(account, packet, new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket result) {
+ if (result.getType() == IqPacket.TYPE_RESULT) {
+ if (account.setAvatar(avatar.getFilename())) {
+ databaseBackend.updateAccount(account);
+ }
+ callback.success(avatar);
+ } else {
+ callback.error(R.string.error_publish_avatar_server_reject, avatar);
+ }
+ }
+ });
+ } else {
+ callback.error(R.string.error_publish_avatar_server_reject, avatar);
+ }
+ }
+ });
+ } else {
+ callback.error(R.string.error_publish_avatar_converting, null);
+ }
+ }
+
+ public void fetchAvatar(Account account, Avatar avatar) {
+ fetchAvatar(account, avatar, null);
+ }
+
+ public void fetchAvatar(Account account, final Avatar avatar, final UiCallback<Avatar> callback) {
+ Log.d(LOGTAG,account.getJid()+": retrieving avatar for "+avatar.owner);
+ IqPacket packet = this.mIqGenerator.retrieveAvatar(avatar);
+ sendIqPacket(account, packet, new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket result) {
+ avatar.image = mIqParser.avatarData(result);
+ if (avatar.image!=null) {
+ if (getFileBackend().save(avatar)) {
+ if (account.getJid().equals(avatar.owner)) {
+ if (account.setAvatar(avatar.getFilename())) {
+ databaseBackend.updateAccount(account);
+ }
+ } else {
+ Contact contact = account.getRoster().getContact(avatar.owner);
+ contact.setAvatar(avatar.getFilename());
+ }
+ if (callback!=null) {
+ callback.success(avatar);
+ }
+ return;
+ }
+ }
+ if (callback!=null) {
+ callback.error(0, null);
+ }
+ }
+ });
+ }
+
+ public void checkForAvatar(Account account, final UiCallback<Avatar> callback) {
+ IqPacket packet = this.mIqGenerator.retrieveAvatarMetaData(null);
+ this.sendIqPacket(account, packet, new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE_RESULT) {
+ Element pubsub = packet.findChild("pubsub", "http://jabber.org/protocol/pubsub");
+ if (pubsub!=null) {
+ Element items = pubsub.findChild("items");
+ if (items!=null) {
+ Avatar avatar = Avatar.parseMetadata(items);
+ if (avatar!=null) {
+ avatar.owner = account.getJid();
+ if (fileBackend.isAvatarCached(avatar)) {
+ if (account.setAvatar(avatar.getFilename())) {
+ databaseBackend.updateAccount(account);
+ }
+ callback.success(avatar);
+ } else {
+ fetchAvatar(account, avatar,callback);
+ }
+ return;
+ }
+ }
+ }
+ }
+ callback.error(0, null);
+ }
+ });
+ }
+
public void deleteContactOnServer(Contact contact) {
contact.resetOption(Contact.Options.PREEMPTIVE_GRANT);
contact.resetOption(Contact.Options.DIRTY_PUSH);
diff --git a/src/eu/siacs/conversations/ui/ConferenceDetailsActivity.java b/src/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
index 56903da8..c33277f9 100644
--- a/src/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
+++ b/src/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
@@ -7,6 +7,8 @@ import org.openintents.openpgp.util.OpenPgpUtils;
import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.PgpEngine;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.entities.MucOptions.OnRenameListener;
@@ -54,7 +56,7 @@ public class ConferenceDetailsActivity extends XmppActivity {
private List<User> users = new ArrayList<MucOptions.User>();
private OnConversationUpdate onConvChanged = new OnConversationUpdate() {
-
+
@Override
public void onConversationUpdate() {
runOnUiThread(new Runnable() {
@@ -150,13 +152,14 @@ public class ConferenceDetailsActivity extends XmppActivity {
this.uuid = getIntent().getExtras().getString("uuid");
}
if (uuid != null) {
- this.conversation = xmppConnectionService.findConversationByUuid(uuid);
+ this.conversation = xmppConnectionService
+ .findConversationByUuid(uuid);
if (this.conversation != null) {
populateView();
}
}
}
-
+
@Override
protected void onStop() {
if (xmppConnectionServiceBound) {
@@ -164,39 +167,39 @@ public class ConferenceDetailsActivity extends XmppActivity {
}
super.onStop();
}
-
+
protected void registerListener() {
if (xmppConnectionServiceBound) {
xmppConnectionService
.setOnConversationListChangedListener(this.onConvChanged);
- xmppConnectionService.setOnRenameListener(new OnRenameListener() {
+ xmppConnectionService.setOnRenameListener(new OnRenameListener() {
- @Override
- public void onRename(final boolean success) {
- runOnUiThread(new Runnable() {
+ @Override
+ public void onRename(final boolean success) {
+ runOnUiThread(new Runnable() {
- @Override
- public void run() {
- populateView();
- if (success) {
- Toast.makeText(ConferenceDetailsActivity.this,
- getString(R.string.your_nick_has_been_changed),
- Toast.LENGTH_SHORT).show();
- } else {
- Toast.makeText(ConferenceDetailsActivity.this,
- getString(R.string.nick_in_use),
- Toast.LENGTH_SHORT).show();
+ @Override
+ public void run() {
+ populateView();
+ if (success) {
+ Toast.makeText(
+ ConferenceDetailsActivity.this,
+ getString(R.string.your_nick_has_been_changed),
+ Toast.LENGTH_SHORT).show();
+ } else {
+ Toast.makeText(ConferenceDetailsActivity.this,
+ getString(R.string.nick_in_use),
+ Toast.LENGTH_SHORT).show();
+ }
}
- }
- });
- }
- });
+ });
+ }
+ });
}
}
private void populateView() {
- mYourPhoto.setImageBitmap(UIHelper.getContactPicture(conversation
- .getMucOptions().getActualNick(), 48, this, false));
+ mYourPhoto.setImageBitmap(conversation.getAccount().getImage(this, 48));
setTitle(conversation.getName(true));
mFullJid.setText(conversation.getContactJid().split("/")[0]);
mYourNick.setText(conversation.getMucOptions().getActualNick());
@@ -222,28 +225,44 @@ public class ConferenceDetailsActivity extends XmppActivity {
this.users.addAll(conversation.getMucOptions().getUsers());
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
membersView.removeAllViews();
- for (final User contact : conversation.getMucOptions().getUsers()) {
- View view = (View) inflater.inflate(R.layout.contact, null);
- TextView displayName = (TextView) view
+ Account account = conversation.getAccount();
+ for (final User user : conversation.getMucOptions().getUsers()) {
+ View view = (View) inflater.inflate(R.layout.contact, membersView,
+ false);
+ TextView name = (TextView) view
.findViewById(R.id.contact_display_name);
TextView key = (TextView) view.findViewById(R.id.key);
- displayName.setText(contact.getName());
TextView role = (TextView) view.findViewById(R.id.contact_jid);
- role.setText(getReadableRole(contact.getRole()));
- if (contact.getPgpKeyId() != 0) {
+ if (user.getPgpKeyId() != 0) {
key.setVisibility(View.VISIBLE);
key.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- viewPgpKey(contact);
+ viewPgpKey(user);
}
});
- key.setText(OpenPgpUtils.convertKeyIdToHex(contact
- .getPgpKeyId()));
+ key.setText(OpenPgpUtils.convertKeyIdToHex(user.getPgpKeyId()));
+ }
+ Bitmap bm;
+ if (user.getJid() != null) {
+ Contact contact = account.getRoster().getContact(user.getJid());
+ if (contact.showInRoster()) {
+ bm = contact.getImage(48, this);
+ name.setText(contact.getDisplayName());
+ role.setText(user.getName() + " \u2022 " + getReadableRole(user.getRole()));
+ } else {
+ bm = UIHelper.getContactPicture(user.getName(), 48, this,
+ false);
+ name.setText(user.getName());
+ role.setText(getReadableRole(user.getRole()));
+ }
+ } else {
+ bm = UIHelper
+ .getContactPicture(user.getName(), 48, this, false);
+ name.setText(user.getName());
+ role.setText(getReadableRole(user.getRole()));
}
- Bitmap bm = UIHelper.getContactPicture(contact.getName(), 48, this,
- false);
ImageView iv = (ImageView) view.findViewById(R.id.contact_photo);
iv.setImageBitmap(bm);
membersView.addView(view);
diff --git a/src/eu/siacs/conversations/ui/ConversationActivity.java b/src/eu/siacs/conversations/ui/ConversationActivity.java
index aa4fda4e..c68063d2 100644
--- a/src/eu/siacs/conversations/ui/ConversationActivity.java
+++ b/src/eu/siacs/conversations/ui/ConversationActivity.java
@@ -4,13 +4,14 @@ import java.io.FileNotFoundException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.RejectedExecutionException;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message;
-import eu.siacs.conversations.services.ImageProvider;
import eu.siacs.conversations.services.XmppConnectionService.OnConversationUpdate;
+import eu.siacs.conversations.ui.adapter.ConversationAdapter;
import eu.siacs.conversations.utils.ExceptionHelper;
import eu.siacs.conversations.utils.UIHelper;
import android.net.Uri;
@@ -18,10 +19,10 @@ import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.provider.MediaStore;
+import android.app.ActionBar;
import android.app.AlertDialog;
import android.app.FragmentTransaction;
import android.app.PendingIntent;
-import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.IntentSender.SendIntentException;
@@ -29,8 +30,6 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.support.v4.widget.SlidingPaneLayout;
@@ -38,11 +37,9 @@ import android.support.v4.widget.SlidingPaneLayout.PanelSlideListener;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
-import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
-import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
@@ -50,7 +47,6 @@ import android.widget.CheckBox;
import android.widget.ListView;
import android.widget.PopupMenu;
import android.widget.PopupMenu.OnMenuItemClickListener;
-import android.widget.TextView;
import android.widget.ImageView;
import android.widget.Toast;
@@ -61,17 +57,17 @@ public class ConversationActivity extends XmppActivity {
public static final String TEXT = "text";
public static final String PRESENCE = "eu.siacs.conversations.presence";
- public static final int REQUEST_SEND_MESSAGE = 0x75441;
- public static final int REQUEST_DECRYPT_PGP = 0x76783;
- private static final int REQUEST_ATTACH_FILE_DIALOG = 0x48502;
- private static final int REQUEST_IMAGE_CAPTURE = 0x33788;
- private static final int REQUEST_RECORD_AUDIO = 0x46189;
- private static final int REQUEST_SEND_PGP_IMAGE = 0x53883;
- public static final int REQUEST_ENCRYPT_MESSAGE = 0x378018;
+ public static final int REQUEST_SEND_MESSAGE = 0x0201;
+ public static final int REQUEST_DECRYPT_PGP = 0x0202;
+ private static final int REQUEST_ATTACH_FILE_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;
+ public static final int REQUEST_ENCRYPT_MESSAGE = 0x0207;
- private static final int ATTACHMENT_CHOICE_CHOOSE_IMAGE = 0x92734;
- private static final int ATTACHMENT_CHOICE_TAKE_PHOTO = 0x84123;
- private static final int ATTACHMENT_CHOICE_RECORD_VOICE = 0x75291;
+ 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;
protected SlidingPaneLayout spl;
@@ -115,6 +111,8 @@ public class ConversationActivity extends XmppActivity {
protected ConversationActivity activity = this;
private DisplayMetrics metrics;
private Toast prepareImageToast;
+
+ private Uri pendingImageUri = null;
public List<Conversation> getConversationList() {
return this.conversationList;
@@ -150,93 +148,11 @@ public class ConversationActivity extends XmppActivity {
setContentView(R.layout.fragment_conversations_overview);
listView = (ListView) findViewById(R.id.list);
+
+ getActionBar().setDisplayHomeAsUpEnabled(false);
+ getActionBar().setHomeButtonEnabled(false);
- this.listAdapter = new ArrayAdapter<Conversation>(this,
- R.layout.conversation_list_row, conversationList) {
- @Override
- public View getView(int position, View view, ViewGroup parent) {
- if (view == null) {
- LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- view = (View) inflater.inflate(
- R.layout.conversation_list_row, null);
- }
- Conversation conv;
- if (conversationList.size() > position) {
- conv = getItem(position);
- } else {
- return view;
- }
- if (!spl.isSlideable()) {
- if (conv == getSelectedConversation()) {
- view.setBackgroundColor(0xffdddddd);
- } else {
- view.setBackgroundColor(Color.TRANSPARENT);
- }
- } else {
- view.setBackgroundColor(Color.TRANSPARENT);
- }
- TextView convName = (TextView) view
- .findViewById(R.id.conversation_name);
- convName.setText(conv.getName(useSubject));
- TextView convLastMsg = (TextView) view
- .findViewById(R.id.conversation_lastmsg);
- ImageView imagePreview = (ImageView) view
- .findViewById(R.id.conversation_lastimage);
-
- Message latestMessage = conv.getLatestMessage();
-
- if (latestMessage.getType() == Message.TYPE_TEXT) {
- if ((latestMessage.getEncryption() != Message.ENCRYPTION_PGP)
- && (latestMessage.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED)) {
- convLastMsg.setText(conv.getLatestMessage().getBody());
- } else {
- convLastMsg
- .setText(getText(R.string.encrypted_message_received));
- }
- convLastMsg.setVisibility(View.VISIBLE);
- imagePreview.setVisibility(View.GONE);
- } else if (latestMessage.getType() == Message.TYPE_IMAGE) {
- if (latestMessage.getStatus() >= Message.STATUS_RECIEVED) {
- convLastMsg.setVisibility(View.GONE);
- imagePreview.setVisibility(View.VISIBLE);
- loadBitmap(latestMessage, imagePreview);
- } else {
- convLastMsg.setVisibility(View.VISIBLE);
- imagePreview.setVisibility(View.GONE);
- if (latestMessage.getStatus() == Message.STATUS_RECEIVED_OFFER) {
- convLastMsg
- .setText(getText(R.string.image_offered_for_download));
- } else if (latestMessage.getStatus() == Message.STATUS_RECIEVING) {
- convLastMsg
- .setText(getText(R.string.receiving_image));
- } else {
- convLastMsg.setText("");
- }
- }
- }
-
- if (!conv.isRead()) {
- convName.setTypeface(null, Typeface.BOLD);
- convLastMsg.setTypeface(null, Typeface.BOLD);
- } else {
- convName.setTypeface(null, Typeface.NORMAL);
- convLastMsg.setTypeface(null, Typeface.NORMAL);
- }
-
- ((TextView) view.findViewById(R.id.conversation_lastupdate))
- .setText(UIHelper.readableTimeDifference(getContext(),
- conv.getLatestMessage().getTimeSent()));
-
- ImageView profilePicture = (ImageView) view
- .findViewById(R.id.conversation_image);
- profilePicture.setImageBitmap(UIHelper.getContactPicture(conv,
- 56, activity.getApplicationContext(), false));
-
- return view;
- }
-
- };
-
+ this.listAdapter = new ConversationAdapter(this, conversationList);
listView.setAdapter(this.listAdapter);
listView.setOnItemClickListener(new OnItemClickListener() {
@@ -247,7 +163,7 @@ public class ConversationActivity extends XmppActivity {
paneShouldBeOpen = false;
if (getSelectedConversation() != conversationList.get(position)) {
setSelectedConversation(conversationList.get(position));
- swapConversationFragment(); // .onBackendConnected(conversationList.get(position));
+ swapConversationFragment();
} else {
spl.closePane();
}
@@ -262,9 +178,12 @@ public class ConversationActivity extends XmppActivity {
@Override
public void onPanelOpened(View arg0) {
paneShouldBeOpen = true;
- getActionBar().setDisplayHomeAsUpEnabled(false);
- getActionBar().setHomeButtonEnabled(false);
- getActionBar().setTitle(R.string.app_name);
+ ActionBar ab = getActionBar();
+ if (ab!=null) {
+ ab.setDisplayHomeAsUpEnabled(false);
+ ab.setHomeButtonEnabled(false);
+ ab.setTitle(R.string.app_name);
+ }
invalidateOptionsMenu();
hideKeyboard();
}
@@ -274,10 +193,13 @@ public class ConversationActivity extends XmppActivity {
paneShouldBeOpen = false;
if ((conversationList.size() > 0)
&& (getSelectedConversation() != null)) {
- getActionBar().setDisplayHomeAsUpEnabled(true);
- getActionBar().setHomeButtonEnabled(true);
- getActionBar().setTitle(
+ ActionBar ab = getActionBar();
+ if (ab!=null) {
+ ab.setDisplayHomeAsUpEnabled(true);
+ ab.setHomeButtonEnabled(true);
+ ab.setTitle(
getSelectedConversation().getName(useSubject));
+ }
invalidateOptionsMenu();
if (!getSelectedConversation().isRead()) {
xmppConnectionService
@@ -345,10 +267,11 @@ public class ConversationActivity extends XmppActivity {
@Override
public void onPresenceSelected() {
if (attachmentChoice == ATTACHMENT_CHOICE_TAKE_PHOTO) {
+ pendingImageUri = xmppConnectionService.getFileBackend().getTakePhotoUri();
+ Log.d("xmppService",pendingImageUri.toString());
Intent takePictureIntent = new Intent(
MediaStore.ACTION_IMAGE_CAPTURE);
- takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
- ImageProvider.getIncomingContentUri());
+ takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,pendingImageUri);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(takePictureIntent,
REQUEST_IMAGE_CAPTURE);
@@ -427,7 +350,7 @@ public class ConversationActivity extends XmppActivity {
switch (item.getItemId()) {
case android.R.id.home:
spl.openPane();
- break;
+ return true;
case R.id.action_attach_file:
View menuAttachFile = findViewById(R.id.action_attach_file);
if (menuAttachFile==null) {
@@ -602,12 +525,15 @@ public class ConversationActivity extends XmppActivity {
protected ConversationFragment swapConversationFragment() {
ConversationFragment selectedFragment = new ConversationFragment();
+ if (!isFinishing()) {
FragmentTransaction transaction = getFragmentManager()
.beginTransaction();
transaction.replace(R.id.selected_conversation, selectedFragment,
"conversation");
- transaction.commitAllowingStateLoss();
+
+ transaction.commitAllowingStateLoss();
+ }
return selectedFragment;
}
@@ -624,19 +550,24 @@ public class ConversationActivity extends XmppActivity {
@Override
protected void onNewIntent(Intent intent) {
- if ((Intent.ACTION_VIEW.equals(intent.getAction()) && (VIEW_CONVERSATION
- .equals(intent.getType())))) {
- String convToView = (String) intent.getExtras().get(CONVERSATION);
- updateConversationList();
- for (int i = 0; i < conversationList.size(); ++i) {
- if (conversationList.get(i).getUuid().equals(convToView)) {
- setSelectedConversation(conversationList.get(i));
- break;
+ if (xmppConnectionServiceBound) {
+ if ((Intent.ACTION_VIEW.equals(intent.getAction()) && (VIEW_CONVERSATION
+ .equals(intent.getType())))) {
+ String convToView = (String) intent.getExtras().get(CONVERSATION);
+ updateConversationList();
+ for (int i = 0; i < conversationList.size(); ++i) {
+ if (conversationList.get(i).getUuid().equals(convToView)) {
+ setSelectedConversation(conversationList.get(i));
+ break;
+ }
}
+ paneShouldBeOpen = false;
+ String text = intent.getExtras().getString(TEXT, null);
+ swapConversationFragment().setText(text);
}
- paneShouldBeOpen = false;
- String text = intent.getExtras().getString(TEXT, null);
- swapConversationFragment().setText(text);
+ } else {
+ handledViewIntent = false;
+ setIntent(intent);
}
}
@@ -669,6 +600,13 @@ public class ConversationActivity extends XmppActivity {
if (conversationList.size() == 0) {
updateConversationList();
}
+
+ if (getSelectedConversation()!=null && pendingImageUri !=null) {
+ attachImageToConversation(getSelectedConversation(), pendingImageUri);
+ pendingImageUri = null;
+ } else {
+ pendingImageUri = null;
+ }
if ((getIntent().getAction() != null)
&& (getIntent().getAction().equals(Intent.ACTION_VIEW) && (!handledViewIntent))) {
@@ -690,8 +628,7 @@ public class ConversationActivity extends XmppActivity {
}
} else {
if (xmppConnectionService.getAccounts().size() == 0) {
- startActivity(new Intent(this, ManageAccountActivity.class));
- finish();
+ startActivity(new Intent(this, EditAccountActivity.class));
} else if (conversationList.size() <= 0) {
// add no history
startActivity(new Intent(this, StartConversationActivity.class));
@@ -731,8 +668,11 @@ public class ConversationActivity extends XmppActivity {
selectedFragment.hideSnackbar();
}
} else if (requestCode == REQUEST_ATTACH_FILE_DIALOG) {
- attachImageToConversation(getSelectedConversation(),
- data.getData());
+ pendingImageUri = data.getData();
+ if (xmppConnectionServiceBound) {
+ attachImageToConversation(getSelectedConversation(),pendingImageUri);
+ pendingImageUri = null;
+ }
} else if (requestCode == REQUEST_SEND_PGP_IMAGE) {
} else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_IMAGE) {
@@ -745,19 +685,22 @@ public class ConversationActivity extends XmppActivity {
} else if (requestCode == REQUEST_ENCRYPT_MESSAGE) {
// encryptTextMessage();
} else if (requestCode == REQUEST_IMAGE_CAPTURE) {
- attachImageToConversation(getSelectedConversation(), null);
+ if (xmppConnectionServiceBound) {
+ attachImageToConversation(getSelectedConversation(), pendingImageUri);
+ pendingImageUri = null;
+ }
+ Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
+ intent.setData(pendingImageUri);
+ sendBroadcast(intent);
} else if (requestCode == REQUEST_RECORD_AUDIO) {
- Log.d("xmppService", data.getData().toString());
attachAudioToConversation(getSelectedConversation(),
data.getData());
- } else {
- Log.d(LOGTAG, "unknown result code:" + requestCode);
}
}
}
private void attachAudioToConversation(Conversation conversation, Uri uri) {
-
+
}
private void attachImageToConversation(Conversation conversation, Uri uri) {
@@ -818,9 +761,7 @@ public class ConversationActivity extends XmppActivity {
try {
this.startIntentSenderForResult(pi.getIntentSender(), requestCode,
null, 0, 0, 0);
- } catch (SendIntentException e1) {
- Log.d("xmppService", "failed to start intent to send message");
- }
+ } catch (SendIntentException e1) {}
}
class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> {
@@ -838,7 +779,6 @@ public class ConversationActivity extends XmppActivity {
return xmppConnectionService.getFileBackend().getThumbnail(
message, (int) (metrics.density * 288), false);
} catch (FileNotFoundException e) {
- Log.d("xmppService", "file not found!");
return null;
}
}
@@ -873,7 +813,11 @@ public class ConversationActivity extends XmppActivity {
final AsyncDrawable asyncDrawable = new AsyncDrawable(
getResources(), null, task);
imageView.setImageDrawable(asyncDrawable);
- task.execute(message);
+ try {
+ task.execute(message);
+ } catch (RejectedExecutionException e) {
+ return;
+ }
}
}
}
diff --git a/src/eu/siacs/conversations/ui/ConversationFragment.java b/src/eu/siacs/conversations/ui/ConversationFragment.java
index 1df59843..48fa8c69 100644
--- a/src/eu/siacs/conversations/ui/ConversationFragment.java
+++ b/src/eu/siacs/conversations/ui/ConversationFragment.java
@@ -13,13 +13,16 @@ import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.services.XmppConnectionService;
+import eu.siacs.conversations.ui.EditMessage.OnEnterPressed;
import eu.siacs.conversations.ui.XmppActivity.OnPresenceSelected;
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.UIHelper;
import android.app.AlertDialog;
import android.app.Fragment;
import android.app.PendingIntent;
+import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentSender;
@@ -30,14 +33,17 @@ import android.preference.PreferenceManager;
import android.text.Editable;
import android.text.Selection;
import android.view.Gravity;
+import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
import android.widget.AbsListView.OnScrollListener;
+import android.widget.TextView.OnEditorActionListener;
import android.widget.AbsListView;
-import android.widget.EditText;
import android.widget.ListView;
import android.widget.ImageButton;
import android.widget.RelativeLayout;
@@ -55,7 +61,7 @@ public class ConversationFragment extends Fragment {
protected String queuedPqpMessage = null;
- private EditText chatMsg;
+ private EditMessage mEditMessage;
private String pastedText = null;
private RelativeLayout snackbar;
private TextView snackbarMessage;
@@ -66,23 +72,28 @@ public class ConversationFragment extends Fragment {
private IntentSender askForPassphraseIntent = null;
- private OnClickListener sendMsgListener = new OnClickListener() {
+ private OnEditorActionListener mEditorActionListener = new OnEditorActionListener() {
@Override
- public void onClick(View v) {
- if (chatMsg.getText().length() < 1)
- return;
- Message message = new Message(conversation, chatMsg.getText()
- .toString(), conversation.getNextEncryption());
- if (conversation.getNextEncryption() == Message.ENCRYPTION_OTR) {
- sendOtrMessage(message);
- } else if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
- sendPgpMessage(message);
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ if (actionId == EditorInfo.IME_ACTION_DONE) {
+ InputMethodManager imm = (InputMethodManager) v.getContext()
+ .getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
+ return true;
} else {
- sendPlainTextMessage(message);
+ return false;
}
}
};
+
+ private OnClickListener mSendButtonListener = new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ sendMessage();
+ }
+ };
protected OnClickListener clickToDecryptListener = new OnClickListener() {
@Override
@@ -148,19 +159,53 @@ public class ConversationFragment extends Fragment {
private ConversationActivity activity;
+ private void sendMessage() {
+ if (mEditMessage.getText().length() < 1) {
+ if (this.conversation.getMode() == Conversation.MODE_MULTI) {
+ conversation.setNextPresence(null);
+ updateChatMsgHint();
+ }
+ return;
+ }
+ Message message = new Message(conversation, mEditMessage.getText()
+ .toString(), conversation.getNextEncryption());
+ if (conversation.getMode() == Conversation.MODE_MULTI) {
+ if (conversation.getNextPresence() != null) {
+ message.setPresence(conversation.getNextPresence());
+ message.setType(Message.TYPE_PRIVATE);
+ conversation.setNextPresence(null);
+ }
+ }
+ if (conversation.getNextEncryption() == Message.ENCRYPTION_OTR) {
+ sendOtrMessage(message);
+ } else if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
+ sendPgpMessage(message);
+ } else {
+ sendPlainTextMessage(message);
+ }
+ }
+
public void updateChatMsgHint() {
- switch (conversation.getNextEncryption()) {
- case Message.ENCRYPTION_NONE:
- chatMsg.setHint(getString(R.string.send_plain_text_message));
- break;
- case Message.ENCRYPTION_OTR:
- chatMsg.setHint(getString(R.string.send_otr_message));
- break;
- case Message.ENCRYPTION_PGP:
- chatMsg.setHint(getString(R.string.send_pgp_message));
- break;
- default:
- break;
+ if (conversation.getMode() == Conversation.MODE_MULTI
+ && conversation.getNextPresence() != null) {
+ this.mEditMessage.setHint(getString(
+ R.string.send_private_message_to,
+ conversation.getNextPresence()));
+ } else {
+ switch (conversation.getNextEncryption()) {
+ case Message.ENCRYPTION_NONE:
+ mEditMessage
+ .setHint(getString(R.string.send_plain_text_message));
+ break;
+ case Message.ENCRYPTION_OTR:
+ mEditMessage.setHint(getString(R.string.send_otr_message));
+ break;
+ case Message.ENCRYPTION_PGP:
+ mEditMessage.setHint(getString(R.string.send_pgp_message));
+ break;
+ default:
+ break;
+ }
}
}
@@ -169,8 +214,8 @@ public class ConversationFragment extends Fragment {
ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.fragment_conversation,
container, false);
- chatMsg = (EditText) view.findViewById(R.id.textinput);
- chatMsg.setOnClickListener(new OnClickListener() {
+ mEditMessage = (EditMessage) view.findViewById(R.id.textinput);
+ mEditMessage.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
@@ -179,10 +224,18 @@ public class ConversationFragment extends Fragment {
}
}
});
+ mEditMessage.setOnEditorActionListener(mEditorActionListener);
+ mEditMessage.setOnEnterPressedListener(new OnEnterPressed() {
+
+ @Override
+ public void onEnterPressed() {
+ sendMessage();
+ }
+ });
ImageButton sendButton = (ImageButton) view
.findViewById(R.id.textSendButton);
- sendButton.setOnClickListener(this.sendMsgListener);
+ sendButton.setOnClickListener(this.mSendButtonListener);
snackbar = (RelativeLayout) view.findViewById(R.id.snackbar);
snackbarMessage = (TextView) view.findViewById(R.id.snackbar_message);
@@ -191,30 +244,56 @@ public class ConversationFragment extends Fragment {
messagesView = (ListView) view.findViewById(R.id.messages_view);
messagesView.setOnScrollListener(mOnScrollListener);
messagesView.setTranscriptMode(ListView.TRANSCRIPT_MODE_NORMAL);
- messageListAdapter = new MessageAdapter((ConversationActivity) getActivity(), this.messageList);
- messageListAdapter.setOnContactPictureClicked(new OnContactPictureClicked() {
-
- @Override
- public void onContactPictureClicked(Message message) {
- if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
- highlightInConference(message.getCounterpart());
- }
- }
- });
+ messageListAdapter = new MessageAdapter(
+ (ConversationActivity) getActivity(), this.messageList);
+ messageListAdapter
+ .setOnContactPictureClicked(new OnContactPictureClicked() {
+
+ @Override
+ public void onContactPictureClicked(Message message) {
+ if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
+ if (message.getPresence() != null) {
+ highlightInConference(message.getPresence());
+ } else {
+ highlightInConference(message.getCounterpart());
+ }
+ }
+ }
+ });
+ messageListAdapter
+ .setOnContactPictureLongClicked(new OnContactPictureLongClicked() {
+
+ @Override
+ public void onContactPictureLongClicked(Message message) {
+ if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
+ if (message.getPresence() != null) {
+ privateMessageWith(message.getPresence());
+ } else {
+ privateMessageWith(message.getCounterpart());
+ }
+ }
+ }
+ });
messagesView.setAdapter(messageListAdapter);
return view;
}
+ protected void privateMessageWith(String counterpart) {
+ this.mEditMessage.setText("");
+ this.conversation.setNextPresence(counterpart);
+ updateChatMsgHint();
+ }
+
protected void highlightInConference(String nick) {
- String oldString = chatMsg.getText().toString().trim();
+ String oldString = mEditMessage.getText().toString().trim();
if (oldString.isEmpty()) {
- chatMsg.setText(nick + ": ");
+ mEditMessage.setText(nick + ": ");
} else {
- chatMsg.setText(oldString + " " + nick + " ");
+ mEditMessage.setText(oldString + " " + nick + " ");
}
- int position = chatMsg.length();
- Editable etext = chatMsg.getText();
+ int position = mEditMessage.length();
+ Editable etext = mEditMessage.getText();
Selection.setSelection(etext, position);
}
@@ -234,31 +313,31 @@ public class ConversationFragment extends Fragment {
public void onStop() {
super.onStop();
if (this.conversation != null) {
- this.conversation.setNextMessage(chatMsg.getText().toString());
+ this.conversation.setNextMessage(mEditMessage.getText().toString());
}
}
public void onBackendConnected() {
+ this.activity = (ConversationActivity) getActivity();
this.conversation = activity.getSelectedConversation();
if (this.conversation == null) {
return;
}
String oldString = conversation.getNextMessage().trim();
if (this.pastedText == null) {
- this.chatMsg.setText(oldString);
+ this.mEditMessage.setText(oldString);
} else {
if (oldString.isEmpty()) {
- chatMsg.setText(pastedText);
+ mEditMessage.setText(pastedText);
} else {
- chatMsg.setText(oldString + " " + pastedText);
+ mEditMessage.setText(oldString + " " + pastedText);
}
pastedText = null;
}
- int position = chatMsg.length();
- Editable etext = chatMsg.getText();
+ int position = mEditMessage.length();
+ Editable etext = mEditMessage.getText();
Selection.setSelection(etext, position);
- updateMessages();
if (activity.getSlidingPaneLayout().isSlideable()) {
if (!activity.shouldPaneBeOpen()) {
activity.getSlidingPaneLayout().closePane();
@@ -269,6 +348,10 @@ public class ConversationFragment extends Fragment {
activity.invalidateOptionsMenu();
}
}
+ if (this.conversation.getMode() == Conversation.MODE_MULTI) {
+ conversation.setNextPresence(null);
+ }
+ updateMessages();
}
private void decryptMessage(Message message) {
@@ -307,15 +390,19 @@ public class ConversationFragment extends Fragment {
final ConversationActivity activity = (ConversationActivity) getActivity();
if (this.conversation != null) {
final Contact contact = this.conversation.getContact();
- if (!contact.showInRoster() && contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
- showSnackbar(R.string.contact_added_you, R.string.add_back, new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- activity.xmppConnectionService.createContact(contact);
- activity.switchToContactDetails(contact);
- }
- });
+ if (!contact.showInRoster()
+ && contact
+ .getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
+ showSnackbar(R.string.contact_added_you, R.string.add_back,
+ new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ activity.xmppConnectionService
+ .createContact(contact);
+ activity.switchToContactDetails(contact);
+ }
+ });
}
for (Message message : this.conversation.getMessages()) {
if ((message.getEncryption() == Message.ENCRYPTION_PGP)
@@ -371,7 +458,8 @@ public class ConversationFragment extends Fragment {
if (size >= 1) {
messagesView.setSelection(size - 1);
}
- chatMsg.setText("");
+ mEditMessage.setText("");
+ updateChatMsgHint();
}
protected void updateStatusMessages() {
@@ -425,7 +513,9 @@ public class ConversationFragment extends Fragment {
protected void showSnackbar(int message, int action,
OnClickListener clickListener) {
snackbar.setVisibility(View.VISIBLE);
+ snackbar.setOnClickListener(null);
snackbarMessage.setText(message);
+ snackbarMessage.setOnClickListener(null);
snackbarAction.setText(action);
snackbarAction.setOnClickListener(clickListener);
}
@@ -560,6 +650,6 @@ public class ConversationFragment extends Fragment {
}
public void clearInputField() {
- this.chatMsg.setText("");
+ this.mEditMessage.setText("");
}
}
diff --git a/src/eu/siacs/conversations/ui/EditAccountActivity.java b/src/eu/siacs/conversations/ui/EditAccountActivity.java
new file mode 100644
index 00000000..2d24aa49
--- /dev/null
+++ b/src/eu/siacs/conversations/ui/EditAccountActivity.java
@@ -0,0 +1,341 @@
+package eu.siacs.conversations.ui;
+
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.AutoCompleteTextView;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.TextView;
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
+import eu.siacs.conversations.ui.adapter.KnownHostsAdapter;
+import eu.siacs.conversations.utils.UIHelper;
+import eu.siacs.conversations.utils.Validator;
+import eu.siacs.conversations.xmpp.XmppConnection.Features;
+import eu.siacs.conversations.xmpp.pep.Avatar;
+
+public class EditAccountActivity extends XmppActivity {
+
+ private AutoCompleteTextView mAccountJid;
+ private EditText mPassword;
+ private EditText mPasswordConfirm;
+ private CheckBox mRegisterNew;
+ private Button mCancelButton;
+ private Button mSaveButton;
+
+ private LinearLayout mStats;
+ private TextView mServerInfoSm;
+ private TextView mServerInfoCarbons;
+ private TextView mServerInfoPep;
+ private TextView mSessionEst;
+ private TextView mOtrFingerprint;
+ private TextView mOtrFingerprintHeadline;
+
+ private String jidToEdit;
+ private Account mAccount;
+
+ private boolean mFetchingAvatar = false;
+
+ private OnClickListener mSaveButtonClickListener = new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (!Validator.isValidJid(mAccountJid.getText().toString())) {
+ mAccountJid.setError(getString(R.string.invalid_jid));
+ mAccountJid.requestFocus();
+ return;
+ }
+ boolean registerNewAccount = mRegisterNew.isChecked();
+ String[] jidParts = mAccountJid.getText().toString().split("@");
+ String username = jidParts[0];
+ String server;
+ if (jidParts.length >= 2) {
+ server = jidParts[1];
+ } else {
+ server = "";
+ }
+ String password = mPassword.getText().toString();
+ String passwordConfirm = mPasswordConfirm.getText().toString();
+ if (registerNewAccount) {
+ if (!password.equals(passwordConfirm)) {
+ mPasswordConfirm
+ .setError(getString(R.string.passwords_do_not_match));
+ mPasswordConfirm.requestFocus();
+ return;
+ }
+ }
+ if (mAccount != null) {
+ mAccount.setPassword(password);
+ mAccount.setUsername(username);
+ mAccount.setServer(server);
+ mAccount.setOption(Account.OPTION_REGISTER, registerNewAccount);
+ xmppConnectionService.updateAccount(mAccount);
+ } else {
+ if (xmppConnectionService.findAccountByJid(mAccountJid
+ .getText().toString()) != null) {
+ mAccountJid
+ .setError(getString(R.string.account_already_exists));
+ mAccountJid.requestFocus();
+ return;
+ }
+ mAccount = new Account(username, server, password);
+ mAccount.setOption(Account.OPTION_USETLS, true);
+ mAccount.setOption(Account.OPTION_USECOMPRESSION, true);
+ mAccount.setOption(Account.OPTION_REGISTER, registerNewAccount);
+ xmppConnectionService.createAccount(mAccount);
+ }
+ if (jidToEdit != null) {
+ finish();
+ } else {
+ updateSaveButton();
+ updateAccountInformation();
+ }
+
+ }
+ };
+ private OnClickListener mCancelButtonClickListener = new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ finish();
+ }
+ };
+ private OnAccountUpdate mOnAccountUpdateListener = new OnAccountUpdate() {
+
+ @Override
+ public void onAccountUpdate() {
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ if (mAccount != null
+ && mAccount.getStatus() != Account.STATUS_ONLINE
+ && mFetchingAvatar) {
+ startActivity(new Intent(getApplicationContext(),
+ ManageAccountActivity.class));
+ finish();
+ } else if (jidToEdit == null && mAccount != null
+ && mAccount.getStatus() == Account.STATUS_ONLINE) {
+ if (!mFetchingAvatar) {
+ mFetchingAvatar = true;
+ xmppConnectionService.checkForAvatar(mAccount,
+ mAvatarFetchCallback);
+ }
+ } else {
+ updateSaveButton();
+ }
+ if (mAccount != null) {
+ updateAccountInformation();
+ }
+ }
+ });
+ }
+ };
+ private UiCallback<Avatar> mAvatarFetchCallback = new UiCallback<Avatar>() {
+
+ @Override
+ public void userInputRequried(PendingIntent pi, Avatar avatar) {
+ finishInitialSetup(avatar);
+ }
+
+ @Override
+ public void success(Avatar avatar) {
+ finishInitialSetup(avatar);
+ }
+
+ @Override
+ public void error(int errorCode, Avatar avatar) {
+ finishInitialSetup(avatar);
+ }
+ };
+ private KnownHostsAdapter mKnownHostsAdapter;
+
+ protected void finishInitialSetup(final Avatar avatar) {
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ Intent intent;
+ if (avatar != null) {
+ intent = new Intent(getApplicationContext(),
+ StartConversationActivity.class);
+ } else {
+ intent = new Intent(getApplicationContext(),
+ PublishProfilePictureActivity.class);
+ intent.putExtra("account", mAccount.getJid());
+ intent.putExtra("setup", true);
+ }
+ startActivity(intent);
+ finish();
+ }
+ });
+ }
+
+ protected boolean inputDataDiffersFromAccount() {
+ if (mAccount == null) {
+ return true;
+ } else {
+ return (!mAccount.getJid().equals(mAccountJid.getText().toString()))
+ || (!mAccount.getPassword().equals(
+ mPassword.getText().toString()) || mAccount
+ .isOptionSet(Account.OPTION_REGISTER) != mRegisterNew
+ .isChecked());
+ }
+ }
+
+ protected void updateSaveButton() {
+ if (mAccount != null
+ && mAccount.getStatus() == Account.STATUS_CONNECTING) {
+ this.mSaveButton.setEnabled(false);
+ this.mSaveButton.setTextColor(getSecondaryTextColor());
+ this.mSaveButton.setText(R.string.account_status_connecting);
+ } else {
+ this.mSaveButton.setEnabled(true);
+ this.mSaveButton.setTextColor(getPrimaryTextColor());
+ if (jidToEdit != null) {
+ this.mSaveButton.setText(R.string.connect);
+ } else {
+ this.mSaveButton.setText(R.string.next);
+ }
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_edit_account);
+ this.mAccountJid = (AutoCompleteTextView) findViewById(R.id.account_jid);
+ this.mPassword = (EditText) findViewById(R.id.account_password);
+ this.mPasswordConfirm = (EditText) findViewById(R.id.account_password_confirm);
+ this.mRegisterNew = (CheckBox) findViewById(R.id.account_register_new);
+ this.mStats = (LinearLayout) findViewById(R.id.stats);
+ this.mSessionEst = (TextView) findViewById(R.id.session_est);
+ this.mServerInfoCarbons = (TextView) findViewById(R.id.server_info_carbons);
+ this.mServerInfoSm = (TextView) findViewById(R.id.server_info_sm);
+ this.mServerInfoPep = (TextView) findViewById(R.id.server_info_pep);
+ this.mOtrFingerprint = (TextView) findViewById(R.id.otr_fingerprint);
+ this.mOtrFingerprintHeadline = (TextView) findViewById(R.id.otr_fingerprint_headline);
+ this.mSaveButton = (Button) findViewById(R.id.save_button);
+ this.mCancelButton = (Button) findViewById(R.id.cancel_button);
+ this.mSaveButton.setOnClickListener(this.mSaveButtonClickListener);
+ this.mCancelButton.setOnClickListener(this.mCancelButtonClickListener);
+ this.mRegisterNew
+ .setOnCheckedChangeListener(new OnCheckedChangeListener() {
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView,
+ boolean isChecked) {
+ if (isChecked) {
+ mPasswordConfirm.setVisibility(View.VISIBLE);
+ } else {
+ mPasswordConfirm.setVisibility(View.GONE);
+ }
+ updateSaveButton();
+ }
+ });
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ if (getIntent() != null) {
+ this.jidToEdit = getIntent().getStringExtra("jid");
+ if (this.jidToEdit != null) {
+ this.mRegisterNew.setVisibility(View.GONE);
+ getActionBar().setTitle(R.string.mgmt_account_edit);
+ } else {
+ getActionBar().setTitle(R.string.action_add_account);
+ }
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ if (xmppConnectionServiceBound) {
+ xmppConnectionService.removeOnAccountListChangedListener();
+ }
+ super.onStop();
+ }
+
+ @Override
+ protected void onBackendConnected() {
+ this.mKnownHostsAdapter = new KnownHostsAdapter(this,
+ android.R.layout.simple_list_item_1,
+ xmppConnectionService.getKnownHosts());
+ this.xmppConnectionService
+ .setOnAccountListChangedListener(this.mOnAccountUpdateListener);
+ if (this.jidToEdit != null) {
+ this.mAccount = xmppConnectionService.findAccountByJid(jidToEdit);
+ updateAccountInformation();
+ } else if (this.xmppConnectionService.getAccounts().size() == 0) {
+ getActionBar().setDisplayHomeAsUpEnabled(false);
+ getActionBar().setDisplayShowHomeEnabled(false);
+ this.mCancelButton.setEnabled(false);
+ this.mCancelButton.setTextColor(getSecondaryTextColor());
+ }
+ this.mAccountJid.setAdapter(this.mKnownHostsAdapter);
+ updateSaveButton();
+ }
+
+ private void updateAccountInformation() {
+ this.mAccountJid.setText(this.mAccount.getJid());
+ this.mPassword.setText(this.mAccount.getPassword());
+ if (this.mAccount.isOptionSet(Account.OPTION_REGISTER)) {
+ this.mRegisterNew.setVisibility(View.VISIBLE);
+ this.mRegisterNew.setChecked(true);
+ this.mPasswordConfirm.setText(this.mAccount.getPassword());
+ } else {
+ this.mRegisterNew.setVisibility(View.GONE);
+ this.mRegisterNew.setChecked(false);
+ }
+ if (this.mAccount.getStatus() == Account.STATUS_ONLINE
+ && !this.mFetchingAvatar) {
+ this.mStats.setVisibility(View.VISIBLE);
+ this.mSessionEst.setText(UIHelper.readableTimeDifference(
+ getApplicationContext(), this.mAccount.getXmppConnection()
+ .getLastSessionEstablished()));
+ Features features = this.mAccount.getXmppConnection().getFeatures();
+ if (features.carbons()) {
+ this.mServerInfoCarbons.setText(R.string.server_info_available);
+ } else {
+ this.mServerInfoCarbons
+ .setText(R.string.server_info_unavailable);
+ }
+ if (features.sm()) {
+ this.mServerInfoSm.setText(R.string.server_info_available);
+ } else {
+ this.mServerInfoSm.setText(R.string.server_info_unavailable);
+ }
+ if (features.pubsub()) {
+ this.mServerInfoPep.setText(R.string.server_info_available);
+ } else {
+ this.mServerInfoPep.setText(R.string.server_info_unavailable);
+ }
+ String fingerprint = this.mAccount
+ .getOtrFingerprint(getApplicationContext());
+ if (fingerprint != null) {
+ this.mOtrFingerprintHeadline.setVisibility(View.VISIBLE);
+ this.mOtrFingerprint.setVisibility(View.VISIBLE);
+ this.mOtrFingerprint.setText(fingerprint);
+ } else {
+ this.mOtrFingerprint.setVisibility(View.GONE);
+ this.mOtrFingerprintHeadline.setVisibility(View.GONE);
+ }
+ } else {
+ if (this.mAccount.errorStatus()) {
+ this.mAccountJid.setError(getString(this.mAccount
+ .getReadableStatusId()));
+ this.mAccountJid.requestFocus();
+ }
+ this.mStats.setVisibility(View.GONE);
+ }
+ }
+}
diff --git a/src/eu/siacs/conversations/ui/EditAccountDialog.java b/src/eu/siacs/conversations/ui/EditAccountDialog.java
deleted file mode 100644
index 7c135fc1..00000000
--- a/src/eu/siacs/conversations/ui/EditAccountDialog.java
+++ /dev/null
@@ -1,157 +0,0 @@
-package eu.siacs.conversations.ui;
-
-import java.util.List;
-
-import eu.siacs.conversations.R;
-import eu.siacs.conversations.entities.Account;
-import eu.siacs.conversations.ui.adapter.KnownHostsAdapter;
-import eu.siacs.conversations.utils.Validator;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.content.Context;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.AutoCompleteTextView;
-import android.widget.Button;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-import android.widget.CompoundButton.OnCheckedChangeListener;
-import android.widget.EditText;
-import android.widget.TextView;
-
-public class EditAccountDialog extends DialogFragment {
-
- protected Account account;
-
- protected AutoCompleteTextView mAccountJid;
-
- public void setAccount(Account account) {
- this.account = account;
- }
-
- public interface EditAccountListener {
- public void onAccountEdited(Account account);
- }
-
- protected EditAccountListener listener = null;
-
- private KnownHostsAdapter mKnownHostsAdapter;
-
- public void setEditAccountListener(EditAccountListener listener) {
- this.listener = listener;
- }
-
- public void setKnownHosts(List<String> hosts, Context context) {
- this.mKnownHostsAdapter = new KnownHostsAdapter(context, android.R.layout.simple_list_item_1, hosts);
- if (this.mAccountJid != null) {
- this.mAccountJid.setAdapter(this.mKnownHostsAdapter);
- }
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
- LayoutInflater inflater = getActivity().getLayoutInflater();
- View view = inflater.inflate(R.layout.edit_account_dialog, null);
- mAccountJid = (AutoCompleteTextView) view.findViewById(R.id.account_jid);
- if (this.mKnownHostsAdapter!=null) {
- mAccountJid.setAdapter(this.mKnownHostsAdapter);
- }
- final TextView confirmPwDesc = (TextView) view
- .findViewById(R.id.account_confirm_password_desc);
-
- final EditText password = (EditText) view
- .findViewById(R.id.account_password);
- final EditText passwordConfirm = (EditText) view
- .findViewById(R.id.account_password_confirm2);
- final CheckBox registerAccount = (CheckBox) view
- .findViewById(R.id.edit_account_register_new);
-
- if (account != null) {
- mAccountJid.setText(account.getJid());
- password.setText(account.getPassword());
- if (account.isOptionSet(Account.OPTION_REGISTER)) {
- registerAccount.setChecked(true);
- passwordConfirm.setVisibility(View.VISIBLE);
- passwordConfirm.setText(account.getPassword());
- } else {
- registerAccount.setVisibility(View.GONE);
- }
- }
- builder.setTitle(R.string.account_settings);
-
-
- registerAccount
- .setOnCheckedChangeListener(new OnCheckedChangeListener() {
-
- @Override
- public void onCheckedChanged(CompoundButton buttonView,
- boolean isChecked) {
- if (isChecked) {
- passwordConfirm.setVisibility(View.VISIBLE);
- confirmPwDesc.setVisibility(View.VISIBLE);
- } else {
- passwordConfirm.setVisibility(View.GONE);
- confirmPwDesc.setVisibility(View.GONE);
- }
- }
- });
-
- builder.setView(view);
- builder.setNeutralButton(getString(R.string.cancel), null);
- builder.setPositiveButton(getString(R.string.save), null);
- return builder.create();
- }
-
- @Override
- public void onStart() {
- super.onStart();
- final AlertDialog d = (AlertDialog) getDialog();
- Button positiveButton = (Button) d.getButton(Dialog.BUTTON_POSITIVE);
- positiveButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- EditText jidEdit = (EditText) d.findViewById(R.id.account_jid);
- String jid = jidEdit.getText().toString();
- EditText passwordEdit = (EditText) d
- .findViewById(R.id.account_password);
- EditText passwordConfirmEdit = (EditText) d.findViewById(R.id.account_password_confirm2);
- String password = passwordEdit.getText().toString();
- String passwordConfirm = passwordConfirmEdit.getText().toString();
- CheckBox register = (CheckBox) d.findViewById(R.id.edit_account_register_new);
- String username;
- String server;
- if (Validator.isValidJid(jid)) {
- String[] parts = jid.split("@");
- username = parts[0];
- server = parts[1];
- } else {
- jidEdit.setError(getString(R.string.invalid_jid));
- return;
- }
- if (register.isChecked()) {
- if (!passwordConfirm.equals(password)) {
- passwordConfirmEdit.setError(getString(R.string.passwords_do_not_match));
- return;
- }
- }
- if (account != null) {
- account.setPassword(password);
- account.setUsername(username);
- account.setServer(server);
- } else {
- account = new Account(username, server, password);
- account.setOption(Account.OPTION_USETLS, true);
- account.setOption(Account.OPTION_USECOMPRESSION, true);
- }
- account.setOption(Account.OPTION_REGISTER, register.isChecked());
- if (listener != null) {
- listener.onAccountEdited(account);
- d.dismiss();
- }
- }
- });
- }
-}
diff --git a/src/eu/siacs/conversations/ui/EditMessage.java b/src/eu/siacs/conversations/ui/EditMessage.java
new file mode 100644
index 00000000..f8302050
--- /dev/null
+++ b/src/eu/siacs/conversations/ui/EditMessage.java
@@ -0,0 +1,39 @@
+package eu.siacs.conversations.ui;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.widget.EditText;
+
+public class EditMessage extends EditText {
+
+ public EditMessage(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public EditMessage(Context context) {
+ super(context);
+ }
+
+ protected OnEnterPressed mOnEnterPressed;
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_ENTER) {
+ if (mOnEnterPressed != null) {
+ mOnEnterPressed.onEnterPressed();
+ }
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ public void setOnEnterPressedListener(OnEnterPressed listener) {
+ this.mOnEnterPressed = listener;
+ }
+
+ public interface OnEnterPressed {
+ public void onEnterPressed();
+ }
+
+}
diff --git a/src/eu/siacs/conversations/ui/ManageAccountActivity.java b/src/eu/siacs/conversations/ui/ManageAccountActivity.java
index e56e2db9..c57121da 100644
--- a/src/eu/siacs/conversations/ui/ManageAccountActivity.java
+++ b/src/eu/siacs/conversations/ui/ManageAccountActivity.java
@@ -6,42 +6,31 @@ import java.util.List;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
-import eu.siacs.conversations.ui.EditAccountDialog.EditAccountListener;
-import eu.siacs.conversations.xmpp.XmppConnection;
-import android.app.Activity;
+import eu.siacs.conversations.ui.adapter.AccountAdapter;
import android.app.AlertDialog;
-import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.os.Bundle;
-import android.os.SystemClock;
-import android.view.ActionMode;
-import android.view.LayoutInflater;
+import android.view.ContextMenu;
import android.view.Menu;
-import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
-import android.view.ViewGroup;
+import android.view.ContextMenu.ContextMenuInfo;
import android.widget.AdapterView;
+import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView.OnItemClickListener;
-import android.widget.AdapterView.OnItemLongClickListener;
-import android.widget.ArrayAdapter;
import android.widget.ListView;
-import android.widget.TextView;
public class ManageAccountActivity extends XmppActivity {
- protected boolean isActionMode = false;
- protected ActionMode actionMode;
- protected Account selectedAccountForActionMode = null;
protected ManageAccountActivity activity = this;
- protected boolean firstrun = true;
+ protected Account selectedAccount = null;
protected List<Account> accountList = new ArrayList<Account>();
protected ListView accountListView;
- protected ArrayAdapter<Account> accountListViewAdapter;
+ protected AccountAdapter mAccountAdapter;
protected OnAccountUpdate accountChanged = new OnAccountUpdate() {
@Override
@@ -52,7 +41,7 @@ public class ManageAccountActivity extends XmppActivity {
@Override
public void run() {
- accountListViewAdapter.notifyDataSetChanged();
+ mAccountAdapter.notifyDataSetChanged();
}
});
}
@@ -66,347 +55,34 @@ public class ManageAccountActivity extends XmppActivity {
setContentView(R.layout.manage_accounts);
accountListView = (ListView) findViewById(R.id.account_list);
- accountListViewAdapter = new ArrayAdapter<Account>(
- getApplicationContext(), R.layout.account_row, this.accountList) {
- @Override
- public View getView(int position, View view, ViewGroup parent) {
- Account account = getItem(position);
- if (view == null) {
- LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- view = (View) inflater.inflate(R.layout.account_row, null);
- }
- ((TextView) view.findViewById(R.id.account_jid))
- .setText(account.getJid());
- TextView statusView = (TextView) view
- .findViewById(R.id.account_status);
- switch (account.getStatus()) {
- case Account.STATUS_DISABLED:
- statusView
- .setText(getString(R.string.account_status_disabled));
- statusView.setTextColor(0xFF1da9da);
- break;
- case Account.STATUS_ONLINE:
- statusView
- .setText(getString(R.string.account_status_online));
- statusView.setTextColor(0xFF83b600);
- break;
- case Account.STATUS_CONNECTING:
- statusView
- .setText(getString(R.string.account_status_connecting));
- statusView.setTextColor(0xFF1da9da);
- break;
- case Account.STATUS_OFFLINE:
- statusView
- .setText(getString(R.string.account_status_offline));
- statusView.setTextColor(0xFFe92727);
- break;
- case Account.STATUS_UNAUTHORIZED:
- statusView
- .setText(getString(R.string.account_status_unauthorized));
- statusView.setTextColor(0xFFe92727);
- break;
- case Account.STATUS_SERVER_NOT_FOUND:
- statusView
- .setText(getString(R.string.account_status_not_found));
- statusView.setTextColor(0xFFe92727);
- break;
- case Account.STATUS_NO_INTERNET:
- statusView
- .setText(getString(R.string.account_status_no_internet));
- statusView.setTextColor(0xFFe92727);
- break;
- case Account.STATUS_SERVER_REQUIRES_TLS:
- statusView
- .setText(getString(R.string.account_status_requires_tls));
- statusView.setTextColor(0xFFe92727);
- break;
- case Account.STATUS_REGISTRATION_FAILED:
- statusView
- .setText(getString(R.string.account_status_regis_fail));
- statusView.setTextColor(0xFFe92727);
- break;
- case Account.STATUS_REGISTRATION_CONFLICT:
- statusView
- .setText(getString(R.string.account_status_regis_conflict));
- statusView.setTextColor(0xFFe92727);
- break;
- case Account.STATUS_REGISTRATION_SUCCESSFULL:
- statusView
- .setText(getString(R.string.account_status_regis_success));
- statusView.setTextColor(0xFF83b600);
- break;
- case Account.STATUS_REGISTRATION_NOT_SUPPORTED:
- statusView
- .setText(getString(R.string.account_status_regis_not_sup));
- statusView.setTextColor(0xFFe92727);
- break;
- default:
- statusView.setText("");
- break;
- }
-
- return view;
- }
- };
- final XmppActivity activity = this;
- accountListView.setAdapter(this.accountListViewAdapter);
+ this.mAccountAdapter = new AccountAdapter(this, accountList);
+ accountListView.setAdapter(this.mAccountAdapter);
accountListView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View view,
int position, long arg3) {
- if (!isActionMode) {
- Account account = accountList.get(position);
- if (account.getStatus() == Account.STATUS_OFFLINE) {
- activity.xmppConnectionService.reconnectAccount(
- accountList.get(position), true);
- } else if (account.getStatus() == Account.STATUS_ONLINE) {
- activity.startActivity(new Intent(activity
- .getApplicationContext(),
- StartConversationActivity.class));
- } else if (account.getStatus() != Account.STATUS_DISABLED) {
- editAccount(account);
- }
- } else {
- selectedAccountForActionMode = accountList.get(position);
- actionMode.invalidate();
- }
+ editAccount(accountList.get(position));
}
});
- accountListView
- .setOnItemLongClickListener(new OnItemLongClickListener() {
-
- @Override
- public boolean onItemLongClick(AdapterView<?> arg0,
- View view, int position, long arg3) {
- if (!isActionMode) {
- accountListView
- .setChoiceMode(ListView.CHOICE_MODE_SINGLE);
- accountListView.setItemChecked(position, true);
- selectedAccountForActionMode = accountList
- .get(position);
- actionMode = activity
- .startActionMode((new ActionMode.Callback() {
-
- @Override
- public boolean onPrepareActionMode(
- ActionMode mode, Menu menu) {
- if (selectedAccountForActionMode
- .isOptionSet(Account.OPTION_DISABLED)) {
- menu.findItem(
- R.id.mgmt_account_enable)
- .setVisible(true);
- menu.findItem(
- R.id.mgmt_account_disable)
- .setVisible(false);
- } else {
- menu.findItem(
- R.id.mgmt_account_disable)
- .setVisible(true);
- menu.findItem(
- R.id.mgmt_account_enable)
- .setVisible(false);
- }
- return true;
- }
-
- @Override
- public void onDestroyActionMode(
- ActionMode mode) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public boolean onCreateActionMode(
- ActionMode mode, Menu menu) {
- MenuInflater inflater = mode
- .getMenuInflater();
- inflater.inflate(
- R.menu.manageaccounts_context,
- menu);
- return true;
- }
-
- @Override
- public boolean onActionItemClicked(
- final ActionMode mode,
- MenuItem item) {
- if (item.getItemId() == R.id.mgmt_account_edit) {
- editAccount(selectedAccountForActionMode);
- } else if (item.getItemId() == R.id.mgmt_account_disable) {
- selectedAccountForActionMode
- .setOption(
- Account.OPTION_DISABLED,
- true);
- xmppConnectionService
- .updateAccount(selectedAccountForActionMode);
- mode.finish();
- } else if (item.getItemId() == R.id.mgmt_account_enable) {
- selectedAccountForActionMode
- .setOption(
- Account.OPTION_DISABLED,
- false);
- xmppConnectionService
- .updateAccount(selectedAccountForActionMode);
- mode.finish();
- } else if (item.getItemId() == R.id.mgmt_account_delete) {
- AlertDialog.Builder builder = new AlertDialog.Builder(
- activity);
- builder.setTitle(getString(R.string.mgmt_account_are_you_sure));
- builder.setIconAttribute(android.R.attr.alertDialogIcon);
- builder.setMessage(getString(R.string.mgmt_account_delete_confirm_text));
- builder.setPositiveButton(
- getString(R.string.delete),
- new OnClickListener() {
-
- @Override
- public void onClick(
- DialogInterface dialog,
- int which) {
- xmppConnectionService
- .deleteAccount(selectedAccountForActionMode);
- selectedAccountForActionMode = null;
- mode.finish();
- }
- });
- builder.setNegativeButton(
- getString(R.string.cancel),
- null);
- builder.create().show();
- } else if (item.getItemId() == R.id.mgmt_account_announce_pgp) {
- if (activity.hasPgp()) {
- mode.finish();
- announcePgp(
- selectedAccountForActionMode,
- null);
- } else {
- activity.showInstallPgpDialog();
- }
- } else if (item.getItemId() == R.id.mgmt_otr_key) {
- AlertDialog.Builder builder = new AlertDialog.Builder(
- activity);
- builder.setTitle("OTR Fingerprint");
- String fingerprintTxt = selectedAccountForActionMode
- .getOtrFingerprint(getApplicationContext());
- View view = (View) getLayoutInflater()
- .inflate(
- R.layout.otr_fingerprint,
- null);
- if (fingerprintTxt != null) {
- TextView fingerprint = (TextView) view
- .findViewById(R.id.otr_fingerprint);
- TextView noFingerprintView = (TextView) view
- .findViewById(R.id.otr_no_fingerprint);
- fingerprint
- .setText(fingerprintTxt);
- fingerprint
- .setVisibility(View.VISIBLE);
- noFingerprintView
- .setVisibility(View.GONE);
- }
- builder.setView(view);
- builder.setPositiveButton(
- getString(R.string.done),
- null);
- builder.create().show();
- } else if (item.getItemId() == R.id.mgmt_account_info) {
- AlertDialog.Builder builder = new AlertDialog.Builder(
- activity);
- builder.setTitle(getString(R.string.account_info));
- if (selectedAccountForActionMode
- .getStatus() == Account.STATUS_ONLINE) {
- XmppConnection xmpp = selectedAccountForActionMode
- .getXmppConnection();
- long connectionAge = (SystemClock
- .elapsedRealtime() - xmpp.lastConnect) / 60000;
- long sessionAge = (SystemClock
- .elapsedRealtime() - xmpp.lastSessionStarted) / 60000;
- long connectionAgeHours = connectionAge / 60;
- long sessionAgeHours = sessionAge / 60;
- View view = (View) getLayoutInflater()
- .inflate(
- R.layout.server_info,
- null);
- TextView connection = (TextView) view
- .findViewById(R.id.connection);
- TextView session = (TextView) view
- .findViewById(R.id.session);
- TextView pcks_sent = (TextView) view
- .findViewById(R.id.pcks_sent);
- TextView pcks_received = (TextView) view
- .findViewById(R.id.pcks_received);
- TextView carbon = (TextView) view
- .findViewById(R.id.carbon);
- TextView stream = (TextView) view
- .findViewById(R.id.stream);
- TextView roster = (TextView) view
- .findViewById(R.id.roster);
- TextView presences = (TextView) view
- .findViewById(R.id.number_presences);
- presences.setText(selectedAccountForActionMode
- .countPresences()
- + "");
- pcks_received.setText(""
- + xmpp.getReceivedStanzas());
- pcks_sent.setText(""
- + xmpp.getSentStanzas());
- if (connectionAgeHours >= 2) {
- connection
- .setText(connectionAgeHours
- + " "
- + getString(R.string.hours));
- } else {
- connection
- .setText(connectionAge
- + " "
- + getString(R.string.mins));
- }
- if (xmpp.hasFeatureStreamManagment()) {
- if (sessionAgeHours >= 2) {
- session.setText(sessionAgeHours
- + " "
- + getString(R.string.hours));
- } else {
- session.setText(sessionAge
- + " "
- + getString(R.string.mins));
- }
- stream.setText(getString(R.string.yes));
- } else {
- stream.setText(getString(R.string.no));
- session.setText(connection
- .getText());
- }
- if (xmpp.hasFeaturesCarbon()) {
- carbon.setText(getString(R.string.yes));
- } else {
- carbon.setText(getString(R.string.no));
- }
- if (xmpp.hasFeatureRosterManagment()) {
- roster.setText(getString(R.string.yes));
- } else {
- roster.setText(getString(R.string.no));
- }
- builder.setView(view);
- } else {
- builder.setMessage(getString(R.string.mgmt_account_account_offline));
- }
- builder.setPositiveButton(
- getString(R.string.hide),
- null);
- builder.create().show();
- }
- return true;
- }
+ registerForContextMenu(accountListView);
+ }
- }));
- return true;
- } else {
- return false;
- }
- }
- });
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v,
+ ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, v, menuInfo);
+ activity.getMenuInflater().inflate(R.menu.manageaccounts_context, menu);
+ AdapterView.AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
+ this.selectedAccount = accountList.get(acmi.position);
+ if (this.selectedAccount.isOptionSet(Account.OPTION_DISABLED)) {
+ menu.findItem(R.id.mgmt_account_disable).setVisible(false);
+ menu.findItem(R.id.mgmt_account_announce_pgp).setVisible(false);
+ menu.findItem(R.id.mgmt_account_publish_avatar).setVisible(false);
+ } else {
+ menu.findItem(R.id.mgmt_account_enable).setVisible(false);
+ }
+ menu.setHeaderTitle(this.selectedAccount.getJid());
}
@Override
@@ -422,13 +98,7 @@ public class ManageAccountActivity extends XmppActivity {
xmppConnectionService.setOnAccountListChangedListener(accountChanged);
this.accountList.clear();
this.accountList.addAll(xmppConnectionService.getAccounts());
- accountListViewAdapter.notifyDataSetChanged();
- if ((this.accountList.size() == 0) && (this.firstrun)) {
- getActionBar().setDisplayHomeAsUpEnabled(false);
- getActionBar().setHomeButtonEnabled(false);
- addAccount();
- this.firstrun = false;
- }
+ mAccountAdapter.notifyDataSetChanged();
}
@Override
@@ -438,10 +108,33 @@ public class ManageAccountActivity extends XmppActivity {
}
@Override
+ public boolean onContextItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.mgmt_account_publish_avatar:
+ publishAvatar(selectedAccount);
+ return true;
+ case R.id.mgmt_account_disable:
+ disableAccount(selectedAccount);
+ return true;
+ case R.id.mgmt_account_enable:
+ enableAccount(selectedAccount);
+ return true;
+ case R.id.mgmt_account_delete:
+ deleteAccount(selectedAccount);
+ return true;
+ case R.id.mgmt_account_announce_pgp:
+ publishOpenPGPPublicKey(selectedAccount);
+ default:
+ return super.onContextItemSelected(item);
+ }
+ }
+
+ @Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_add_account:
- addAccount();
+ startActivity(new Intent(getApplicationContext(),
+ EditAccountActivity.class));
break;
default:
break;
@@ -452,7 +145,8 @@ public class ManageAccountActivity extends XmppActivity {
@Override
public boolean onNavigateUp() {
if (xmppConnectionService.getConversations().size() == 0) {
- Intent contactsIntent = new Intent(this, StartConversationActivity.class);
+ Intent contactsIntent = new Intent(this,
+ StartConversationActivity.class);
contactsIntent.setFlags(
// if activity exists in stack, pop the stack and go back to it
Intent.FLAG_ACTIVITY_CLEAR_TOP |
@@ -470,57 +164,51 @@ public class ManageAccountActivity extends XmppActivity {
}
private void editAccount(Account account) {
- EditAccountDialog dialog = new EditAccountDialog();
- dialog.setAccount(account);
- dialog.setEditAccountListener(new EditAccountListener() {
-
- @Override
- public void onAccountEdited(Account account) {
- xmppConnectionService.updateAccount(account);
- if (actionMode != null) {
- actionMode.finish();
- }
- }
- });
- dialog.show(getFragmentManager(), "edit_account");
- dialog.setKnownHosts(xmppConnectionService.getKnownHosts(), this);
+ Intent intent = new Intent(this, EditAccountActivity.class);
+ intent.putExtra("jid", account.getJid());
+ startActivity(intent);
+ }
+ private void publishAvatar(Account account) {
+ Intent intent = new Intent(getApplicationContext(),
+ PublishProfilePictureActivity.class);
+ intent.putExtra("account", account.getJid());
+ startActivity(intent);
}
- protected void addAccount() {
- final Activity activity = this;
- EditAccountDialog dialog = new EditAccountDialog();
- dialog.setEditAccountListener(new EditAccountListener() {
+ private void disableAccount(Account account) {
+ account.setOption(Account.OPTION_DISABLED, true);
+ xmppConnectionService.updateAccount(account);
+ }
- @Override
- public void onAccountEdited(Account account) {
- xmppConnectionService.createAccount(account);
- activity.getActionBar().setDisplayHomeAsUpEnabled(true);
- activity.getActionBar().setHomeButtonEnabled(true);
- }
- });
- dialog.show(getFragmentManager(), "add_account");
- dialog.setKnownHosts(xmppConnectionService.getKnownHosts(), this);
+ private void enableAccount(Account account) {
+ account.setOption(Account.OPTION_DISABLED, false);
+ xmppConnectionService.updateAccount(account);
}
- @Override
- public void onActionModeStarted(ActionMode mode) {
- super.onActionModeStarted(mode);
- this.isActionMode = true;
+ private void publishOpenPGPPublicKey(Account account) {
+ if (activity.hasPgp()) {
+ announcePgp(account, null);
+ } else {
+ this.showInstallPgpDialog();
+ }
}
- @Override
- public void onActionModeFinished(ActionMode mode) {
- super.onActionModeFinished(mode);
- this.isActionMode = false;
- accountListView.clearChoices();
- accountListView.requestLayout();
- accountListView.post(new Runnable() {
- @Override
- public void run() {
- accountListView.setChoiceMode(ListView.CHOICE_MODE_NONE);
- }
- });
+ private void deleteAccount(final Account account) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+ builder.setTitle(getString(R.string.mgmt_account_are_you_sure));
+ builder.setIconAttribute(android.R.attr.alertDialogIcon);
+ builder.setMessage(getString(R.string.mgmt_account_delete_confirm_text));
+ builder.setPositiveButton(getString(R.string.delete),
+ new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ xmppConnectionService.deleteAccount(account);
+ selectedAccount = null;
+ }
+ });
+ builder.setNegativeButton(getString(R.string.cancel), null);
+ builder.create().show();
}
@Override
@@ -528,7 +216,7 @@ public class ManageAccountActivity extends XmppActivity {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
if (requestCode == REQUEST_ANNOUNCE_PGP) {
- announcePgp(selectedAccountForActionMode, null);
+ announcePgp(selectedAccount, null);
}
}
}
diff --git a/src/eu/siacs/conversations/ui/PublishProfilePictureActivity.java b/src/eu/siacs/conversations/ui/PublishProfilePictureActivity.java
new file mode 100644
index 00000000..c4c1b45e
--- /dev/null
+++ b/src/eu/siacs/conversations/ui/PublishProfilePictureActivity.java
@@ -0,0 +1,242 @@
+package eu.siacs.conversations.ui;
+
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnLongClickListener;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.utils.PhoneHelper;
+import eu.siacs.conversations.xmpp.pep.Avatar;
+
+public class PublishProfilePictureActivity extends XmppActivity {
+
+ private static final int REQUEST_CHOOSE_FILE = 0xac23;
+
+ private ImageView avatar;
+ private TextView accountTextView;
+ private TextView hintOrWarning;
+ private TextView secondaryHint;
+ private Button cancelButton;
+ private Button publishButton;
+
+ private Uri avatarUri;
+ private Uri defaultUri;
+
+ private Account account;
+
+ private boolean support = false;
+
+ private boolean mInitialAccountSetup;
+
+ private UiCallback<Avatar> avatarPublication = new UiCallback<Avatar>() {
+
+ @Override
+ public void success(Avatar object) {
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ if (mInitialAccountSetup) {
+ startActivity(new Intent(getApplicationContext(),
+ StartConversationActivity.class));
+ }
+ finish();
+ }
+ });
+ }
+
+ @Override
+ public void error(final int errorCode, Avatar object) {
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ hintOrWarning.setText(errorCode);
+ hintOrWarning.setTextColor(getWarningTextColor());
+ publishButton.setText(R.string.publish_avatar);
+ enablePublishButton();
+ }
+ });
+
+ }
+
+ @Override
+ public void userInputRequried(PendingIntent pi, Avatar object) {
+ }
+ };
+
+ private OnLongClickListener backToDefaultListener = new OnLongClickListener() {
+
+ @Override
+ public boolean onLongClick(View v) {
+ avatarUri = defaultUri;
+ loadImageIntoPreview(defaultUri);
+ return true;
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_publish_profile_picture);
+ this.avatar = (ImageView) findViewById(R.id.account_image);
+ this.cancelButton = (Button) findViewById(R.id.cancel_button);
+ this.publishButton = (Button) findViewById(R.id.publish_button);
+ this.accountTextView = (TextView) findViewById(R.id.account);
+ this.hintOrWarning = (TextView) findViewById(R.id.hint_or_warning);
+ this.secondaryHint = (TextView) findViewById(R.id.secondary_hint);
+ this.publishButton.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (avatarUri != null) {
+ publishButton.setText(R.string.publishing);
+ disablePublishButton();
+ xmppConnectionService.publishAvatar(account, avatarUri,
+ avatarPublication);
+ }
+ }
+ });
+ this.cancelButton.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (mInitialAccountSetup) {
+ startActivity(new Intent(getApplicationContext(),
+ StartConversationActivity.class));
+ }
+ finish();
+ }
+ });
+ this.avatar.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ 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_CHOOSE_FILE);
+ }
+ });
+ this.defaultUri = PhoneHelper.getSefliUri(getApplicationContext());
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode,
+ final Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (resultCode == RESULT_OK) {
+ if (requestCode == REQUEST_CHOOSE_FILE) {
+ this.avatarUri = data.getData();
+ if (xmppConnectionServiceBound) {
+ loadImageIntoPreview(this.avatarUri);
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void onBackendConnected() {
+ if (getIntent() != null) {
+ String jid = getIntent().getStringExtra("account");
+ if (jid != null) {
+ this.account = xmppConnectionService.findAccountByJid(jid);
+ if (this.account.getXmppConnection() != null) {
+ this.support = this.account.getXmppConnection()
+ .getFeatures().pubsub();
+ }
+ if (this.avatarUri == null) {
+ if (this.account.getAvatar() != null
+ || this.defaultUri == null) {
+ this.avatar.setImageBitmap(this.account.getImage(
+ getApplicationContext(), 384));
+ if (this.defaultUri != null) {
+ this.avatar
+ .setOnLongClickListener(this.backToDefaultListener);
+ } else {
+ this.secondaryHint.setVisibility(View.INVISIBLE);
+ }
+ if (!support) {
+ this.hintOrWarning
+ .setTextColor(getWarningTextColor());
+ this.hintOrWarning
+ .setText(R.string.error_publish_avatar_no_server_support);
+ }
+ } else {
+ this.avatarUri = this.defaultUri;
+ loadImageIntoPreview(this.defaultUri);
+ this.secondaryHint.setVisibility(View.INVISIBLE);
+ }
+ } else {
+ loadImageIntoPreview(avatarUri);
+ }
+ this.accountTextView.setText(this.account.getJid());
+ }
+ }
+
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ if (getIntent() != null) {
+ this.mInitialAccountSetup = getIntent().getBooleanExtra("setup",
+ false);
+ }
+ if (this.mInitialAccountSetup) {
+ this.cancelButton.setText(R.string.skip);
+ }
+ }
+
+ protected void loadImageIntoPreview(Uri uri) {
+ Bitmap bm = xmppConnectionService.getFileBackend().cropCenterSquare(
+ uri, 384);
+ if (bm == null) {
+ disablePublishButton();
+ this.hintOrWarning.setTextColor(getWarningTextColor());
+ this.hintOrWarning
+ .setText(R.string.error_publish_avatar_converting);
+ return;
+ }
+ this.avatar.setImageBitmap(bm);
+ if (support) {
+ enablePublishButton();
+ this.publishButton.setText(R.string.publish_avatar);
+ this.hintOrWarning.setText(R.string.publish_avatar_explanation);
+ this.hintOrWarning.setTextColor(getPrimaryTextColor());
+ } else {
+ disablePublishButton();
+ this.hintOrWarning.setTextColor(getWarningTextColor());
+ this.hintOrWarning
+ .setText(R.string.error_publish_avatar_no_server_support);
+ }
+ if (this.defaultUri != null && uri.equals(this.defaultUri)) {
+ this.secondaryHint.setVisibility(View.INVISIBLE);
+ this.avatar.setOnLongClickListener(null);
+ } else if (this.defaultUri != null) {
+ this.secondaryHint.setVisibility(View.VISIBLE);
+ this.avatar.setOnLongClickListener(this.backToDefaultListener);
+ }
+ }
+
+ protected void enablePublishButton() {
+ this.publishButton.setEnabled(true);
+ this.publishButton.setTextColor(getPrimaryTextColor());
+ }
+
+ protected void disablePublishButton() {
+ this.publishButton.setEnabled(false);
+ this.publishButton.setTextColor(getSecondaryTextColor());
+ }
+
+}
diff --git a/src/eu/siacs/conversations/ui/SettingsActivity.java b/src/eu/siacs/conversations/ui/SettingsActivity.java
index abaf8c68..f8fd9469 100644
--- a/src/eu/siacs/conversations/ui/SettingsActivity.java
+++ b/src/eu/siacs/conversations/ui/SettingsActivity.java
@@ -1,9 +1,8 @@
package eu.siacs.conversations.ui;
-import android.app.Activity;
import android.os.Bundle;
-public class SettingsActivity extends Activity {
+public class SettingsActivity extends XmppActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -13,4 +12,9 @@ public class SettingsActivity extends Activity {
.replace(android.R.id.content, new SettingsFragment()).commit();
}
+ @Override
+ void onBackendConnected() {
+
+ }
+
}
diff --git a/src/eu/siacs/conversations/ui/StartConversationActivity.java b/src/eu/siacs/conversations/ui/StartConversationActivity.java
index d12d2878..bcb9f1dd 100644
--- a/src/eu/siacs/conversations/ui/StartConversationActivity.java
+++ b/src/eu/siacs/conversations/ui/StartConversationActivity.java
@@ -21,6 +21,7 @@ import android.text.Editable;
import android.text.TextWatcher;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
+import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@@ -63,6 +64,7 @@ public class StartConversationActivity extends XmppActivity {
private List<String> mKnownHosts;
private List<String> mKnownConferenceHosts;
+ private Menu mOptionsMenu;
private EditText mSearchEditText;
public int conference_context_id;
@@ -141,14 +143,16 @@ public class StartConversationActivity extends XmppActivity {
}
};
private OnRosterUpdate onRosterUpdate = new OnRosterUpdate() {
-
+
@Override
public void onRosterUpdate() {
runOnUiThread(new Runnable() {
-
+
@Override
public void run() {
- filter(mSearchEditText.getText().toString());
+ if (mSearchEditText != null) {
+ filter(mSearchEditText.getText().toString());
+ }
}
});
}
@@ -187,7 +191,8 @@ public class StartConversationActivity extends XmppActivity {
}
});
- mConferenceAdapter = new ListItemAdapter(getApplicationContext(),conferences);
+ mConferenceAdapter = new ListItemAdapter(getApplicationContext(),
+ conferences);
mConferenceListFragment.setListAdapter(mConferenceAdapter);
mConferenceListFragment.setContextMenu(R.menu.conference_context);
mConferenceListFragment
@@ -200,7 +205,8 @@ public class StartConversationActivity extends XmppActivity {
}
});
- mContactsAdapter = new ListItemAdapter(getApplicationContext(),contacts);
+ mContactsAdapter = new ListItemAdapter(getApplicationContext(),
+ contacts);
mContactsListFragment.setListAdapter(mContactsAdapter);
mContactsListFragment.setContextMenu(R.menu.contact_context);
mContactsListFragment
@@ -214,7 +220,7 @@ public class StartConversationActivity extends XmppActivity {
});
}
-
+
@Override
public void onStop() {
super.onStop();
@@ -266,11 +272,10 @@ public class StartConversationActivity extends XmppActivity {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setNegativeButton(R.string.cancel, null);
builder.setTitle(R.string.action_delete_contact);
- builder.setMessage(
- getString(R.string.remove_contact_text,
- contact.getJid()));
- builder.setPositiveButton(R.string.delete,new OnClickListener() {
-
+ builder.setMessage(getString(R.string.remove_contact_text,
+ contact.getJid()));
+ builder.setPositiveButton(R.string.delete, new OnClickListener() {
+
@Override
public void onClick(DialogInterface dialog, int which) {
xmppConnectionService.deleteContactOnServer(contact);
@@ -278,21 +283,20 @@ public class StartConversationActivity extends XmppActivity {
}
});
builder.create().show();
-
+
}
protected void deleteConference() {
int position = conference_context_id;
final Bookmark bookmark = (Bookmark) conferences.get(position);
-
+
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setNegativeButton(R.string.cancel, null);
builder.setTitle(R.string.delete_bookmark);
- builder.setMessage(
- getString(R.string.remove_bookmark_text,
- bookmark.getJid()));
- builder.setPositiveButton(R.string.delete,new OnClickListener() {
-
+ builder.setMessage(getString(R.string.remove_bookmark_text,
+ bookmark.getJid()));
+ builder.setPositiveButton(R.string.delete, new OnClickListener() {
+
@Override
public void onClick(DialogInterface dialog, int which) {
bookmark.unregisterConversation();
@@ -303,7 +307,7 @@ public class StartConversationActivity extends XmppActivity {
}
});
builder.create().show();
-
+
}
protected void showCreateContactDialog() {
@@ -327,6 +331,9 @@ public class StartConversationActivity extends XmppActivity {
@Override
public void onClick(View v) {
+ if (!xmppConnectionServiceBound) {
+ return;
+ }
if (Validator.isValidJid(jid.getText().toString())) {
String accountJid = (String) spinner
.getSelectedItem();
@@ -373,6 +380,9 @@ public class StartConversationActivity extends XmppActivity {
@Override
public void onClick(View v) {
+ if (!xmppConnectionServiceBound) {
+ return;
+ }
if (Validator.isValidJid(jid.getText().toString())) {
String accountJid = (String) spinner
.getSelectedItem();
@@ -394,7 +404,8 @@ public class StartConversationActivity extends XmppActivity {
conferenceJid, true);
conversation.setBookmark(bookmark);
if (!conversation.getMucOptions().online()) {
- xmppConnectionService.joinMuc(conversation);
+ xmppConnectionService
+ .joinMuc(conversation);
}
switchToConversation(conversation);
}
@@ -430,6 +441,7 @@ public class StartConversationActivity extends XmppActivity {
@Override
public boolean onCreateOptionsMenu(Menu menu) {
+ this.mOptionsMenu = menu;
getMenuInflater().inflate(R.menu.start_conversation, menu);
MenuItem menuCreateContact = (MenuItem) menu
.findItem(R.id.action_create_contact);
@@ -463,8 +475,17 @@ public class StartConversationActivity extends XmppActivity {
}
@Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_SEARCH && !event.isLongPress()) {
+ mOptionsMenu.findItem(R.id.action_search).expandActionView();
+ return true;
+ }
+ return super.onKeyUp(keyCode, event);
+ }
+
+ @Override
void onBackendConnected() {
- xmppConnectionService.setOnRosterUpdateListener(this.onRosterUpdate );
+ xmppConnectionService.setOnRosterUpdateListener(this.onRosterUpdate);
if (mSearchEditText != null) {
filter(mSearchEditText.getText().toString());
} else {
@@ -482,8 +503,10 @@ public class StartConversationActivity extends XmppActivity {
}
protected void filter(String needle) {
- this.filterContacts(needle);
- this.filterConferences(needle);
+ if (xmppConnectionServiceBound) {
+ this.filterContacts(needle);
+ this.filterConferences(needle);
+ }
}
protected void filterContacts(String needle) {
diff --git a/src/eu/siacs/conversations/ui/XmppActivity.java b/src/eu/siacs/conversations/ui/XmppActivity.java
index fad4d026..44043a79 100644
--- a/src/eu/siacs/conversations/ui/XmppActivity.java
+++ b/src/eu/siacs/conversations/ui/XmppActivity.java
@@ -31,22 +31,24 @@ import android.widget.EditText;
public abstract class XmppActivity extends Activity {
- public static final int REQUEST_ANNOUNCE_PGP = 0x73731;
- protected static final int REQUEST_INVITE_TO_CONVERSATION = 0x341830;
+ protected static final int REQUEST_ANNOUNCE_PGP = 0x0101;
+ protected static final int REQUEST_INVITE_TO_CONVERSATION = 0x0102;
protected final static String LOGTAG = "xmppService";
public XmppConnectionService xmppConnectionService;
public boolean xmppConnectionServiceBound = false;
protected boolean handledViewIntent = false;
-
+
protected int mPrimaryTextColor;
protected int mSecondaryTextColor;
-
+ protected int mWarningTextColor;
+ protected int mPrimaryColor;
+
protected interface OnValueEdited {
public void onValueEdited(String value);
}
-
+
public interface OnPresenceSelected {
public void onPresenceSelected();
}
@@ -152,6 +154,9 @@ public abstract class XmppActivity extends Activity {
case R.id.action_accounts:
startActivity(new Intent(this, ManageAccountActivity.class));
break;
+ case android.R.id.home:
+ finish();
+ break;
}
return super.onOptionsItemSelected(item);
}
@@ -162,6 +167,8 @@ public abstract class XmppActivity extends Activity {
ExceptionHelper.init(getApplicationContext());
mPrimaryTextColor = getResources().getColor(R.color.primarytext);
mSecondaryTextColor = getResources().getColor(R.color.secondarytext);
+ mWarningTextColor = getResources().getColor(R.color.warningtext);
+ mPrimaryColor = getResources().getColor(R.color.primary);
}
public void switchToConversation(Conversation conversation) {
@@ -199,11 +206,12 @@ public abstract class XmppActivity extends Activity {
}
protected void inviteToConversation(Conversation conversation) {
- Intent intent = new Intent(getApplicationContext(), ChooseContactActivity.class);
- intent.putExtra("conversation",conversation.getUuid());
+ Intent intent = new Intent(getApplicationContext(),
+ ChooseContactActivity.class);
+ intent.putExtra("conversation", conversation.getUuid());
startActivityForResult(intent, REQUEST_INVITE_TO_CONVERSATION);
}
-
+
protected void announcePgp(Account account, final Conversation conversation) {
xmppConnectionService.getPgpEngine().generateSignature(account,
"online", new UiCallback<Account>() {
@@ -214,7 +222,8 @@ public abstract class XmppActivity extends Activity {
try {
startIntentSenderForResult(pi.getIntentSender(),
REQUEST_ANNOUNCE_PGP, null, 0, 0, 0);
- } catch (SendIntentException e) {}
+ } catch (SendIntentException e) {
+ }
}
@Override
@@ -275,15 +284,17 @@ public abstract class XmppActivity extends Activity {
builder.create().show();
}
- protected void quickEdit(final String previousValue, final OnValueEdited callback) {
+ protected void quickEdit(final String previousValue,
+ final OnValueEdited callback) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
- View view = (View) getLayoutInflater().inflate(R.layout.quickedit, null);
+ View view = (View) getLayoutInflater()
+ .inflate(R.layout.quickedit, null);
final EditText editor = (EditText) view.findViewById(R.id.editor);
editor.setText(previousValue);
builder.setView(view);
builder.setNegativeButton(R.string.cancel, null);
builder.setPositiveButton(R.string.edit, new OnClickListener() {
-
+
@Override
public void onClick(DialogInterface dialog, int which) {
String value = editor.getText().toString();
@@ -294,11 +305,11 @@ public abstract class XmppActivity extends Activity {
});
builder.create().show();
}
-
+
public void selectPresence(final Conversation conversation,
final OnPresenceSelected listener) {
Contact contact = conversation.getContact();
- if (contact == null) {
+ if (!contact.showInRoster()) {
showAddToRosterDialog(conversation);
} else {
Presences presences = contact.getPresences();
@@ -346,26 +357,37 @@ public abstract class XmppActivity extends Activity {
}
}
}
-
+
protected void onActivityResult(int requestCode, int resultCode,
final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
- if (requestCode == REQUEST_INVITE_TO_CONVERSATION && resultCode == RESULT_OK) {
+ if (requestCode == REQUEST_INVITE_TO_CONVERSATION
+ && resultCode == RESULT_OK) {
String contactJid = data.getStringExtra("contact");
String conversationUuid = data.getStringExtra("conversation");
- Conversation conversation = xmppConnectionService.findConversationByUuid(conversationUuid);
+ Conversation conversation = xmppConnectionService
+ .findConversationByUuid(conversationUuid);
if (conversation.getMode() == Conversation.MODE_MULTI) {
xmppConnectionService.invite(conversation, contactJid);
}
- Log.d("xmppService","inviting "+contactJid+" to "+conversation.getName(true));
+ Log.d("xmppService", "inviting " + contactJid + " to "
+ + conversation.getName(true));
}
}
-
+
public int getSecondaryTextColor() {
return this.mSecondaryTextColor;
}
-
+
public int getPrimaryTextColor() {
return this.mPrimaryTextColor;
}
+
+ public int getWarningTextColor() {
+ return this.mWarningTextColor;
+ }
+
+ public int getPrimaryColor() {
+ return this.mPrimaryColor;
+ }
}
diff --git a/src/eu/siacs/conversations/ui/adapter/AccountAdapter.java b/src/eu/siacs/conversations/ui/adapter/AccountAdapter.java
new file mode 100644
index 00000000..cd8b376f
--- /dev/null
+++ b/src/eu/siacs/conversations/ui/adapter/AccountAdapter.java
@@ -0,0 +1,101 @@
+package eu.siacs.conversations.ui.adapter;
+
+import java.util.List;
+
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.ui.XmppActivity;
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+public class AccountAdapter extends ArrayAdapter<Account> {
+
+ private XmppActivity activity;
+
+ public AccountAdapter(XmppActivity activity, List<Account> objects) {
+ super(activity, 0, objects);
+ this.activity = activity;
+ }
+
+ @Override
+ public View getView(int position, View view, ViewGroup parent) {
+ Account account = getItem(position);
+ if (view == null) {
+ LayoutInflater inflater = (LayoutInflater) getContext()
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ view = (View) inflater.inflate(R.layout.account_row, parent, false);
+ }
+ TextView jid = (TextView) view.findViewById(R.id.account_jid);
+ jid.setText(account.getJid());
+ TextView statusView = (TextView) view.findViewById(R.id.account_status);
+ ImageView imageView = (ImageView) view.findViewById(R.id.account_image);
+ imageView.setImageBitmap(account.getImage(activity,48));
+ switch (account.getStatus()) {
+ case Account.STATUS_DISABLED:
+ statusView.setText(getContext().getString(
+ R.string.account_status_disabled));
+ statusView.setTextColor(activity.getSecondaryTextColor());
+ break;
+ case Account.STATUS_ONLINE:
+ statusView.setText(getContext().getString(
+ R.string.account_status_online));
+ statusView.setTextColor(activity.getPrimaryColor());
+ break;
+ case Account.STATUS_CONNECTING:
+ statusView.setText(getContext().getString(
+ R.string.account_status_connecting));
+ statusView.setTextColor(activity.getSecondaryTextColor());
+ break;
+ case Account.STATUS_OFFLINE:
+ statusView.setText(getContext().getString(
+ R.string.account_status_offline));
+ statusView.setTextColor(activity.getWarningTextColor());
+ break;
+ case Account.STATUS_UNAUTHORIZED:
+ statusView.setText(getContext().getString(
+ R.string.account_status_unauthorized));
+ statusView.setTextColor(activity.getWarningTextColor());
+ break;
+ case Account.STATUS_SERVER_NOT_FOUND:
+ statusView.setText(getContext().getString(
+ R.string.account_status_not_found));
+ statusView.setTextColor(activity.getWarningTextColor());
+ break;
+ case Account.STATUS_NO_INTERNET:
+ statusView.setText(getContext().getString(
+ R.string.account_status_no_internet));
+ statusView.setTextColor(activity.getWarningTextColor());
+ break;
+ case Account.STATUS_REGISTRATION_FAILED:
+ statusView.setText(getContext().getString(
+ R.string.account_status_regis_fail));
+ statusView.setTextColor(activity.getWarningTextColor());
+ break;
+ case Account.STATUS_REGISTRATION_CONFLICT:
+ statusView.setText(getContext().getString(
+ R.string.account_status_regis_conflict));
+ statusView.setTextColor(activity.getWarningTextColor());
+ break;
+ case Account.STATUS_REGISTRATION_SUCCESSFULL:
+ statusView.setText(getContext().getString(
+ R.string.account_status_regis_success));
+ statusView.setTextColor(activity.getSecondaryTextColor());
+ break;
+ case Account.STATUS_REGISTRATION_NOT_SUPPORTED:
+ statusView.setText(getContext().getString(
+ R.string.account_status_regis_not_sup));
+ statusView.setTextColor(activity.getWarningTextColor());
+ break;
+ default:
+ statusView.setText("");
+ break;
+ }
+
+ return view;
+ }
+}
diff --git a/src/eu/siacs/conversations/ui/adapter/ConversationAdapter.java b/src/eu/siacs/conversations/ui/adapter/ConversationAdapter.java
new file mode 100644
index 00000000..66403804
--- /dev/null
+++ b/src/eu/siacs/conversations/ui/adapter/ConversationAdapter.java
@@ -0,0 +1,107 @@
+package eu.siacs.conversations.ui.adapter;
+
+import java.util.List;
+
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.ui.ConversationActivity;
+import eu.siacs.conversations.utils.UIHelper;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.Typeface;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+public class ConversationAdapter extends ArrayAdapter<Conversation> {
+
+ ConversationActivity activity;
+
+ public ConversationAdapter(ConversationActivity activity,
+ List<Conversation> conversations) {
+ super(activity, 0, conversations);
+ this.activity = activity;
+ }
+
+ @Override
+ public View getView(int position, View view, ViewGroup parent) {
+ if (view == null) {
+ LayoutInflater inflater = (LayoutInflater) activity
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ view = (View) inflater.inflate(R.layout.conversation_list_row,
+ parent, false);
+ }
+ Conversation conv = getItem(position);
+ if (!activity.getSlidingPaneLayout().isSlideable()) {
+ if (conv == activity.getSelectedConversation()) {
+ view.setBackgroundColor(0xffdddddd);
+ } else {
+ view.setBackgroundColor(Color.TRANSPARENT);
+ }
+ } else {
+ view.setBackgroundColor(Color.TRANSPARENT);
+ }
+ TextView convName = (TextView) view
+ .findViewById(R.id.conversation_name);
+ convName.setText(conv.getName(true));
+ TextView convLastMsg = (TextView) view
+ .findViewById(R.id.conversation_lastmsg);
+ ImageView imagePreview = (ImageView) view
+ .findViewById(R.id.conversation_lastimage);
+
+ Message latestMessage = conv.getLatestMessage();
+
+ if (latestMessage.getType() == Message.TYPE_TEXT
+ || latestMessage.getType() == Message.TYPE_PRIVATE) {
+ if ((latestMessage.getEncryption() != Message.ENCRYPTION_PGP)
+ && (latestMessage.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED)) {
+ convLastMsg.setText(conv.getLatestMessage().getBody());
+ } else {
+ convLastMsg.setText(activity
+ .getText(R.string.encrypted_message_received));
+ }
+ convLastMsg.setVisibility(View.VISIBLE);
+ imagePreview.setVisibility(View.GONE);
+ } else if (latestMessage.getType() == Message.TYPE_IMAGE) {
+ if (latestMessage.getStatus() >= Message.STATUS_RECIEVED) {
+ convLastMsg.setVisibility(View.GONE);
+ imagePreview.setVisibility(View.VISIBLE);
+ activity.loadBitmap(latestMessage, imagePreview);
+ } else {
+ convLastMsg.setVisibility(View.VISIBLE);
+ imagePreview.setVisibility(View.GONE);
+ if (latestMessage.getStatus() == Message.STATUS_RECEIVED_OFFER) {
+ convLastMsg.setText(activity
+ .getText(R.string.image_offered_for_download));
+ } else if (latestMessage.getStatus() == Message.STATUS_RECIEVING) {
+ convLastMsg.setText(activity
+ .getText(R.string.receiving_image));
+ } else {
+ convLastMsg.setText("");
+ }
+ }
+ }
+
+ if (!conv.isRead()) {
+ convName.setTypeface(null, Typeface.BOLD);
+ convLastMsg.setTypeface(null, Typeface.BOLD);
+ } else {
+ convName.setTypeface(null, Typeface.NORMAL);
+ convLastMsg.setTypeface(null, Typeface.NORMAL);
+ }
+
+ ((TextView) view.findViewById(R.id.conversation_lastupdate))
+ .setText(UIHelper.readableTimeDifference(getContext(), conv
+ .getLatestMessage().getTimeSent()));
+
+ ImageView profilePicture = (ImageView) view
+ .findViewById(R.id.conversation_image);
+ profilePicture.setImageBitmap(conv.getImage(activity, 56));
+
+ return view;
+ }
+}
diff --git a/src/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java b/src/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java
index 040e6266..d286052c 100644
--- a/src/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java
+++ b/src/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java
@@ -23,7 +23,10 @@ public class KnownHostsAdapter extends ArrayAdapter<String> {
}
} else if (split.length == 2) {
for (String domain : domains) {
- if (domain.contains(split[1])) {
+ if (domain.contentEquals(split[1])) {
+ suggestions.clear();
+ break;
+ } else if (domain.contains(split[1])) {
suggestions.add(split[0].toLowerCase(Locale.getDefault()) + "@" + domain);
}
}
diff --git a/src/eu/siacs/conversations/ui/adapter/MessageAdapter.java b/src/eu/siacs/conversations/ui/adapter/MessageAdapter.java
index 0a2857d2..72587f99 100644
--- a/src/eu/siacs/conversations/ui/adapter/MessageAdapter.java
+++ b/src/eu/siacs/conversations/ui/adapter/MessageAdapter.java
@@ -7,16 +7,18 @@ import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message;
-import eu.siacs.conversations.services.ImageProvider;
import eu.siacs.conversations.ui.ConversationActivity;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.jingle.JingleConnection;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.Typeface;
-import android.preference.PreferenceManager;
+import android.text.Html;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.StyleSpan;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup;
@@ -37,44 +39,38 @@ public class MessageAdapter extends ArrayAdapter<Message> {
private ConversationActivity activity;
- private Bitmap selfBitmap2;
+ private Bitmap accountBitmap;
private BitmapCache mBitmapCache = new BitmapCache();
private DisplayMetrics metrics;
- private boolean useSubject = true;
-
private OnContactPictureClicked mOnContactPictureClickedListener;
+ private OnContactPictureLongClicked mOnContactPictureLongClickedListener;
public MessageAdapter(ConversationActivity activity, List<Message> messages) {
super(activity, 0, messages);
this.activity = activity;
metrics = getContext().getResources().getDisplayMetrics();
- SharedPreferences preferences = PreferenceManager
- .getDefaultSharedPreferences(getContext());
- useSubject = preferences.getBoolean("use_subject_in_muc", true);
}
private Bitmap getSelfBitmap() {
- if (this.selfBitmap2 == null) {
+ if (this.accountBitmap == null) {
if (getCount() > 0) {
- SharedPreferences preferences = PreferenceManager
- .getDefaultSharedPreferences(getContext());
- boolean showPhoneSelfContactPicture = preferences.getBoolean(
- "show_phone_selfcontact_picture", true);
-
- this.selfBitmap2 = UIHelper.getSelfContactPicture(getItem(0)
- .getConversation().getAccount(), 48,
- showPhoneSelfContactPicture, getContext());
+ this.accountBitmap = getItem(0).getConversation().getAccount()
+ .getImage(getContext(), 48);
}
}
- return this.selfBitmap2;
+ return this.accountBitmap;
}
public void setOnContactPictureClicked(OnContactPictureClicked listener) {
this.mOnContactPictureClickedListener = listener;
}
+
+ public void setOnContactPictureLongClicked(OnContactPictureLongClicked listener) {
+ this.mOnContactPictureLongClickedListener = listener;
+ }
@Override
public int getViewTypeCount() {
@@ -130,7 +126,16 @@ public class MessageAdapter extends ArrayAdapter<Message> {
error = true;
default:
if (multiReceived) {
- info = message.getCounterpart();
+ Contact contact = message.getContact();
+ if (contact != null) {
+ info = contact.getDisplayName();
+ } else {
+ if (message.getPresence() != null) {
+ info = message.getPresence();
+ } else {
+ info = message.getCounterpart();
+ }
+ }
}
break;
}
@@ -199,14 +204,33 @@ public class MessageAdapter extends ArrayAdapter<Message> {
viewHolder.messageBody.setTextIsSelectable(false);
}
- private void displayTextMessage(ViewHolder viewHolder, String text) {
+ private void displayTextMessage(ViewHolder viewHolder, Message message) {
if (viewHolder.download_button != null) {
viewHolder.download_button.setVisibility(View.GONE);
}
viewHolder.image.setVisibility(View.GONE);
viewHolder.messageBody.setVisibility(View.VISIBLE);
- if (text != null) {
- viewHolder.messageBody.setText(text.trim());
+ if (message.getBody() != null) {
+ if (message.getType() != Message.TYPE_PRIVATE) {
+ viewHolder.messageBody.setText(message.getBody().trim());
+ } else {
+ String privateMarker;
+ if (message.getStatus() <= Message.STATUS_RECIEVED) {
+ privateMarker = activity.getString(R.string.private_message);
+ } else {
+ String to;
+ if (message.getPresence() != null) {
+ to = message.getPresence();
+ } else {
+ to = message.getCounterpart();
+ }
+ privateMarker = activity.getString(R.string.private_message_to, to);
+ }
+ SpannableString span = new SpannableString(privateMarker+" "+message.getBody());
+ span.setSpan(new ForegroundColorSpan(activity.getSecondaryTextColor()), 0, privateMarker.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ span.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 0, privateMarker.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ viewHolder.messageBody.setText(span);
+ }
} else {
viewHolder.messageBody.setText("");
}
@@ -245,8 +269,8 @@ public class MessageAdapter extends ArrayAdapter<Message> {
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setDataAndType(ImageProvider.getContentUri(message),
- "image/*");
+ intent.setDataAndType(activity.xmppConnectionService
+ .getFileBackend().getJingleFileUri(message), "image/*");
getContext().startActivity(intent);
}
});
@@ -257,7 +281,9 @@ public class MessageAdapter extends ArrayAdapter<Message> {
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_STREAM,
- ImageProvider.getContentUri(message));
+ activity.xmppConnectionService.getFileBackend()
+ .getJingleFileUri(message));
+ shareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
shareIntent.setType("image/webp");
getContext().startActivity(
Intent.createChooser(shareIntent,
@@ -277,7 +303,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
switch (type) {
case SENT:
view = (View) activity.getLayoutInflater().inflate(
- R.layout.message_sent, null);
+ R.layout.message_sent, parent, false);
viewHolder.message_box = (LinearLayout) view
.findViewById(R.id.message_box);
viewHolder.contact_picture = (ImageView) view
@@ -295,7 +321,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
break;
case RECIEVED:
view = (View) activity.getLayoutInflater().inflate(
- R.layout.message_recieved, null);
+ R.layout.message_recieved, parent, false);
viewHolder.message_box = (LinearLayout) view
.findViewById(R.id.message_box);
viewHolder.contact_picture = (ImageView) view
@@ -307,9 +333,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
if (item.getConversation().getMode() == Conversation.MODE_SINGLE) {
viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(
- item.getConversation().getName(useSubject), item
- .getConversation().getContact(),
- getContext()));
+ item.getConversation().getContact(), getContext()));
}
viewHolder.indicator = (ImageView) view
@@ -324,15 +348,13 @@ public class MessageAdapter extends ArrayAdapter<Message> {
break;
case STATUS:
view = (View) activity.getLayoutInflater().inflate(
- R.layout.message_status, null);
+ R.layout.message_status, parent, false);
viewHolder.contact_picture = (ImageView) view
.findViewById(R.id.message_photo);
if (item.getConversation().getMode() == Conversation.MODE_SINGLE) {
viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(
- item.getConversation().getName(useSubject), item
- .getConversation().getContact(),
- getContext()));
+ item.getConversation().getContact(), getContext()));
viewHolder.contact_picture.setAlpha(128);
viewHolder.contact_picture
.setOnClickListener(new OnClickListener() {
@@ -366,8 +388,17 @@ public class MessageAdapter extends ArrayAdapter<Message> {
if (type == RECIEVED) {
if (item.getConversation().getMode() == Conversation.MODE_MULTI) {
- viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(
- item.getCounterpart(), null, getContext()));
+ Contact contact = item.getContact();
+ if (contact != null) {
+ viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(
+ contact, getContext()));
+ } else {
+ String name = item.getPresence();
+ if (name==null) {
+ name = item.getCounterpart();
+ }
+ viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(name, getContext()));
+ }
viewHolder.contact_picture
.setOnClickListener(new OnClickListener() {
@@ -381,6 +412,18 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}
});
+ viewHolder.contact_picture.setOnLongClickListener(new OnLongClickListener() {
+
+ @Override
+ public boolean onLongClick(View v) {
+ if (MessageAdapter.this.mOnContactPictureLongClickedListener != null) {
+ MessageAdapter.this.mOnContactPictureLongClickedListener.onContactPictureLongClicked(item);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ });
}
}
@@ -431,7 +474,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
} else if (item.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
displayDecryptionFailed(viewHolder);
} else {
- displayTextMessage(viewHolder, item.getBody());
+ displayTextMessage(viewHolder, item);
}
}
@@ -453,20 +496,24 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}
private class BitmapCache {
- private HashMap<String, Bitmap> bitmaps = new HashMap<String, Bitmap>();
+ private HashMap<String, Bitmap> contactBitmaps = new HashMap<String, Bitmap>();
+ private HashMap<String, Bitmap> unknownBitmaps = new HashMap<String, Bitmap>();
+
+ public Bitmap get(Contact contact, Context context) {
+ if (!contactBitmaps.containsKey(contact.getJid())) {
+ contactBitmaps.put(contact.getJid(),
+ contact.getImage(48, context));
+ }
+ return contactBitmaps.get(contact.getJid());
+ }
- public Bitmap get(String name, Contact contact, Context context) {
- if (bitmaps.containsKey(name)) {
- return bitmaps.get(name);
+ public Bitmap get(String name, Context context) {
+ if (unknownBitmaps.containsKey(name)) {
+ return unknownBitmaps.get(name);
} else {
- Bitmap bm;
- if (contact != null) {
- bm = UIHelper
- .getContactPicture(contact, 48, context, false);
- } else {
- bm = UIHelper.getContactPicture(name, 48, context, false);
- }
- bitmaps.put(name, bm);
+ Bitmap bm = UIHelper
+ .getContactPicture(name, 48, context, false);
+ unknownBitmaps.put(name, bm);
return bm;
}
}
@@ -475,4 +522,8 @@ public class MessageAdapter extends ArrayAdapter<Message> {
public interface OnContactPictureClicked {
public void onContactPictureClicked(Message message);
}
+
+ public interface OnContactPictureLongClicked {
+ public void onContactPictureLongClicked(Message message);
+ }
}
diff --git a/src/eu/siacs/conversations/utils/PhoneHelper.java b/src/eu/siacs/conversations/utils/PhoneHelper.java
index bccd7210..63c17761 100644
--- a/src/eu/siacs/conversations/utils/PhoneHelper.java
+++ b/src/eu/siacs/conversations/utils/PhoneHelper.java
@@ -70,11 +70,11 @@ public class PhoneHelper {
public static Uri getSefliUri(Context context) {
String[] mProjection = new String[] { Profile._ID,
- Profile.PHOTO_THUMBNAIL_URI };
+ Profile.PHOTO_URI };
Cursor mProfileCursor = context.getContentResolver().query(
Profile.CONTENT_URI, mProjection, null, null, null);
- if (mProfileCursor.getCount() == 0) {
+ if (mProfileCursor == null || mProfileCursor.getCount() == 0) {
return null;
} else {
mProfileCursor.moveToFirst();
diff --git a/src/eu/siacs/conversations/utils/UIHelper.java b/src/eu/siacs/conversations/utils/UIHelper.java
index 1cd3403c..7a807b2f 100644
--- a/src/eu/siacs/conversations/utils/UIHelper.java
+++ b/src/eu/siacs/conversations/utils/UIHelper.java
@@ -214,13 +214,17 @@ public class UIHelper {
new String[] { conversation.getName(false) }, size,
bgColor, fgColor);
}
- String[] names = new String[members.size() + 1];
- names[0] = conversation.getMucOptions().getActualNick();
- for (int i = 0; i < members.size(); ++i) {
- names[i + 1] = members.get(i).getName();
+ ArrayList<String> names = new ArrayList<String>();
+ names.add(conversation.getMucOptions().getActualNick());
+ for(User user : members) {
+ names.add(user.getName());
+ if (names.size() > 4 ) {
+ break;
+ }
}
-
- return getUnknownContactPicture(names, size, bgColor, fgColor);
+ String[] mArrayNames = new String[names.size()];
+ names.toArray(mArrayNames);
+ return getUnknownContactPicture(mArrayNames, size, bgColor, fgColor);
}
public static Bitmap getContactPicture(Conversation conversation,
@@ -341,7 +345,7 @@ public class UIHelper {
if ((currentCon != null)
&& (currentCon.getMode() == Conversation.MODE_MULTI)
- && (!alwaysNotify)) {
+ && (!alwaysNotify) && notify) {
String nick = currentCon.getMucOptions().getActualNick();
Pattern highlight = generateNickHighlightPattern(nick);
Matcher m = highlight.matcher(currentCon.getLatestMessage()
@@ -372,8 +376,7 @@ public class UIHelper {
} else if (unread.size() == 1) {
Conversation conversation = unread.get(0);
targetUuid = conversation.getUuid();
- mBuilder.setLargeIcon(UIHelper.getContactPicture(conversation, 64,
- context, true));
+ mBuilder.setLargeIcon(conversation.getImage(context, 64));
mBuilder.setContentTitle(conversation.getName(useSubject));
if (notify) {
mBuilder.setTicker(conversation.getLatestMessage()
@@ -484,8 +487,7 @@ public class UIHelper {
long id = Long.parseLong(systemAccount[0]);
badge.assignContactUri(Contacts.getLookupUri(id, systemAccount[1]));
}
- badge.setImageBitmap(UIHelper.getContactPicture(contact, 72, context,
- false));
+ badge.setImageBitmap(contact.getImage(72, context));
}
public static AlertDialog getVerifyFingerprintDialog(
diff --git a/src/eu/siacs/conversations/utils/Validator.java b/src/eu/siacs/conversations/utils/Validator.java
index 9ca1e34f..a1a119e7 100644
--- a/src/eu/siacs/conversations/utils/Validator.java
+++ b/src/eu/siacs/conversations/utils/Validator.java
@@ -5,7 +5,7 @@ import java.util.regex.Pattern;
public class Validator {
public static final Pattern VALID_JID =
- Pattern.compile("\\b^[^@/<>'\"\\s]+@[^@/<>'\"\\s]+$", Pattern.CASE_INSENSITIVE);
+ Pattern.compile("^[^@/<>'\"\\s]+@[^@/<>'\"\\s]+$", Pattern.CASE_INSENSITIVE);
public static boolean isValidJid(String jid) {
Matcher matcher = VALID_JID.matcher(jid);
diff --git a/src/eu/siacs/conversations/xml/Element.java b/src/eu/siacs/conversations/xml/Element.java
index f8e070f7..3d22ba6a 100644
--- a/src/eu/siacs/conversations/xml/Element.java
+++ b/src/eu/siacs/conversations/xml/Element.java
@@ -144,4 +144,12 @@ public class Element {
public void clearChildren() {
this.children.clear();
}
+
+ public void setAttribute(String name, long value) {
+ this.setAttribute(name, ""+value);
+ }
+
+ public void setAttribute(String name, int value) {
+ this.setAttribute(name, ""+value);
+ }
}
diff --git a/src/eu/siacs/conversations/xml/TagWriter.java b/src/eu/siacs/conversations/xml/TagWriter.java
index 23a260f2..4828d5d9 100644
--- a/src/eu/siacs/conversations/xml/TagWriter.java
+++ b/src/eu/siacs/conversations/xml/TagWriter.java
@@ -41,12 +41,18 @@ public class TagWriter {
public TagWriter() {
}
- public void setOutputStream(OutputStream out) {
+ public void setOutputStream(OutputStream out) throws IOException {
+ if (out==null) {
+ throw new IOException();
+ }
this.plainOutputStream = out;
this.outputStream = new OutputStreamWriter(out);
}
- public OutputStream getOutputStream() {
+ public OutputStream getOutputStream() throws IOException {
+ if (this.plainOutputStream==null) {
+ throw new IOException();
+ }
return this.plainOutputStream;
}
diff --git a/src/eu/siacs/conversations/xml/XmlReader.java b/src/eu/siacs/conversations/xml/XmlReader.java
index 10b2fb06..1c7e94e6 100644
--- a/src/eu/siacs/conversations/xml/XmlReader.java
+++ b/src/eu/siacs/conversations/xml/XmlReader.java
@@ -28,24 +28,33 @@ public class XmlReader {
this.wakeLock = wakeLock;
}
- public void setInputStream(InputStream inputStream) {
+ public void setInputStream(InputStream inputStream) throws IOException {
+ if (inputStream==null) {
+ throw new IOException();
+ }
this.is = inputStream;
try {
parser.setInput(new InputStreamReader(this.is));
} catch (XmlPullParserException e) {
- Log.d(LOGTAG,"error setting input stream");
+ throw new IOException("error resetting parser");
}
}
- public InputStream getInputStream() {
+ public InputStream getInputStream() throws IOException {
+ if (this.is==null) {
+ throw new IOException();
+ }
return is;
}
- public void reset() {
+ public void reset() throws IOException {
+ if (this.is==null) {
+ throw new IOException();
+ }
try {
parser.setInput(new InputStreamReader(this.is));
} catch (XmlPullParserException e) {
- Log.d(LOGTAG,"error resetting input stream");
+ throw new IOException("error resetting parser");
}
}
@@ -54,7 +63,7 @@ public class XmlReader {
try { wakeLock.release();} catch (RuntimeException re) {}
}
try {
- while(parser.next() != XmlPullParser.END_DOCUMENT) {
+ while(this.is != null && parser.next() != XmlPullParser.END_DOCUMENT) {
wakeLock.acquire();
if (parser.getEventType() == XmlPullParser.START_TAG) {
Tag tag = Tag.start(parser.getName());
@@ -81,8 +90,6 @@ public class XmlReader {
throw new IOException("xml parser mishandled ArrayIndexOufOfBounds", e);
} catch (StringIndexOutOfBoundsException e) {
throw new IOException("xml parser mishandled StringIndexOufOfBounds", e);
- } catch (NullPointerException e) {
- throw new IOException("null pointer in xml parser");
}
return null;
}
diff --git a/src/eu/siacs/conversations/xmpp/XmppConnection.java b/src/eu/siacs/conversations/xmpp/XmppConnection.java
index 45bac2f6..ba7a9245 100644
--- a/src/eu/siacs/conversations/xmpp/XmppConnection.java
+++ b/src/eu/siacs/conversations/xmpp/XmppConnection.java
@@ -7,14 +7,8 @@ import java.math.BigInteger;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
-import java.security.cert.CertPathValidatorException;
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
@@ -25,16 +19,13 @@ import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.TrustManagerFactory;
+
import javax.net.ssl.X509TrustManager;
-import org.bouncycastle.pqc.math.linearalgebra.GoppaCode.MaMaPe;
import org.xmlpull.v1.XmlPullParserException;
import de.duenndns.ssl.MemorizingTrustManager;
-import android.content.Context;
import android.os.Bundle;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
@@ -73,6 +64,8 @@ public class XmppConnection implements Runnable {
private Socket socket;
private XmlReader tagReader;
private TagWriter tagWriter;
+
+ private Features features = new Features(this);
private boolean shouldBind = true;
private boolean shouldAuthenticate = true;
@@ -81,14 +74,16 @@ public class XmppConnection implements Runnable {
private String streamId = null;
private int smVersion = 3;
+
+ private boolean usingCompression = false;
private int stanzasReceived = 0;
private int stanzasSent = 0;
- public long lastPaketReceived = 0;
- public long lastPingSent = 0;
- public long lastConnect = 0;
- public long lastSessionStarted = 0;
+ private long lastPaketReceived = 0;
+ private long lastPingSent = 0;
+ private long lastConnect = 0;
+ private long lastSessionStarted = 0;
private int attempt = 0;
@@ -134,7 +129,9 @@ public class XmppConnection implements Runnable {
protected void connect() {
Log.d(LOGTAG, account.getJid() + ": connecting");
+ usingCompression = false;
lastConnect = SystemClock.elapsedRealtime();
+ lastPingSent = SystemClock.elapsedRealtime();
this.attempt++;
try {
shouldAuthenticate = shouldBind = !account
@@ -228,11 +225,6 @@ public class XmppConnection implements Runnable {
processStreamError(nextTag);
} else if (nextTag.isStart("features")) {
processStreamFeatures(nextTag);
- if ((streamFeatures.getChildren().size() == 1)
- && (streamFeatures.hasChild("starttls"))
- && (!account.isOptionSet(Account.OPTION_USETLS))) {
- changeStatus(Account.STATUS_SERVER_REQUIRES_TLS);
- }
} else if (nextTag.isStart("proceed")) {
switchOverToTls(nextTag);
} else if (nextTag.isStart("compressed")) {
@@ -422,7 +414,6 @@ public class XmppConnection implements Runnable {
throws XmlPullParserException, IOException,
NoSuchAlgorithmException {
tagReader.readTag(); // read tag close
-
tagWriter.setOutputStream(new ZLibOutputStream(tagWriter
.getOutputStream()));
tagReader
@@ -430,6 +421,7 @@ public class XmppConnection implements Runnable {
sendStartStream();
Log.d(LOGTAG, account.getJid() + ": compression enabled");
+ usingCompression = true;
processStream(tagReader.readTag());
}
@@ -609,25 +601,32 @@ public class XmppConnection implements Runnable {
this.sendUnboundIqPacket(iq, new OnIqPacketReceived() {
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
- String resource = packet.findChild("bind").findChild("jid")
- .getContent().split("/")[1];
- account.setResource(resource);
- if (streamFeatures.hasChild("sm", "urn:xmpp:sm:3")) {
- smVersion = 3;
- EnablePacket enable = new EnablePacket(smVersion);
- tagWriter.writeStanzaAsync(enable);
- } else if (streamFeatures.hasChild("sm", "urn:xmpp:sm:2")) {
- smVersion = 2;
- EnablePacket enable = new EnablePacket(smVersion);
- tagWriter.writeStanzaAsync(enable);
- }
- sendServiceDiscoveryInfo(account.getServer());
- sendServiceDiscoveryItems(account.getServer());
- if (bindListener != null) {
- bindListener.onBind(account);
+ Element bind = packet.findChild("bind");
+ if (bind!=null) {
+ Element jid = bind.findChild("jid");
+ if (jid!=null) {
+ account.setResource(jid.getContent().split("/")[1]);
+ if (streamFeatures.hasChild("sm", "urn:xmpp:sm:3")) {
+ smVersion = 3;
+ EnablePacket enable = new EnablePacket(smVersion);
+ tagWriter.writeStanzaAsync(enable);
+ } else if (streamFeatures.hasChild("sm", "urn:xmpp:sm:2")) {
+ smVersion = 2;
+ EnablePacket enable = new EnablePacket(smVersion);
+ tagWriter.writeStanzaAsync(enable);
+ }
+ sendServiceDiscoveryInfo(account.getServer());
+ sendServiceDiscoveryItems(account.getServer());
+ if (bindListener != null) {
+ bindListener.onBind(account);
+ }
+ changeStatus(Account.STATUS_ONLINE);
+ } else {
+ disconnect(true);
+ }
+ } else {
+ disconnect(true);
}
-
- changeStatus(Account.STATUS_ONLINE);
}
});
if (this.streamFeatures.hasChild("session")) {
@@ -664,7 +663,7 @@ public class XmppConnection implements Runnable {
}
private void enableAdvancedStreamFeatures() {
- if (hasFeaturesCarbon()) {
+ if (getFeatures().carbons()) {
sendEnableCarbons();
}
}
@@ -772,6 +771,7 @@ public class XmppConnection implements Runnable {
iq.addChild("ping", "urn:xmpp:ping");
this.sendIqPacket(iq, null);
}
+ this.lastPingSent = SystemClock.elapsedRealtime();
}
public void setOnMessagePacketReceivedListener(
@@ -835,33 +835,6 @@ public class XmppConnection implements Runnable {
}
}
- public boolean hasFeatureRosterManagment() {
- if (this.streamFeatures == null) {
- return false;
- } else {
- return this.streamFeatures.hasChild("ver");
- }
- }
-
- public boolean hasFeatureStreamManagment() {
- if (this.streamFeatures == null) {
- return false;
- } else {
- return this.streamFeatures.hasChild("sm");
- }
- }
-
- public boolean hasFeaturesCarbon() {
- return hasDiscoFeature(account.getServer(), "urn:xmpp:carbons:2");
- }
-
- public boolean hasDiscoFeature(String server, String feature) {
- if (!disco.containsKey(server)) {
- return false;
- }
- return disco.get(server).contains(feature);
- }
-
public List<String> findDiscoItemsByFeature(String feature) {
List<String> items = new ArrayList<String>();
for (Entry<String, List<String>> cursor : disco.entrySet()) {
@@ -883,15 +856,7 @@ public class XmppConnection implements Runnable {
public void r() {
this.tagWriter.writeStanzaAsync(new RequestPacket(smVersion));
}
-
- public int getReceivedStanzas() {
- return this.stanzasReceived;
- }
-
- public int getSentStanzas() {
- return this.stanzasSent;
- }
-
+
public String getMucServer() {
return findDiscoItemByFeature("http://jabber.org/protocol/muc");
}
@@ -905,4 +870,76 @@ public class XmppConnection implements Runnable {
public int getAttempt() {
return this.attempt;
}
+
+ public Features getFeatures() {
+ return this.features;
+ }
+
+ public class Features {
+ XmppConnection connection;
+ public Features(XmppConnection connection) {
+ this.connection = connection;
+ }
+
+ private boolean hasDiscoFeature(String server, String feature) {
+ if (!connection.disco.containsKey(server)) {
+ return false;
+ }
+ return connection.disco.get(server).contains(feature);
+ }
+
+ public boolean carbons() {
+ return hasDiscoFeature(account.getServer(), "urn:xmpp:carbons:2");
+ }
+
+ public boolean sm() {
+ if (connection.streamFeatures == null) {
+ return false;
+ } else {
+ return connection.streamFeatures.hasChild("sm");
+ }
+ }
+
+ public boolean pubsub() {
+ return hasDiscoFeature(account.getServer(), "http://jabber.org/protocol/pubsub#publish");
+ }
+
+ public boolean rosterVersioning() {
+ if (connection.streamFeatures == null) {
+ return false;
+ } else {
+ return connection.streamFeatures.hasChild("ver");
+ }
+ }
+
+ public boolean streamhost() {
+ return connection.findDiscoItemByFeature("http://jabber.org/protocol/bytestreams") != null;
+ }
+
+ public boolean compression() {
+ return connection.usingCompression;
+ }
+ }
+
+ public long getLastSessionEstablished() {
+ long diff;
+ if (this.lastSessionStarted == 0) {
+ diff = SystemClock.elapsedRealtime() - this.lastConnect;
+ } else {
+ diff = SystemClock.elapsedRealtime() - this.lastSessionStarted;
+ }
+ return System.currentTimeMillis() - diff;
+ }
+
+ public long getLastConnect() {
+ return this.lastConnect;
+ }
+
+ public long getLastPingSent() {
+ return this.lastPingSent;
+ }
+
+ public long getLastPacketReceived() {
+ return this.lastPaketReceived;
+ }
}
diff --git a/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java b/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
index f1a0373c..1f1dfb21 100644
--- a/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
+++ b/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
@@ -2,13 +2,15 @@ package eu.siacs.conversations.xmpp.jingle;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+import android.content.Intent;
import android.graphics.BitmapFactory;
+import android.net.Uri;
import android.util.Log;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Conversation;
@@ -23,12 +25,12 @@ import eu.siacs.conversations.xmpp.stanzas.IqPacket;
public class JingleConnection {
- private final String[] extensions = {"webp","jpeg","jpg","png"};
- private final String[] cryptoExtensions = {"pgp","gpg","otr"};
-
+ private final String[] extensions = { "webp", "jpeg", "jpg", "png" };
+ private final String[] cryptoExtensions = { "pgp", "gpg", "otr" };
+
private JingleConnectionManager mJingleConnectionManager;
private XmppConnectionService mXmppConnectionService;
-
+
public static final int STATUS_INITIATED = 0;
public static final int STATUS_ACCEPTED = 1;
public static final int STATUS_TERMINATED = 2;
@@ -36,9 +38,9 @@ public class JingleConnection {
public static final int STATUS_FINISHED = 4;
public static final int STATUS_TRANSMITTING = 5;
public static final int STATUS_FAILED = 99;
-
+
private int ibbBlockSize = 4096;
-
+
private int status = -1;
private Message message;
private String sessionId;
@@ -46,55 +48,65 @@ public class JingleConnection {
private String initiator;
private String responder;
private List<JingleCandidate> candidates = new ArrayList<JingleCandidate>();
- private HashMap<String, JingleSocks5Transport> connections = new HashMap<String, JingleSocks5Transport>();
-
+ private ConcurrentHashMap<String, JingleSocks5Transport> connections = new ConcurrentHashMap<String, JingleSocks5Transport>();
+
private String transportId;
private Element fileOffer;
private JingleFile file = null;
-
+
private String contentName;
private String contentCreator;
-
+
private boolean receivedCandidate = false;
private boolean sentCandidate = false;
-
+
private boolean acceptedAutomatically = false;
-
+
private JingleTransport transport = null;
-
+
private OnIqPacketReceived responseListener = new OnIqPacketReceived() {
-
+
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
if (packet.getType() == IqPacket.TYPE_ERROR) {
if (initiator.equals(account.getFullJid())) {
- mXmppConnectionService.markMessage(message, Message.STATUS_SEND_FAILED);
+ mXmppConnectionService.markMessage(message,
+ Message.STATUS_SEND_FAILED);
}
status = STATUS_FAILED;
}
}
};
-
+
final OnFileTransmissionStatusChanged onFileTransmissionSatusChanged = new OnFileTransmissionStatusChanged() {
-
+
@Override
public void onFileTransmitted(JingleFile file) {
if (responder.equals(account.getFullJid())) {
sendSuccess();
if (acceptedAutomatically) {
message.markUnread();
- JingleConnection.this.mXmppConnectionService.notifyUi(message.getConversation(), true);
+ JingleConnection.this.mXmppConnectionService.notifyUi(
+ message.getConversation(), true);
}
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
- BitmapFactory.decodeFile(file.getAbsolutePath(),options);
+ BitmapFactory.decodeFile(file.getAbsolutePath(), options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
- message.setBody(""+file.getSize()+","+imageWidth+","+imageHeight);
+ message.setBody("" + file.getSize() + "," + imageWidth + ","
+ + imageHeight);
mXmppConnectionService.databaseBackend.createMessage(message);
- mXmppConnectionService.markMessage(message, Message.STATUS_RECIEVED);
+ mXmppConnectionService.markMessage(message,
+ Message.STATUS_RECIEVED);
+ }
+ Log.d("xmppService",
+ "sucessfully transmitted file:" + file.getAbsolutePath());
+ if (message.getEncryption()!=Message.ENCRYPTION_PGP) {
+ Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
+ intent.setData(Uri.fromFile(file));
+ mXmppConnectionService.sendBroadcast(intent);
}
- Log.d("xmppService","sucessfully transmitted file:"+file.getAbsolutePath());
}
@Override
@@ -103,48 +115,49 @@ public class JingleConnection {
JingleConnection.this.cancel();
}
};
-
+
private OnProxyActivated onProxyActivated = new OnProxyActivated() {
-
+
@Override
public void success() {
if (initiator.equals(account.getFullJid())) {
- Log.d("xmppService","we were initiating. sending file");
- transport.send(file,onFileTransmissionSatusChanged);
+ Log.d("xmppService", "we were initiating. sending file");
+ transport.send(file, onFileTransmissionSatusChanged);
} else {
- transport.receive(file,onFileTransmissionSatusChanged);
- Log.d("xmppService","we were responding. receiving file");
+ transport.receive(file, onFileTransmissionSatusChanged);
+ Log.d("xmppService", "we were responding. receiving file");
}
}
-
+
@Override
public void failed() {
- Log.d("xmppService","proxy activation failed");
+ Log.d("xmppService", "proxy activation failed");
}
};
-
+
public JingleConnection(JingleConnectionManager mJingleConnectionManager) {
this.mJingleConnectionManager = mJingleConnectionManager;
- this.mXmppConnectionService = mJingleConnectionManager.getXmppConnectionService();
+ this.mXmppConnectionService = mJingleConnectionManager
+ .getXmppConnectionService();
}
-
+
public String getSessionId() {
return this.sessionId;
}
-
+
public String getAccountJid() {
return this.account.getFullJid();
}
-
+
public String getCounterPart() {
return this.message.getCounterpart();
}
-
+
public void deliverPacket(JinglePacket packet) {
boolean returnResult = true;
if (packet.isAction("session-terminate")) {
Reason reason = packet.getReason();
- if (reason!=null) {
+ if (reason != null) {
if (reason.hasChild("cancel")) {
this.cancel();
} else if (reason.hasChild("success")) {
@@ -164,24 +177,26 @@ public class JingleConnection {
returnResult = this.receiveFallbackToIbb(packet);
} else {
returnResult = false;
- Log.d("xmppService","trying to fallback to something unknown"+packet.toString());
+ Log.d("xmppService", "trying to fallback to something unknown"
+ + packet.toString());
}
} else if (packet.isAction("transport-accept")) {
returnResult = this.receiveTransportAccept(packet);
} else {
- Log.d("xmppService","packet arrived in connection. action was "+packet.getAction());
+ Log.d("xmppService", "packet arrived in connection. action was "
+ + packet.getAction());
returnResult = false;
}
IqPacket response;
if (returnResult) {
response = packet.generateRespone(IqPacket.TYPE_RESULT);
-
+
} else {
response = packet.generateRespone(IqPacket.TYPE_ERROR);
}
account.getXmppConnection().sendIqPacket(response, null);
}
-
+
public void init(Message message) {
this.contentCreator = "initiator";
this.contentName = this.mJingleConnectionManager.nextRandomId();
@@ -193,42 +208,52 @@ public class JingleConnection {
if (this.candidates.size() > 0) {
this.sendInitRequest();
} else {
- this.mJingleConnectionManager.getPrimaryCandidate(account, new OnPrimaryCandidateFound() {
-
- @Override
- public void onPrimaryCandidateFound(boolean success, final JingleCandidate candidate) {
- if (success) {
- final JingleSocks5Transport socksConnection = new JingleSocks5Transport(JingleConnection.this, candidate);
- connections.put(candidate.getCid(), socksConnection);
- socksConnection.connect(new OnTransportConnected() {
-
- @Override
- public void failed() {
- Log.d("xmppService","connection to our own primary candidete failed");
- sendInitRequest();
- }
-
- @Override
- public void established() {
- Log.d("xmppService","succesfully connected to our own primary candidate");
+ this.mJingleConnectionManager.getPrimaryCandidate(account,
+ new OnPrimaryCandidateFound() {
+
+ @Override
+ public void onPrimaryCandidateFound(boolean success,
+ final JingleCandidate candidate) {
+ if (success) {
+ final JingleSocks5Transport socksConnection = new JingleSocks5Transport(
+ JingleConnection.this, candidate);
+ connections.put(candidate.getCid(),
+ socksConnection);
+ socksConnection
+ .connect(new OnTransportConnected() {
+
+ @Override
+ public void failed() {
+ Log.d("xmppService",
+ "connection to our own primary candidete failed");
+ sendInitRequest();
+ }
+
+ @Override
+ public void established() {
+ Log.d("xmppService",
+ "succesfully connected to our own primary candidate");
+ mergeCandidate(candidate);
+ sendInitRequest();
+ }
+ });
mergeCandidate(candidate);
+ } else {
+ Log.d("xmppService",
+ "no primary candidate of our own was found");
sendInitRequest();
}
- });
- mergeCandidate(candidate);
- } else {
- Log.d("xmppService","no primary candidate of our own was found");
- sendInitRequest();
- }
- }
- });
+ }
+ });
}
-
+
}
-
+
public void init(Account account, JinglePacket packet) {
this.status = STATUS_INITIATED;
- Conversation conversation = this.mXmppConnectionService.findOrCreateConversation(account, packet.getFrom().split("/")[0], false);
+ Conversation conversation = this.mXmppConnectionService
+ .findOrCreateConversation(account,
+ packet.getFrom().split("/")[0], false);
this.message = new Message(conversation, "", Message.ENCRYPTION_NONE);
this.message.setType(Message.TYPE_IMAGE);
this.message.setStatus(Message.STATUS_RECEIVED_OFFER);
@@ -243,51 +268,67 @@ public class JingleConnection {
this.contentCreator = content.getAttribute("creator");
this.contentName = content.getAttribute("name");
this.transportId = content.getTransportId();
- this.mergeCandidates(JingleCandidate.parse(content.socks5transport().getChildren()));
+ this.mergeCandidates(JingleCandidate.parse(content.socks5transport()
+ .getChildren()));
this.fileOffer = packet.getJingleContent().getFileOffer();
- if (fileOffer!=null) {
+ if (fileOffer != null) {
Element fileSize = fileOffer.findChild("size");
Element fileNameElement = fileOffer.findChild("name");
- if (fileNameElement!=null) {
+ if (fileNameElement != null) {
boolean supportedFile = false;
- String[] filename = fileNameElement.getContent().toLowerCase(Locale.US).split("\\.");
- if (Arrays.asList(this.extensions).contains(filename[filename.length - 1])) {
+ String[] filename = fileNameElement.getContent()
+ .toLowerCase(Locale.US).split("\\.");
+ if (Arrays.asList(this.extensions).contains(
+ filename[filename.length - 1])) {
supportedFile = true;
- } else if (Arrays.asList(this.cryptoExtensions).contains(filename[filename.length - 1])) {
+ } else if (Arrays.asList(this.cryptoExtensions).contains(
+ filename[filename.length - 1])) {
if (filename.length == 3) {
- if (Arrays.asList(this.extensions).contains(filename[filename.length -2])) {
+ if (Arrays.asList(this.extensions).contains(
+ filename[filename.length - 2])) {
supportedFile = true;
if (filename[filename.length - 1].equals("otr")) {
- Log.d("xmppService","receiving otr file");
- this.message.setEncryption(Message.ENCRYPTION_OTR);
+ Log.d("xmppService", "receiving otr file");
+ this.message
+ .setEncryption(Message.ENCRYPTION_OTR);
} else {
- this.message.setEncryption(Message.ENCRYPTION_PGP);
+ this.message
+ .setEncryption(Message.ENCRYPTION_PGP);
}
}
}
}
if (supportedFile) {
long size = Long.parseLong(fileSize.getContent());
- message.setBody(""+size);
+ message.setBody("" + size);
conversation.getMessages().add(message);
- if (size<=this.mJingleConnectionManager.getAutoAcceptFileSize()) {
- Log.d("xmppService","auto accepting file from "+packet.getFrom());
+ if (size <= this.mJingleConnectionManager
+ .getAutoAcceptFileSize()) {
+ Log.d("xmppService", "auto accepting file from "
+ + packet.getFrom());
this.acceptedAutomatically = true;
this.sendAccept();
} else {
message.markUnread();
- Log.d("xmppService","not auto accepting new file offer with size: "+size+" allowed size:"+this.mJingleConnectionManager.getAutoAcceptFileSize());
- this.mXmppConnectionService.notifyUi(conversation, true);
+ Log.d("xmppService",
+ "not auto accepting new file offer with size: "
+ + size
+ + " allowed size:"
+ + this.mJingleConnectionManager
+ .getAutoAcceptFileSize());
+ this.mXmppConnectionService
+ .notifyUi(conversation, true);
}
- this.file = this.mXmppConnectionService.getFileBackend().getJingleFile(message,false);
+ this.file = this.mXmppConnectionService.getFileBackend()
+ .getJingleFile(message, false);
if (message.getEncryption() == Message.ENCRYPTION_OTR) {
byte[] key = conversation.getSymmetricKey();
- if (key==null) {
+ if (key == null) {
this.sendCancel();
this.cancel();
return;
} else {
- this.file.setKey(conversation.getSymmetricKey());
+ this.file.setKey(key);
}
}
this.file.setExpectedSize(size);
@@ -304,20 +345,21 @@ public class JingleConnection {
this.cancel();
}
}
-
+
private void sendInitRequest() {
JinglePacket packet = this.bootstrapPacket("session-initiate");
- Content content = new Content(this.contentCreator,this.contentName);
+ Content content = new Content(this.contentCreator, this.contentName);
if (message.getType() == Message.TYPE_IMAGE) {
content.setTransportId(this.transportId);
- this.file = this.mXmppConnectionService.getFileBackend().getJingleFile(message,false);
+ this.file = this.mXmppConnectionService.getFileBackend()
+ .getJingleFile(message, false);
if (message.getEncryption() == Message.ENCRYPTION_OTR) {
Conversation conversation = this.message.getConversation();
this.mXmppConnectionService.renewSymmetricKey(conversation);
content.setFileOffer(this.file, true);
this.file.setKey(conversation.getSymmetricKey());
} else {
- content.setFileOffer(this.file,false);
+ content.setFileOffer(this.file, false);
}
this.transportId = this.mJingleConnectionManager.nextRandomId();
content.setTransportId(this.transportId);
@@ -327,62 +369,72 @@ public class JingleConnection {
this.status = STATUS_INITIATED;
}
}
-
+
private List<Element> getCandidatesAsElements() {
List<Element> elements = new ArrayList<Element>();
- for(JingleCandidate c : this.candidates) {
+ for (JingleCandidate c : this.candidates) {
elements.add(c.toElement());
}
return elements;
}
-
+
private void sendAccept() {
status = STATUS_ACCEPTED;
mXmppConnectionService.markMessage(message, Message.STATUS_RECIEVING);
- this.mJingleConnectionManager.getPrimaryCandidate(this.account, new OnPrimaryCandidateFound() {
-
- @Override
- public void onPrimaryCandidateFound(boolean success,final JingleCandidate candidate) {
- final JinglePacket packet = bootstrapPacket("session-accept");
- final Content content = new Content(contentCreator,contentName);
- content.setFileOffer(fileOffer);
- content.setTransportId(transportId);
- if ((success)&&(!equalCandidateExists(candidate))) {
- final JingleSocks5Transport socksConnection = new JingleSocks5Transport(JingleConnection.this, candidate);
- connections.put(candidate.getCid(), socksConnection);
- socksConnection.connect(new OnTransportConnected() {
-
- @Override
- public void failed() {
- Log.d("xmppService","connection to our own primary candidate failed");
- content.socks5transport().setChildren(getCandidatesAsElements());
- packet.setContent(content);
- sendJinglePacket(packet);
- connectNextCandidate();
- }
-
- @Override
- public void established() {
- Log.d("xmppService","connected to primary candidate");
- mergeCandidate(candidate);
- content.socks5transport().setChildren(getCandidatesAsElements());
+ this.mJingleConnectionManager.getPrimaryCandidate(this.account,
+ new OnPrimaryCandidateFound() {
+
+ @Override
+ public void onPrimaryCandidateFound(boolean success,
+ final JingleCandidate candidate) {
+ final JinglePacket packet = bootstrapPacket("session-accept");
+ final Content content = new Content(contentCreator,
+ contentName);
+ content.setFileOffer(fileOffer);
+ content.setTransportId(transportId);
+ if ((success) && (!equalCandidateExists(candidate))) {
+ final JingleSocks5Transport socksConnection = new JingleSocks5Transport(
+ JingleConnection.this, candidate);
+ connections.put(candidate.getCid(), socksConnection);
+ socksConnection.connect(new OnTransportConnected() {
+
+ @Override
+ public void failed() {
+ Log.d("xmppService",
+ "connection to our own primary candidate failed");
+ content.socks5transport().setChildren(
+ getCandidatesAsElements());
+ packet.setContent(content);
+ sendJinglePacket(packet);
+ connectNextCandidate();
+ }
+
+ @Override
+ public void established() {
+ Log.d("xmppService",
+ "connected to primary candidate");
+ mergeCandidate(candidate);
+ content.socks5transport().setChildren(
+ getCandidatesAsElements());
+ packet.setContent(content);
+ sendJinglePacket(packet);
+ connectNextCandidate();
+ }
+ });
+ } else {
+ Log.d("xmppService",
+ "did not find a primary candidate for ourself");
+ content.socks5transport().setChildren(
+ getCandidatesAsElements());
packet.setContent(content);
sendJinglePacket(packet);
connectNextCandidate();
}
- });
- } else {
- Log.d("xmppService","did not find a primary candidate for ourself");
- content.socks5transport().setChildren(getCandidatesAsElements());
- packet.setContent(content);
- sendJinglePacket(packet);
- connectNextCandidate();
- }
- }
- });
-
+ }
+ });
+
}
-
+
private JinglePacket bootstrapPacket(String action) {
JinglePacket packet = new JinglePacket();
packet.setAction(action);
@@ -392,15 +444,16 @@ public class JingleConnection {
packet.setInitiator(this.initiator);
return packet;
}
-
+
private void sendJinglePacket(JinglePacket packet) {
- //Log.d("xmppService",packet.toString());
- account.getXmppConnection().sendIqPacket(packet,responseListener);
+ // Log.d("xmppService",packet.toString());
+ account.getXmppConnection().sendIqPacket(packet, responseListener);
}
-
+
private boolean receiveAccept(JinglePacket packet) {
Content content = packet.getJingleContent();
- mergeCandidates(JingleCandidate.parse(content.socks5transport().getChildren()));
+ mergeCandidates(JingleCandidate.parse(content.socks5transport()
+ .getChildren()));
this.status = STATUS_ACCEPTED;
mXmppConnectionService.markMessage(message, Message.STATUS_UNSEND);
this.connectNextCandidate();
@@ -411,16 +464,20 @@ public class JingleConnection {
Content content = packet.getJingleContent();
if (content.hasSocks5Transport()) {
if (content.socks5transport().hasChild("activated")) {
- if ((this.transport!=null)&&(this.transport instanceof JingleSocks5Transport)) {
+ if ((this.transport != null)
+ && (this.transport instanceof JingleSocks5Transport)) {
onProxyActivated.success();
} else {
- String cid = content.socks5transport().findChild("activated").getAttribute("cid");
- Log.d("xmppService","received proxy activated ("+cid+")prior to choosing our own transport");
- JingleSocks5Transport connection = this.connections.get(cid);
- if (connection!=null) {
+ String cid = content.socks5transport()
+ .findChild("activated").getAttribute("cid");
+ Log.d("xmppService", "received proxy activated (" + cid
+ + ")prior to choosing our own transport");
+ JingleSocks5Transport connection = this.connections
+ .get(cid);
+ if (connection != null) {
connection.setActivated(true);
} else {
- Log.d("xmppService","activated connection not found");
+ Log.d("xmppService", "activated connection not found");
this.sendCancel();
this.cancel();
}
@@ -430,23 +487,25 @@ public class JingleConnection {
onProxyActivated.failed();
return true;
} else if (content.socks5transport().hasChild("candidate-error")) {
- Log.d("xmppService","received candidate error");
+ Log.d("xmppService", "received candidate error");
this.receivedCandidate = true;
- if ((status == STATUS_ACCEPTED)&&(this.sentCandidate)) {
+ if ((status == STATUS_ACCEPTED) && (this.sentCandidate)) {
this.connect();
}
return true;
- } else if (content.socks5transport().hasChild("candidate-used")){
- String cid = content.socks5transport().findChild("candidate-used").getAttribute("cid");
- if (cid!=null) {
- Log.d("xmppService","candidate used by counterpart:"+cid);
+ } else if (content.socks5transport().hasChild("candidate-used")) {
+ String cid = content.socks5transport()
+ .findChild("candidate-used").getAttribute("cid");
+ if (cid != null) {
+ Log.d("xmppService", "candidate used by counterpart:" + cid);
JingleCandidate candidate = getCandidate(cid);
candidate.flagAsUsedByCounterpart();
this.receivedCandidate = true;
- if ((status == STATUS_ACCEPTED)&&(this.sentCandidate)) {
+ if ((status == STATUS_ACCEPTED) && (this.sentCandidate)) {
this.connect();
} else {
- Log.d("xmppService","ignoring because file is already in transmission or we havent sent our candidate yet");
+ Log.d("xmppService",
+ "ignoring because file is already in transmission or we havent sent our candidate yet");
}
return true;
} else {
@@ -463,8 +522,8 @@ public class JingleConnection {
private void connect() {
final JingleSocks5Transport connection = chooseConnection();
this.transport = connection;
- if (connection==null) {
- Log.d("xmppService","could not find suitable candidate");
+ if (connection == null) {
+ Log.d("xmppService", "could not find suitable candidate");
this.disconnect();
if (this.initiator.equals(account.getFullJid())) {
this.sendFallbackToIbb();
@@ -473,65 +532,80 @@ public class JingleConnection {
this.status = STATUS_TRANSMITTING;
if (connection.needsActivation()) {
if (connection.getCandidate().isOurs()) {
- Log.d("xmppService","candidate "+connection.getCandidate().getCid()+" was our proxy. going to activate");
+ Log.d("xmppService", "candidate "
+ + connection.getCandidate().getCid()
+ + " was our proxy. going to activate");
IqPacket activation = new IqPacket(IqPacket.TYPE_SET);
activation.setTo(connection.getCandidate().getJid());
- activation.query("http://jabber.org/protocol/bytestreams").setAttribute("sid", this.getSessionId());
- activation.query().addChild("activate").setContent(this.getCounterPart());
- this.account.getXmppConnection().sendIqPacket(activation, new OnIqPacketReceived() {
-
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- if (packet.getType()==IqPacket.TYPE_ERROR) {
- onProxyActivated.failed();
- } else {
- onProxyActivated.success();
- sendProxyActivated(connection.getCandidate().getCid());
- }
- }
- });
+ activation.query("http://jabber.org/protocol/bytestreams")
+ .setAttribute("sid", this.getSessionId());
+ activation.query().addChild("activate")
+ .setContent(this.getCounterPart());
+ this.account.getXmppConnection().sendIqPacket(activation,
+ new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(Account account,
+ IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE_ERROR) {
+ onProxyActivated.failed();
+ } else {
+ onProxyActivated.success();
+ sendProxyActivated(connection
+ .getCandidate().getCid());
+ }
+ }
+ });
} else {
- Log.d("xmppService","candidate "+connection.getCandidate().getCid()+" was a proxy. waiting for other party to activate");
+ Log.d("xmppService",
+ "candidate "
+ + connection.getCandidate().getCid()
+ + " was a proxy. waiting for other party to activate");
}
} else {
if (initiator.equals(account.getFullJid())) {
- Log.d("xmppService","we were initiating. sending file");
- connection.send(file,onFileTransmissionSatusChanged);
+ Log.d("xmppService", "we were initiating. sending file");
+ connection.send(file, onFileTransmissionSatusChanged);
} else {
- Log.d("xmppService","we were responding. receiving file");
- connection.receive(file,onFileTransmissionSatusChanged);
+ Log.d("xmppService", "we were responding. receiving file");
+ connection.receive(file, onFileTransmissionSatusChanged);
}
}
}
}
-
+
private JingleSocks5Transport chooseConnection() {
JingleSocks5Transport connection = null;
- for (Entry<String, JingleSocks5Transport> cursor : connections.entrySet()) {
- JingleSocks5Transport currentConnection = cursor.getValue();
- //Log.d("xmppService","comparing candidate: "+currentConnection.getCandidate().toString());
- if (currentConnection.isEstablished()&&(currentConnection.getCandidate().isUsedByCounterpart()||(!currentConnection.getCandidate().isOurs()))) {
- //Log.d("xmppService","is usable");
- if (connection==null) {
- connection = currentConnection;
- } else {
- if (connection.getCandidate().getPriority()<currentConnection.getCandidate().getPriority()) {
- connection = currentConnection;
- } else if (connection.getCandidate().getPriority()==currentConnection.getCandidate().getPriority()) {
- //Log.d("xmppService","found two candidates with same priority");
- if (initiator.equals(account.getFullJid())) {
- if (currentConnection.getCandidate().isOurs()) {
- connection = currentConnection;
- }
- } else {
- if (!currentConnection.getCandidate().isOurs()) {
- connection = currentConnection;
- }
- }
- }
- }
- }
- }
+ for (Entry<String, JingleSocks5Transport> cursor : connections
+ .entrySet()) {
+ JingleSocks5Transport currentConnection = cursor.getValue();
+ // Log.d("xmppService","comparing candidate: "+currentConnection.getCandidate().toString());
+ if (currentConnection.isEstablished()
+ && (currentConnection.getCandidate().isUsedByCounterpart() || (!currentConnection
+ .getCandidate().isOurs()))) {
+ // Log.d("xmppService","is usable");
+ if (connection == null) {
+ connection = currentConnection;
+ } else {
+ if (connection.getCandidate().getPriority() < currentConnection
+ .getCandidate().getPriority()) {
+ connection = currentConnection;
+ } else if (connection.getCandidate().getPriority() == currentConnection
+ .getCandidate().getPriority()) {
+ // Log.d("xmppService","found two candidates with same priority");
+ if (initiator.equals(account.getFullJid())) {
+ if (currentConnection.getCandidate().isOurs()) {
+ connection = currentConnection;
+ }
+ } else {
+ if (!currentConnection.getCandidate().isOurs()) {
+ connection = currentConnection;
+ }
+ }
+ }
+ }
+ }
+ }
return connection;
}
@@ -543,60 +617,68 @@ public class JingleConnection {
this.sendJinglePacket(packet);
this.disconnect();
this.status = STATUS_FINISHED;
- this.mXmppConnectionService.markMessage(this.message, Message.STATUS_RECIEVED);
+ this.mXmppConnectionService.markMessage(this.message,
+ Message.STATUS_RECIEVED);
this.mJingleConnectionManager.finishConnection(this);
}
-
+
private void sendFallbackToIbb() {
JinglePacket packet = this.bootstrapPacket("transport-replace");
- Content content = new Content(this.contentCreator,this.contentName);
+ Content content = new Content(this.contentCreator, this.contentName);
this.transportId = this.mJingleConnectionManager.nextRandomId();
content.setTransportId(this.transportId);
- content.ibbTransport().setAttribute("block-size",""+this.ibbBlockSize);
+ content.ibbTransport().setAttribute("block-size",
+ "" + this.ibbBlockSize);
packet.setContent(content);
this.sendJinglePacket(packet);
}
-
+
private boolean receiveFallbackToIbb(JinglePacket packet) {
- String receivedBlockSize = packet.getJingleContent().ibbTransport().getAttribute("block-size");
- if (receivedBlockSize!=null) {
+ String receivedBlockSize = packet.getJingleContent().ibbTransport()
+ .getAttribute("block-size");
+ if (receivedBlockSize != null) {
int bs = Integer.parseInt(receivedBlockSize);
- if (bs>this.ibbBlockSize) {
+ if (bs > this.ibbBlockSize) {
this.ibbBlockSize = bs;
}
}
this.transportId = packet.getJingleContent().getTransportId();
- this.transport = new JingleInbandTransport(this.account,this.responder,this.transportId,this.ibbBlockSize);
+ this.transport = new JingleInbandTransport(this.account,
+ this.responder, this.transportId, this.ibbBlockSize);
this.transport.receive(file, onFileTransmissionSatusChanged);
JinglePacket answer = bootstrapPacket("transport-accept");
Content content = new Content("initiator", "a-file-offer");
content.setTransportId(this.transportId);
- content.ibbTransport().setAttribute("block-size", ""+this.ibbBlockSize);
+ content.ibbTransport().setAttribute("block-size",
+ "" + this.ibbBlockSize);
answer.setContent(content);
this.sendJinglePacket(answer);
return true;
}
-
+
private boolean receiveTransportAccept(JinglePacket packet) {
if (packet.getJingleContent().hasIbbTransport()) {
- String receivedBlockSize = packet.getJingleContent().ibbTransport().getAttribute("block-size");
- if (receivedBlockSize!=null) {
+ String receivedBlockSize = packet.getJingleContent().ibbTransport()
+ .getAttribute("block-size");
+ if (receivedBlockSize != null) {
int bs = Integer.parseInt(receivedBlockSize);
- if (bs>this.ibbBlockSize) {
+ if (bs > this.ibbBlockSize) {
this.ibbBlockSize = bs;
}
}
- this.transport = new JingleInbandTransport(this.account,this.responder,this.transportId,this.ibbBlockSize);
+ this.transport = new JingleInbandTransport(this.account,
+ this.responder, this.transportId, this.ibbBlockSize);
this.transport.connect(new OnTransportConnected() {
-
+
@Override
public void failed() {
- Log.d("xmppService","ibb open failed");
+ Log.d("xmppService", "ibb open failed");
}
-
+
@Override
public void established() {
- JingleConnection.this.transport.send(file, onFileTransmissionSatusChanged);
+ JingleConnection.this.transport.send(file,
+ onFileTransmissionSatusChanged);
}
});
return true;
@@ -604,31 +686,35 @@ public class JingleConnection {
return false;
}
}
-
+
private void receiveSuccess() {
this.status = STATUS_FINISHED;
- this.mXmppConnectionService.markMessage(this.message, Message.STATUS_SEND);
+ this.mXmppConnectionService.markMessage(this.message,
+ Message.STATUS_SEND);
this.disconnect();
this.mJingleConnectionManager.finishConnection(this);
}
-
+
public void cancel() {
+ this.status = STATUS_CANCELED;
this.disconnect();
- if (this.message!=null) {
+ if (this.message != null) {
if (this.responder.equals(account.getFullJid())) {
- this.mXmppConnectionService.markMessage(this.message, Message.STATUS_RECEPTION_FAILED);
+ this.mXmppConnectionService.markMessage(this.message,
+ Message.STATUS_RECEPTION_FAILED);
} else {
if (this.status == STATUS_INITIATED) {
- this.mXmppConnectionService.markMessage(this.message, Message.STATUS_SEND_REJECTED);
+ this.mXmppConnectionService.markMessage(this.message,
+ Message.STATUS_SEND_REJECTED);
} else {
- this.mXmppConnectionService.markMessage(this.message, Message.STATUS_SEND_FAILED);
+ this.mXmppConnectionService.markMessage(this.message,
+ Message.STATUS_SEND_FAILED);
}
}
}
- this.status = STATUS_CANCELED;
this.mJingleConnectionManager.finishConnection(this);
}
-
+
private void sendCancel() {
JinglePacket packet = bootstrapPacket("session-terminate");
Reason reason = new Reason();
@@ -638,73 +724,82 @@ public class JingleConnection {
}
private void connectNextCandidate() {
- for(JingleCandidate candidate : this.candidates) {
- if ((!connections.containsKey(candidate.getCid())&&(!candidate.isOurs()))) {
+ for (JingleCandidate candidate : this.candidates) {
+ if ((!connections.containsKey(candidate.getCid()) && (!candidate
+ .isOurs()))) {
this.connectWithCandidate(candidate);
return;
}
}
this.sendCandidateError();
}
-
+
private void connectWithCandidate(final JingleCandidate candidate) {
- final JingleSocks5Transport socksConnection = new JingleSocks5Transport(this,candidate);
+ final JingleSocks5Transport socksConnection = new JingleSocks5Transport(
+ this, candidate);
connections.put(candidate.getCid(), socksConnection);
socksConnection.connect(new OnTransportConnected() {
-
+
@Override
public void failed() {
- Log.d("xmppService", "connection failed with "+candidate.getHost()+":"+candidate.getPort());
+ Log.d("xmppService",
+ "connection failed with " + candidate.getHost() + ":"
+ + candidate.getPort());
connectNextCandidate();
}
-
+
@Override
public void established() {
- Log.d("xmppService", "established connection with "+candidate.getHost()+":"+candidate.getPort());
+ Log.d("xmppService",
+ "established connection with " + candidate.getHost()
+ + ":" + candidate.getPort());
sendCandidateUsed(candidate.getCid());
}
});
}
private void disconnect() {
- Iterator<Entry<String, JingleSocks5Transport>> it = this.connections.entrySet().iterator();
- while (it.hasNext()) {
- Entry<String, JingleSocks5Transport> pairs = it.next();
- pairs.getValue().disconnect();
- it.remove();
- }
- }
-
+ Iterator<Entry<String, JingleSocks5Transport>> it = this.connections
+ .entrySet().iterator();
+ while (it.hasNext()) {
+ Entry<String, JingleSocks5Transport> pairs = it.next();
+ pairs.getValue().disconnect();
+ it.remove();
+ }
+ }
+
private void sendProxyActivated(String cid) {
JinglePacket packet = bootstrapPacket("transport-info");
- Content content = new Content(this.contentCreator,this.contentName);
+ Content content = new Content(this.contentCreator, this.contentName);
content.setTransportId(this.transportId);
- content.socks5transport().addChild("activated").setAttribute("cid", cid);
+ content.socks5transport().addChild("activated")
+ .setAttribute("cid", cid);
packet.setContent(content);
this.sendJinglePacket(packet);
}
-
+
private void sendCandidateUsed(final String cid) {
JinglePacket packet = bootstrapPacket("transport-info");
- Content content = new Content(this.contentCreator,this.contentName);
+ Content content = new Content(this.contentCreator, this.contentName);
content.setTransportId(this.transportId);
- content.socks5transport().addChild("candidate-used").setAttribute("cid", cid);
+ content.socks5transport().addChild("candidate-used")
+ .setAttribute("cid", cid);
packet.setContent(content);
this.sentCandidate = true;
- if ((receivedCandidate)&&(status == STATUS_ACCEPTED)) {
+ if ((receivedCandidate) && (status == STATUS_ACCEPTED)) {
connect();
}
this.sendJinglePacket(packet);
}
-
+
private void sendCandidateError() {
JinglePacket packet = bootstrapPacket("transport-info");
- Content content = new Content(this.contentCreator,this.contentName);
+ Content content = new Content(this.contentCreator, this.contentName);
content.setTransportId(this.transportId);
content.socks5transport().addChild("candidate-error");
packet.setContent(content);
this.sentCandidate = true;
- if ((receivedCandidate)&&(status == STATUS_ACCEPTED)) {
+ if ((receivedCandidate) && (status == STATUS_ACCEPTED)) {
connect();
}
this.sendJinglePacket(packet);
@@ -713,72 +808,73 @@ public class JingleConnection {
public String getInitiator() {
return this.initiator;
}
-
+
public String getResponder() {
return this.responder;
}
-
+
public int getStatus() {
return this.status;
}
-
+
private boolean equalCandidateExists(JingleCandidate candidate) {
- for(JingleCandidate c : this.candidates) {
+ for (JingleCandidate c : this.candidates) {
if (c.equalValues(candidate)) {
return true;
}
}
return false;
}
-
+
private void mergeCandidate(JingleCandidate candidate) {
- for(JingleCandidate c : this.candidates) {
+ for (JingleCandidate c : this.candidates) {
if (c.equals(candidate)) {
return;
}
}
this.candidates.add(candidate);
}
-
+
private void mergeCandidates(List<JingleCandidate> candidates) {
- for(JingleCandidate c : candidates) {
+ for (JingleCandidate c : candidates) {
mergeCandidate(c);
}
}
-
+
private JingleCandidate getCandidate(String cid) {
- for(JingleCandidate c : this.candidates) {
+ for (JingleCandidate c : this.candidates) {
if (c.getCid().equals(cid)) {
return c;
}
}
return null;
}
-
+
interface OnProxyActivated {
public void success();
+
public void failed();
}
public boolean hasTransportId(String sid) {
return sid.equals(this.transportId);
}
-
+
public JingleTransport getTransport() {
return this.transport;
}
public void accept() {
- if (status==STATUS_INITIATED) {
+ if (status == STATUS_INITIATED) {
new Thread(new Runnable() {
-
+
@Override
public void run() {
sendAccept();
}
}).start();
} else {
- Log.d("xmppService","status ("+status+") was not ok");
+ Log.d("xmppService", "status (" + status + ") was not ok");
}
}
}
diff --git a/src/eu/siacs/conversations/xmpp/pep/Avatar.java b/src/eu/siacs/conversations/xmpp/pep/Avatar.java
new file mode 100644
index 00000000..6d5c1431
--- /dev/null
+++ b/src/eu/siacs/conversations/xmpp/pep/Avatar.java
@@ -0,0 +1,68 @@
+package eu.siacs.conversations.xmpp.pep;
+
+import eu.siacs.conversations.xml.Element;
+import android.util.Base64;
+
+public class Avatar {
+ public String type;
+ public String sha1sum;
+ public String image;
+ public int height;
+ public int width;
+ public long size;
+ public String owner;
+ public byte[] getImageAsBytes() {
+ return Base64.decode(image, Base64.DEFAULT);
+ }
+ public String getFilename() {
+ if (type==null) {
+ return sha1sum;
+ } else if (type.equalsIgnoreCase("image/webp")) {
+ return sha1sum+".webp";
+ } else if (type.equalsIgnoreCase("image/png")) {
+ return sha1sum+".png";
+ } else {
+ return sha1sum;
+ }
+ }
+
+ public static Avatar parseMetadata(Element items) {
+ Element item = items.findChild("item");
+ if (item==null) {
+ return null;
+ }
+ Element metadata = item.findChild("metadata");
+ if (metadata==null) {
+ return null;
+ }
+ String primaryId = item.getAttribute("id");
+ if (primaryId==null) {
+ return null;
+ }
+ for(Element child : metadata.getChildren()) {
+ if (child.getName().equals("info") && primaryId.equals(child.getAttribute("id"))) {
+ Avatar avatar = new Avatar();
+ String height = child.getAttribute("height");
+ String width = child.getAttribute("width");
+ String size = child.getAttribute("bytes");
+ try {
+ if (height!=null) {
+ avatar.height = Integer.parseInt(height);
+ }
+ if (width!=null) {
+ avatar.width = Integer.parseInt(width);
+ }
+ if (size!=null) {
+ avatar.size = Long.parseLong(size);
+ }
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ avatar.type = child.getAttribute("type");
+ avatar.sha1sum = child.getAttribute("id");
+ return avatar;
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java b/src/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java
index 64a9edc3..433b08c9 100644
--- a/src/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java
+++ b/src/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java
@@ -8,6 +8,7 @@ public class MessagePacket extends AbstractStanza {
public static final int TYPE_NORMAL = 2;
public static final int TYPE_GROUPCHAT = 3;
public static final int TYPE_ERROR = 4;
+ public static final int TYPE_HEADLINE = 5;
public MessagePacket() {
super("message");
@@ -59,6 +60,8 @@ public class MessagePacket extends AbstractStanza {
return TYPE_GROUPCHAT;
} else if (type.equals("error")) {
return TYPE_ERROR;
+ } else if (type.equals("headline")) {
+ return TYPE_HEADLINE;
} else {
return TYPE_UNKNOWN;
}