diff options
Diffstat (limited to 'src/eu')
26 files changed, 910 insertions, 1064 deletions
diff --git a/src/eu/siacs/conversations/crypto/PgpEngine.java b/src/eu/siacs/conversations/crypto/PgpEngine.java index 48750e24..0f2aeff4 100644 --- a/src/eu/siacs/conversations/crypto/PgpEngine.java +++ b/src/eu/siacs/conversations/crypto/PgpEngine.java @@ -221,7 +221,6 @@ public class PgpEngine { return 0; } case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: - Log.d("xmppService","openpgp user interaction requeried"); return 0; case OpenPgpApi.RESULT_CODE_ERROR: Log.d("xmppService","openpgp error: "+((OpenPgpError) result diff --git a/src/eu/siacs/conversations/entities/Account.java b/src/eu/siacs/conversations/entities/Account.java index 35870aaa..b9c87eac 100644 --- a/src/eu/siacs/conversations/entities/Account.java +++ b/src/eu/siacs/conversations/entities/Account.java @@ -65,6 +65,8 @@ public class Account extends AbstractEntity{ private String otrFingerprint; + private Roster roster = null; + public Account() { this.uuid = "0"; } @@ -135,7 +137,7 @@ public class Account extends AbstractEntity{ } public boolean hasErrorStatus() { - return getStatus() > STATUS_NO_INTERNET; + return getStatus() > STATUS_NO_INTERNET && (getXmppConnection().getAttempt() >= 2); } public void setResource(String resource) { @@ -287,4 +289,11 @@ public class Account extends AbstractEntity{ return null; } } + + public Roster getRoster() { + if (this.roster==null) { + this.roster = new Roster(this); + } + return this.roster; + } } diff --git a/src/eu/siacs/conversations/entities/Contact.java b/src/eu/siacs/conversations/entities/Contact.java index 599aa8de..22e2661b 100644 --- a/src/eu/siacs/conversations/entities/Contact.java +++ b/src/eu/siacs/conversations/entities/Contact.java @@ -1,36 +1,30 @@ package eu.siacs.conversations.entities; -import java.io.Serializable; import java.util.HashSet; import java.util.Hashtable; import java.util.Set; - import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; - import eu.siacs.conversations.xml.Element; - import android.content.ContentValues; import android.database.Cursor; -import android.util.Log; - -public class Contact extends AbstractEntity implements Serializable { - private static final long serialVersionUID = -4570817093119419962L; +public class Contact { public static final String TABLENAME = "contacts"; - public static final String DISPLAYNAME = "name"; + public static final String SYSTEMNAME = "systemname"; + public static final String SERVERNAME = "servername"; public static final String JID = "jid"; - public static final String SUBSCRIPTION = "subscription"; + public static final String OPTIONS = "options"; public static final String SYSTEMACCOUNT = "systemaccount"; public static final String PHOTOURI = "photouri"; public static final String KEYS = "pgpkey"; - public static final String PRESENCES = "presences"; public static final String ACCOUNT = "accountUuid"; protected String accountUuid; - protected String displayName; + protected String systemName; + protected String serverName; protected String jid; protected int subscription = 0; protected String systemAccount; @@ -42,26 +36,12 @@ public class Contact extends AbstractEntity implements Serializable { protected boolean inRoster = true; - public Contact(Account account, String displayName, String jid, - String photoUri) { - if (account == null) { - this.accountUuid = null; - } else { - this.accountUuid = account.getUuid(); - } - this.account = account; - this.displayName = displayName; - this.jid = jid; - this.photoUri = photoUri; - this.uuid = java.util.UUID.randomUUID().toString(); - } - - public Contact(String uuid, String account, String displayName, String jid, - int subscription, String photoUri, String systemAccount, - String keys, String presences) { - this.uuid = uuid; + public Contact(String account, String systemName, + String serverName, String jid, int subscription, String photoUri, + String systemAccount, String keys) { this.accountUuid = account; - this.displayName = displayName; + this.systemName = systemName; + this.serverName = serverName; this.jid = jid; this.subscription = subscription; this.photoUri = photoUri; @@ -74,11 +54,20 @@ public class Contact extends AbstractEntity implements Serializable { } catch (JSONException e) { this.keys = new JSONObject(); } - this.presences = Presences.fromJsonString(presences); + } + + public Contact(String jid) { + this.jid = jid; } public String getDisplayName() { - return this.displayName; + if (this.systemName != null) { + return this.systemName; + } else if (this.serverName != null) { + return this.serverName; + } else { + return this.jid.split("@")[0]; + } } public String getProfilePhoto() { @@ -90,35 +79,32 @@ public class Contact extends AbstractEntity implements Serializable { } public boolean match(String needle) { - return (jid.toLowerCase().contains(needle.toLowerCase()) || (displayName + return (jid.toLowerCase().contains(needle.toLowerCase()) || (getDisplayName() .toLowerCase().contains(needle.toLowerCase()))); } - @Override public ContentValues getContentValues() { ContentValues values = new ContentValues(); - values.put(UUID, uuid); values.put(ACCOUNT, accountUuid); - values.put(DISPLAYNAME, displayName); + values.put(SYSTEMNAME, systemName); + values.put(SERVERNAME, serverName); values.put(JID, jid); - values.put(SUBSCRIPTION, subscription); + values.put(OPTIONS, subscription); values.put(SYSTEMACCOUNT, systemAccount); values.put(PHOTOURI, photoUri); values.put(KEYS, keys.toString()); - values.put(PRESENCES, presences.toJsonString()); return values; } public static Contact fromCursor(Cursor cursor) { - return new Contact(cursor.getString(cursor.getColumnIndex(UUID)), - cursor.getString(cursor.getColumnIndex(ACCOUNT)), - cursor.getString(cursor.getColumnIndex(DISPLAYNAME)), + return new Contact(cursor.getString(cursor.getColumnIndex(ACCOUNT)), + cursor.getString(cursor.getColumnIndex(SYSTEMNAME)), + cursor.getString(cursor.getColumnIndex(SERVERNAME)), cursor.getString(cursor.getColumnIndex(JID)), - cursor.getInt(cursor.getColumnIndex(SUBSCRIPTION)), + cursor.getInt(cursor.getColumnIndex(OPTIONS)), cursor.getString(cursor.getColumnIndex(PHOTOURI)), cursor.getString(cursor.getColumnIndex(SYSTEMACCOUNT)), - cursor.getString(cursor.getColumnIndex(KEYS)), - cursor.getString(cursor.getColumnIndex(PRESENCES))); + cursor.getString(cursor.getColumnIndex(KEYS))); } public int getSubscription() { @@ -138,10 +124,6 @@ public class Contact extends AbstractEntity implements Serializable { return this.account; } - public void setUuid(String uuid) { - this.uuid = uuid; - } - public boolean couldBeMuc() { String[] split = this.getJid().split("@"); if (split.length != 2) { @@ -154,8 +136,8 @@ public class Contact extends AbstractEntity implements Serializable { return (domainParts[0].equals("conf") || domainParts[0].equals("conference") || domainParts[0].equals("muc") - || domainParts[0].equals("sala") - || domainParts[0].equals("salas")); + || domainParts[0].equals("sala") || domainParts[0] + .equals("salas")); } } } @@ -188,8 +170,12 @@ public class Contact extends AbstractEntity implements Serializable { this.photoUri = uri; } - public void setDisplayName(String name) { - this.displayName = name; + public void setServerName(String serverName) { + this.serverName = serverName; + } + + public void setSystemName(String systemName) { + this.systemName = systemName; } public String getSystemAccount() { @@ -249,15 +235,15 @@ public class Contact extends AbstractEntity implements Serializable { } } - public void setSubscriptionOption(int option) { + public void setOption(int option) { this.subscription |= 1 << option; } - public void resetSubscriptionOption(int option) { + public void resetOption(int option) { this.subscription &= ~(1 << option); } - public boolean getSubscriptionOption(int option) { + public boolean getOption(int option) { return ((this.subscription & (1 << option)) != 0); } @@ -267,40 +253,39 @@ public class Contact extends AbstractEntity implements Serializable { if (subscription != null) { if (subscription.equals("to")) { - this.resetSubscriptionOption(Contact.Subscription.FROM); - this.setSubscriptionOption(Contact.Subscription.TO); + this.resetOption(Contact.Options.FROM); + this.setOption(Contact.Options.TO); } else if (subscription.equals("from")) { - this.resetSubscriptionOption(Contact.Subscription.TO); - this.setSubscriptionOption(Contact.Subscription.FROM); + this.resetOption(Contact.Options.TO); + this.setOption(Contact.Options.FROM); } else if (subscription.equals("both")) { - this.setSubscriptionOption(Contact.Subscription.TO); - this.setSubscriptionOption(Contact.Subscription.FROM); + this.setOption(Contact.Options.TO); + this.setOption(Contact.Options.FROM); } } if ((ask != null) && (ask.equals("subscribe"))) { - this.setSubscriptionOption(Contact.Subscription.ASKING); + this.setOption(Contact.Options.ASKING); } else { - this.resetSubscriptionOption(Contact.Subscription.ASKING); + this.resetOption(Contact.Options.ASKING); + } + } + + public Element asElement() { + Element item = new Element("item"); + item.setAttribute("jid", this.jid); + if (this.serverName != null) { + item.setAttribute("name", this.serverName); } + return item; } - public class Subscription { + public class Options { public static final int TO = 0; public static final int FROM = 1; public static final int ASKING = 2; public static final int PREEMPTIVE_GRANT = 4; - } - - public void flagAsNotInRoster() { - this.inRoster = false; - } - - public boolean isInRoster() { - return this.inRoster; - } - - public String getAccountUuid() { - return this.accountUuid; + public static final int IN_ROSTER = 8; + public static final int PENDING_SUBSCRIPTION_REQUEST = 16; } } diff --git a/src/eu/siacs/conversations/entities/Conversation.java b/src/eu/siacs/conversations/entities/Conversation.java index 5674f84a..e61537da 100644 --- a/src/eu/siacs/conversations/entities/Conversation.java +++ b/src/eu/siacs/conversations/entities/Conversation.java @@ -49,7 +49,6 @@ public class Conversation extends AbstractEntity { private transient List<Message> messages = null; private transient Account account = null; - private transient Contact contact; private transient SessionImpl otrSession; @@ -129,19 +128,13 @@ public class Conversation extends AbstractEntity { public String getName(boolean useSubject) { if ((getMode() == MODE_MULTI) && (getMucOptions().getSubject() != null) && useSubject) { return getMucOptions().getSubject(); - } else if (this.contact != null) { - return this.contact.getDisplayName(); } else { - return this.name; + return this.getContact().getDisplayName(); } } public String getProfilePhotoString() { - if (this.contact == null) { - return null; - } else { - return this.contact.getProfilePhoto(); - } + return this.getContact().getProfilePhoto(); } public String getAccountUuid() { @@ -153,14 +146,7 @@ public class Conversation extends AbstractEntity { } public Contact getContact() { - return this.contact; - } - - public void setContact(Contact contact) { - this.contact = contact; - if (contact != null) { - this.contactUuid = contact.getUuid(); - } + return this.account.getRoster().getContact(this.contactJid); } public void setAccount(Account account) { @@ -269,11 +255,15 @@ public class Conversation extends AbstractEntity { return false; } else { String foreignPresence = this.otrSession.getSessionID().getUserID(); - if (!getContact().getPresences().containsKey(foreignPresence)) { - this.resetOtrSession(); - return false; + if (getContact()==null) { + return true; + } else { + if (!getContact().getPresences().containsKey(foreignPresence)) { + this.resetOtrSession(); + return false; + } + return true; } - return true; } } @@ -341,7 +331,11 @@ public class Conversation extends AbstractEntity { } public String getNextMessage() { - return this.nextMessage; + if (this.nextMessage==null) { + return ""; + } else { + return this.nextMessage; + } } public void setNextMessage(String message) { diff --git a/src/eu/siacs/conversations/entities/Message.java b/src/eu/siacs/conversations/entities/Message.java index 33f7a8d4..950e349e 100644 --- a/src/eu/siacs/conversations/entities/Message.java +++ b/src/eu/siacs/conversations/entities/Message.java @@ -29,6 +29,7 @@ public class Message extends AbstractEntity { public static final int TYPE_TEXT = 0; public static final int TYPE_IMAGE = 1; + public static final int TYPE_AUDIO = 2; public static String CONVERSATION = "conversationUuid"; public static String COUNTERPART = "counterpart"; diff --git a/src/eu/siacs/conversations/entities/Presences.java b/src/eu/siacs/conversations/entities/Presences.java index acbaafca..fd8af573 100644 --- a/src/eu/siacs/conversations/entities/Presences.java +++ b/src/eu/siacs/conversations/entities/Presences.java @@ -4,10 +4,6 @@ import java.util.Hashtable; import java.util.Iterator; import java.util.Map.Entry; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - import eu.siacs.conversations.xml.Element; public class Presences { @@ -47,39 +43,6 @@ public class Presences { return status; } - public String toJsonString() { - JSONArray json = new JSONArray(); - Iterator<Entry<String, Integer>> it = presences.entrySet().iterator(); - - while (it.hasNext()) { - Entry<String, Integer> entry = it.next(); - JSONObject jObj = new JSONObject(); - try { - jObj.put("resource", entry.getKey()); - jObj.put("status", entry.getValue()); - } catch (JSONException e) { - - } - json.put(jObj); - } - return json.toString(); - } - - public static Presences fromJsonString(String jsonString) { - Presences presences = new Presences(); - try { - JSONArray json = new JSONArray(jsonString); - for (int i = 0; i < json.length(); ++i) { - JSONObject jObj = json.getJSONObject(i); - presences.updatePresence(jObj.getString("resource"), - jObj.getInt("status")); - } - } catch (JSONException e1) { - - } - return presences; - } - public static int parseShow(Element show) { if (show == null) { return Presences.ONLINE; diff --git a/src/eu/siacs/conversations/entities/Roster.java b/src/eu/siacs/conversations/entities/Roster.java new file mode 100644 index 00000000..7c18d80a --- /dev/null +++ b/src/eu/siacs/conversations/entities/Roster.java @@ -0,0 +1,63 @@ +package eu.siacs.conversations.entities; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class Roster { + Account account; + HashMap<String, Contact> contacts = new HashMap<String, Contact>(); + private String version = null; + + public Roster(Account account) { + this.account = account; + } + + public boolean hasContact(String jid) { + String cleanJid = jid.split("/")[0]; + return contacts.containsKey(cleanJid); + } + + public Contact getContact(String jid) { + String cleanJid = jid.split("/")[0]; + if (contacts.containsKey(cleanJid)) { + return contacts.get(cleanJid); + } else { + Contact contact = new Contact(cleanJid); + contact.setAccount(account); + contacts.put(cleanJid, contact); + return contact; + } + } + + public void clearPresences() { + // TODO Auto-generated method stub + + } + + public void markAllAsNotInRoster() { + + } + + public List<Contact> getContacts() { + return new ArrayList<Contact>(this.contacts.values()); + } + + public void initContact(Contact contact) { + contact.setAccount(account); + contact.setOption(Contact.Options.IN_ROSTER); + contacts.put(contact.getJid(),contact); + } + + public void setVersion(String version) { + this.version = version; + } + + public String getVersion() { + return this.version; + } + + public Account getAccount() { + return this.account; + } +} diff --git a/src/eu/siacs/conversations/utils/MessageParser.java b/src/eu/siacs/conversations/parser/MessageParser.java index 52b18f66..3733767f 100644 --- a/src/eu/siacs/conversations/utils/MessageParser.java +++ b/src/eu/siacs/conversations/parser/MessageParser.java @@ -1,4 +1,4 @@ -package eu.siacs.conversations.utils; +package eu.siacs.conversations.parser; import java.util.List; @@ -15,29 +15,34 @@ import eu.siacs.conversations.xmpp.stanzas.MessagePacket; public class MessageParser { protected static final String LOGTAG = "xmppService"; + private XmppConnectionService mXmppConnectionService; - public static Message parsePlainTextChat(MessagePacket packet, Account account, XmppConnectionService service) { + public MessageParser(XmppConnectionService service) { + this.mXmppConnectionService = service; + } + + public Message parsePlainTextChat(MessagePacket packet, Account account) { String[] fromParts = packet.getFrom().split("/"); - Conversation conversation = service.findOrCreateConversation(account, fromParts[0],false); + Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, fromParts[0],false); String body = packet.getBody(); return new Message(conversation, packet.getFrom(), body, Message.ENCRYPTION_NONE, Message.STATUS_RECIEVED); } - public static Message parsePgpChat(String pgpBody, MessagePacket packet, Account account, XmppConnectionService service) { + public Message parsePgpChat(String pgpBody, MessagePacket packet, Account account) { String[] fromParts = packet.getFrom().split("/"); - Conversation conversation = service.findOrCreateConversation(account, fromParts[0],false); + Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, fromParts[0],false); return new Message(conversation, packet.getFrom(), pgpBody, Message.ENCRYPTION_PGP, Message.STATUS_RECIEVED); } - public static Message parseOtrChat(MessagePacket packet, Account account, XmppConnectionService service) { + public Message parseOtrChat(MessagePacket packet, Account account) { boolean properlyAddressed = (packet.getTo().split("/").length == 2) || (account.countPresences() == 1); String[] fromParts = packet.getFrom().split("/"); - Conversation conversation = service.findOrCreateConversation(account, fromParts[0],false); + Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, fromParts[0],false); String body = packet.getBody(); if (!conversation.hasValidOtrSession()) { if (properlyAddressed) { Log.d("xmppService","starting new otr session with "+packet.getFrom()+" because no valid otr session has been found"); - conversation.startOtrSession(service.getApplicationContext(), fromParts[1],false); + conversation.startOtrSession(mXmppConnectionService.getApplicationContext(), fromParts[1],false); } else { Log.d("xmppService",account.getJid()+": ignoring otr session with "+fromParts[0]); return null; @@ -48,7 +53,7 @@ public class MessageParser { conversation.resetOtrSession(); if (properlyAddressed) { Log.d("xmppService","replacing otr session with "+packet.getFrom()); - conversation.startOtrSession(service.getApplicationContext(), fromParts[1],false); + conversation.startOtrSession(mXmppConnectionService.getApplicationContext(), fromParts[1],false); } else { return null; } @@ -69,15 +74,15 @@ public class MessageParser { Message msg = messages.get(i); if ((msg.getStatus() == Message.STATUS_UNSEND) && (msg.getEncryption() == Message.ENCRYPTION_OTR)) { - MessagePacket outPacket = service.prepareMessagePacket( + MessagePacket outPacket = mXmppConnectionService.prepareMessagePacket( account, msg, otrSession); msg.setStatus(Message.STATUS_SEND); - service.databaseBackend.updateMessage(msg); + mXmppConnectionService.databaseBackend.updateMessage(msg); account.getXmppConnection() .sendMessagePacket(outPacket); } } - service.updateUi(conversation, false); + mXmppConnectionService.updateUi(conversation, false); } else if ((before != after) && (after == SessionStatus.FINISHED)) { conversation.resetOtrSession(); Log.d(LOGTAG,"otr session stoped"); @@ -93,13 +98,13 @@ public class MessageParser { } } - public static Message parseGroupchat(MessagePacket packet, Account account, XmppConnectionService service) { + public Message parseGroupchat(MessagePacket packet, Account account) { int status; String[] fromParts = packet.getFrom().split("/"); - Conversation conversation = service.findOrCreateConversation(account, fromParts[0],true); + Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, fromParts[0],true); if (packet.hasChild("subject")) { conversation.getMucOptions().setSubject(packet.findChild("subject").getContent()); - service.updateUi(conversation, false); + mXmppConnectionService.updateUi(conversation, false); return null; } if ((fromParts.length == 1)) { @@ -107,15 +112,18 @@ public class MessageParser { } String counterPart = fromParts[1]; if (counterPart.equals(conversation.getMucOptions().getNick())) { - status = Message.STATUS_SEND; + if (mXmppConnectionService.markMessage(conversation, packet.getId(), Message.STATUS_SEND)) { + return null; + } else { + status = Message.STATUS_SEND; + } } else { status = Message.STATUS_RECIEVED; } return new Message(conversation, counterPart, packet.getBody(), Message.ENCRYPTION_NONE, status); } - public static Message parseCarbonMessage(MessagePacket packet, - Account account, XmppConnectionService service) { + public Message parseCarbonMessage(MessagePacket packet,Account account) { int status; String fullJid; Element forwarded; @@ -142,16 +150,16 @@ public class MessageParser { fullJid = message.getAttribute("to"); } String[] parts = fullJid.split("/"); - Conversation conversation = service.findOrCreateConversation(account, parts[0],false); + Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, parts[0],false); return new Message(conversation,fullJid, message.findChild("body").getContent(), Message.ENCRYPTION_NONE,status); } - public static void parseError(MessagePacket packet, Account account, XmppConnectionService service) { + public void parseError(MessagePacket packet, Account account) { String[] fromParts = packet.getFrom().split("/"); - service.markMessage(account, fromParts[0], packet.getId(), Message.STATUS_SEND_FAILED); + mXmppConnectionService.markMessage(account, fromParts[0], packet.getId(), Message.STATUS_SEND_FAILED); } - public static String getPgpBody(MessagePacket packet) { + public String getPgpBody(MessagePacket packet) { for(Element child : packet.getChildren()) { if (child.getName().equals("x")&&child.getAttribute("xmlns").equals("jabber:x:encrypted")) { return child.getContent(); diff --git a/src/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/eu/siacs/conversations/persistance/DatabaseBackend.java index 5a34dac6..771027f2 100644 --- a/src/eu/siacs/conversations/persistance/DatabaseBackend.java +++ b/src/eu/siacs/conversations/persistance/DatabaseBackend.java @@ -2,20 +2,17 @@ package eu.siacs.conversations.persistance; import java.util.ArrayList; import java.util.List; -import java.util.UUID; import java.util.concurrent.CopyOnWriteArrayList; 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.entities.Presences; -import android.content.ContentValues; +import eu.siacs.conversations.entities.Roster; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; -import android.os.Bundle; import android.util.Log; public class DatabaseBackend extends SQLiteOpenHelper { @@ -23,10 +20,19 @@ public class DatabaseBackend extends SQLiteOpenHelper { private static DatabaseBackend instance = null; private static final String DATABASE_NAME = "history"; - private static final int DATABASE_VERSION = 3; + private static final int DATABASE_VERSION = 5; + + 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);"; public DatabaseBackend(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); + Log.d("xmppService",CREATE_CONTATCS_STATEMENT); } @Override @@ -36,7 +42,7 @@ 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.KEYS + " TEXT)"); db.execSQL("create table " + Conversation.TABLENAME + " (" + Conversation.UUID + " TEXT PRIMARY KEY, " + Conversation.NAME + " TEXT, " + Conversation.CONTACT + " TEXT, " @@ -50,31 +56,28 @@ public class DatabaseBackend extends SQLiteOpenHelper { + " 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);"); - db.execSQL("create table " + Contact.TABLENAME + "(" + Contact.UUID - + " TEXT PRIMARY KEY, " + Contact.ACCOUNT + " TEXT, " - + Contact.DISPLAYNAME + " TEXT," + Contact.JID + " TEXT," - + Contact.PRESENCES + " TEXT, " + Contact.KEYS - + " TEXT," + Contact.PHOTOURI + " TEXT," + Contact.SUBSCRIPTION - + " NUMBER," + Contact.SYSTEMACCOUNT + " NUMBER, " - + "FOREIGN KEY(" + Contact.ACCOUNT + ") REFERENCES " - + Account.TABLENAME + "(" + Account.UUID - + ") ON DELETE CASCADE);"); + + " NUMBER, " + Message.STATUS + " NUMBER," + Message.TYPE + + " NUMBER, FOREIGN KEY(" + Message.CONVERSATION + + ") REFERENCES " + Conversation.TABLENAME + "(" + + Conversation.UUID + ") ON DELETE CASCADE);"); + + db.execSQL(CREATE_CONTATCS_STATEMENT); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (oldVersion < 2 && newVersion >= 2) { - // enable compression by default. - db.execSQL("update " + Account.TABLENAME - + " set " + Account.OPTIONS + " = " + Account.OPTIONS + " | 8"); + db.execSQL("update " + Account.TABLENAME + " set " + + Account.OPTIONS + " = " + Account.OPTIONS + " | 8"); } if (oldVersion < 3 && newVersion >= 3) { - //add field type to message - db.execSQL("ALTER TABLE "+Message.TABLENAME+" ADD COLUMN "+Message.TYPE+" NUMBER");; + db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + + Message.TYPE + " NUMBER"); + } + if (oldVersion < 5 && newVersion >= 5) { + db.execSQL("DROP TABLE "+Contact.TABLENAME); + db.execSQL(CREATE_CONTATCS_STATEMENT); + db.execSQL("UPDATE "+Account.TABLENAME+ " SET "+Account.ROSTERVERSION+" = NULL"); } } @@ -99,7 +102,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { SQLiteDatabase db = this.getWritableDatabase(); db.insert(Account.TABLENAME, null, account.getContentValues()); } - + public void createContact(Contact contact) { SQLiteDatabase db = this.getWritableDatabase(); db.insert(Contact.TABLENAME, null, contact.getContentValues()); @@ -145,10 +148,10 @@ public class DatabaseBackend extends SQLiteOpenHelper { public Conversation findConversation(Account account, String contactJid) { SQLiteDatabase db = this.getReadableDatabase(); - String[] selectionArgs = { account.getUuid(), contactJid+"%" }; + String[] selectionArgs = { account.getUuid(), contactJid + "%" }; Cursor cursor = db.query(Conversation.TABLENAME, null, - Conversation.ACCOUNT + "=? AND " + Conversation.CONTACTJID + " like ?", - selectionArgs, null, null, null); + Conversation.ACCOUNT + "=? AND " + Conversation.CONTACTJID + + " like ?", selectionArgs, null, null, null); if (cursor.getCount() == 0) return null; cursor.moveToFirst(); @@ -200,87 +203,32 @@ public class DatabaseBackend extends SQLiteOpenHelper { db.update(Message.TABLENAME, message.getContentValues(), Message.UUID + "=?", args); } - - public void updateContact(Contact contact, boolean updatePresences) { - SQLiteDatabase db = this.getWritableDatabase(); - String[] args = { contact.getUuid() }; - ContentValues values = contact.getContentValues(); - if (!updatePresences) { - values.remove(Contact.PRESENCES); - } else { - values.remove(Contact.DISPLAYNAME); - values.remove(Contact.PHOTOURI); - values.remove(Contact.SYSTEMACCOUNT); - } - db.update(Contact.TABLENAME, contact.getContentValues(), Contact.UUID - + "=?", args); - } - - public void clearPresences(Account account) { - SQLiteDatabase db = this.getWritableDatabase(); - String[] args = { account.getUuid() }; - ContentValues values = new ContentValues(); - values.put(Contact.PRESENCES,"[]"); - db.update(Contact.TABLENAME, values, Contact.ACCOUNT - + "=?", args); - } - - public void mergeContacts(List<Contact> contacts) { - SQLiteDatabase db = this.getWritableDatabase(); - for (int i = 0; i < contacts.size(); i++) { - Contact contact = contacts.get(i); - String[] columns = {Contact.UUID, Contact.PRESENCES}; - String[] args = {contact.getAccount().getUuid(), contact.getJid()}; - Cursor cursor = db.query(Contact.TABLENAME, columns,Contact.ACCOUNT+"=? AND "+Contact.JID+"=?", args, null, null, null); - if (cursor.getCount()>=1) { - cursor.moveToFirst(); - contact.setUuid(cursor.getString(0)); - updateContact(contact,false); - } else { - contact.setUuid(UUID.randomUUID().toString()); - createContact(contact); - } - } - } - public List<Contact> getContactsByAccount(Account account) { - List<Contact> list = new ArrayList<Contact>(); + public void readRoster(Roster roster) { SQLiteDatabase db = this.getReadableDatabase(); Cursor cursor; - if (account==null) { - cursor = db.query(Contact.TABLENAME, null, null, null, null, - null, null); - } else { - String args[] = {account.getUuid()}; - cursor = db.query(Contact.TABLENAME, null, Contact.ACCOUNT+"=?", args, null, - null, null); - } + String args[] = { roster.getAccount().getUuid() }; + cursor = db.query(Contact.TABLENAME, null, Contact.ACCOUNT + "=?", + args, null, null, null); while (cursor.moveToNext()) { - list.add(Contact.fromCursor(cursor)); + roster.initContact(Contact.fromCursor(cursor)); } - return list; } - public List<Contact> getContacts(String where) { - List<Contact> list = new ArrayList<Contact>(); - SQLiteDatabase db = this.getReadableDatabase(); - Cursor cursor = db.query(Contact.TABLENAME, null, where, null, null, null, null); - while (cursor.moveToNext()) { - list.add(Contact.fromCursor(cursor)); + public void writeRoster(Roster roster) { + Account account = roster.getAccount(); + SQLiteDatabase db = this.getWritableDatabase(); + 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()}; + db.delete(Contact.TABLENAME, where, whereArgs); + } } - return list; - } - - public Contact findContact(Account account, String jid) { - SQLiteDatabase db = this.getReadableDatabase(); - String[] selectionArgs = { account.getUuid(), jid }; - Cursor cursor = db.query(Contact.TABLENAME, null, - Contact.ACCOUNT + "=? AND " + Contact.JID + "=?", - selectionArgs, null, null, null); - if (cursor.getCount() == 0) - return null; - cursor.moveToFirst(); - return Contact.fromCursor(cursor); + account.setRosterVersion(roster.getVersion()); + updateAccount(account); } public void deleteMessage(Message message) { @@ -288,34 +236,18 @@ public class DatabaseBackend extends SQLiteOpenHelper { String[] args = { message.getUuid() }; db.delete(Message.TABLENAME, Message.UUID + "=?", args); } - + public void deleteMessagesInConversation(Conversation conversation) { SQLiteDatabase db = this.getWritableDatabase(); String[] args = { conversation.getUuid() }; db.delete(Message.TABLENAME, Message.CONVERSATION + "=?", args); } - public void deleteContact(Contact contact) { - SQLiteDatabase db = this.getWritableDatabase(); - String[] args = { contact.getUuid() }; - db.delete(Contact.TABLENAME, Contact.UUID + "=?", args); - } - - public Contact getContact(String uuid) { - SQLiteDatabase db = this.getWritableDatabase(); - String[] args = { uuid }; - Cursor cursor = db.query(Contact.TABLENAME, null, Contact.UUID + "=?", args, null, null, null); - if (cursor.getCount() == 0) { - return null; - } - cursor.moveToFirst(); - return Contact.fromCursor(cursor); - } - public Conversation findConversationByUuid(String conversationUuid) { SQLiteDatabase db = this.getReadableDatabase(); String[] selectionArgs = { conversationUuid }; - Cursor cursor = db.query(Conversation.TABLENAME, null, Conversation.UUID + "=?", selectionArgs, null, null, null); + Cursor cursor = db.query(Conversation.TABLENAME, null, + Conversation.UUID + "=?", selectionArgs, null, null, null); if (cursor.getCount() == 0) { return null; } @@ -326,18 +258,20 @@ public class DatabaseBackend extends SQLiteOpenHelper { public Message findMessageByUuid(String messageUuid) { SQLiteDatabase db = this.getReadableDatabase(); String[] selectionArgs = { messageUuid }; - Cursor cursor = db.query(Message.TABLENAME, null, Message.UUID + "=?", selectionArgs, null, null, null); + Cursor cursor = db.query(Message.TABLENAME, null, Message.UUID + "=?", + selectionArgs, null, null, null); if (cursor.getCount() == 0) { return null; } cursor.moveToFirst(); return Message.fromCursor(cursor); } - + public Account findAccountByUuid(String accountUuid) { SQLiteDatabase db = this.getReadableDatabase(); String[] selectionArgs = { accountUuid }; - Cursor cursor = db.query(Account.TABLENAME, null, Account.UUID + "=?", selectionArgs, null, null, null); + Cursor cursor = db.query(Account.TABLENAME, null, Account.UUID + "=?", + selectionArgs, null, null, null); if (cursor.getCount() == 0) { return null; } diff --git a/src/eu/siacs/conversations/persistance/FileBackend.java b/src/eu/siacs/conversations/persistance/FileBackend.java index becb1ee3..3a580048 100644 --- a/src/eu/siacs/conversations/persistance/FileBackend.java +++ b/src/eu/siacs/conversations/persistance/FileBackend.java @@ -1,12 +1,12 @@ package eu.siacs.conversations.persistance; 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.lang.ref.WeakReference; import android.content.Context; import android.graphics.Bitmap; @@ -14,14 +14,9 @@ import android.graphics.BitmapFactory; import android.net.Uri; import android.util.Log; import android.util.LruCache; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; - -import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; -import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.xmpp.jingle.JingleFile; public class FileBackend { @@ -43,11 +38,11 @@ public class FileBackend { }; } - + public LruCache<String, Bitmap> getThumbnailCache() { return thumbnailCache; } - + public JingleFile getJingleFile(Message message) { return getJingleFile(message, true); } @@ -58,7 +53,7 @@ public class FileBackend { String path = prefix + "/" + conversation.getAccount().getJid() + "/" + conversation.getContactJid(); String filename; - if ((decrypted)||(message.getEncryption() == Message.ENCRYPTION_NONE)) { + if ((decrypted) || (message.getEncryption() == Message.ENCRYPTION_NONE)) { filename = message.getUuid() + ".webp"; } else { filename = message.getUuid() + ".webp.pgp"; @@ -87,36 +82,48 @@ public class FileBackend { } } - public JingleFile copyImageToPrivateStorage(Message message, Uri image) { + public JingleFile copyImageToPrivateStorage(Message message, Uri image) + throws ImageCopyException { try { - Log.d("xmppService","copying file: "+image.toString()+ " to internal storage"); - InputStream is = context.getContentResolver() - .openInputStream(image); + InputStream is; + if (image != null) { + is = context.getContentResolver().openInputStream(image); + } else { + is = new FileInputStream(getIncomingFile()); + } JingleFile file = getJingleFile(message); file.getParentFile().mkdirs(); file.createNewFile(); OutputStream os = new FileOutputStream(file); Bitmap originalBitmap = BitmapFactory.decodeStream(is); + if (originalBitmap == null) { + os.close(); + throw new ImageCopyException(R.string.error_not_an_image_file); + } is.close(); + if (image == null) { + getIncomingFile().delete(); + } Bitmap scalledBitmap = resize(originalBitmap, IMAGE_SIZE); boolean success = scalledBitmap.compress( Bitmap.CompressFormat.WEBP, 75, os); if (!success) { - return null; + throw new ImageCopyException(R.string.error_compressing_image); } os.flush(); os.close(); long size = file.getSize(); int width = scalledBitmap.getWidth(); int height = scalledBitmap.getHeight(); - message.setBody(""+size+","+width+","+height); + message.setBody("" + size + "," + width + "," + height); return file; } catch (FileNotFoundException e) { - return null; + throw new ImageCopyException(R.string.error_file_not_found); } catch (IOException e) { - return null; + throw new ImageCopyException(R.string.error_io_exception); } catch (SecurityException e) { - return null; + throw new ImageCopyException( + R.string.error_security_exception_during_image_copy); } } @@ -128,7 +135,7 @@ public class FileBackend { public Bitmap getThumbnail(Message message, int size, boolean cacheOnly) throws FileNotFoundException { Bitmap thumbnail = thumbnailCache.get(message.getUuid()); - if ((thumbnail == null)&&(!cacheOnly)) { + if ((thumbnail == null) && (!cacheOnly)) { Bitmap fullsize = BitmapFactory.decodeFile(getJingleFile(message) .getAbsolutePath()); if (fullsize == null) { @@ -160,4 +167,21 @@ public class FileBackend { } f.delete(); } + + public File getIncomingFile() { + return new File(context.getFilesDir().getAbsolutePath() + "/incoming"); + } + + public class ImageCopyException extends Exception { + private static final long serialVersionUID = -1010013599132881427L; + private int resId; + + public ImageCopyException(int resId) { + this.resId = resId; + } + + public int getResId() { + return resId; + } + } } diff --git a/src/eu/siacs/conversations/services/ImageProvider.java b/src/eu/siacs/conversations/services/ImageProvider.java index c1bae661..798a4e25 100644 --- a/src/eu/siacs/conversations/services/ImageProvider.java +++ b/src/eu/siacs/conversations/services/ImageProvider.java @@ -2,13 +2,13 @@ package eu.siacs.conversations.services; import java.io.File; import java.io.FileNotFoundException; +import java.io.IOException; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.persistance.DatabaseBackend; import eu.siacs.conversations.persistance.FileBackend; - import android.content.ContentProvider; import android.content.ContentValues; import android.database.Cursor; @@ -21,46 +21,60 @@ public class ImageProvider extends ContentProvider { @Override public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { - DatabaseBackend databaseBackend = DatabaseBackend - .getInstance(getContext()); + ParcelFileDescriptor pfd; FileBackend fileBackend = new FileBackend(getContext()); - String uuids = uri.getPath(); - Log.d("xmppService", "uuids = " + uuids); - if (uuids == null) { - throw new FileNotFoundException(); - } - String[] uuidsSplited = uuids.split("/"); - if (uuidsSplited.length != 3) { + if ("r".equals(mode)) { + DatabaseBackend databaseBackend = DatabaseBackend + .getInstance(getContext()); + String uuids = uri.getPath(); + Log.d("xmppService", "uuids = " + uuids+" mode="+mode); + if (uuids == null) { + throw new FileNotFoundException(); + } + String[] uuidsSplited = uuids.split("/"); + if (uuidsSplited.length != 3) { + throw new FileNotFoundException(); + } + String conversationUuid = uuidsSplited[1]; + String messageUuid = uuidsSplited[2]; + + Conversation conversation = databaseBackend + .findConversationByUuid(conversationUuid); + if (conversation == null) { + throw new FileNotFoundException("conversation " + conversationUuid + + " could not be found"); + } + Message message = databaseBackend.findMessageByUuid(messageUuid); + if (message == null) { + throw new FileNotFoundException("message " + messageUuid + + " could not be found"); + } + + Account account = databaseBackend.findAccountByUuid(conversation + .getAccountUuid()); + if (account == null) { + throw new FileNotFoundException("account " + + conversation.getAccountUuid() + " cound not be found"); + } + message.setConversation(conversation); + conversation.setAccount(account); + + File file = fileBackend.getJingleFile(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(); } - String conversationUuid = uuidsSplited[1]; - String messageUuid = uuidsSplited[2]; - - Conversation conversation = databaseBackend - .findConversationByUuid(conversationUuid); - if (conversation == null) { - throw new FileNotFoundException("conversation " + conversationUuid - + " could not be found"); - } - Message message = databaseBackend.findMessageByUuid(messageUuid); - if (message == null) { - throw new FileNotFoundException("message " + messageUuid - + " could not be found"); - } - - Account account = databaseBackend.findAccountByUuid(conversation - .getAccountUuid()); - if (account == null) { - throw new FileNotFoundException("account " - + conversation.getAccountUuid() + " cound not be found"); - } - message.setConversation(conversation); - conversation.setAccount(account); - - File file = fileBackend.getJingleFile(message); - ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file, - ParcelFileDescriptor.MODE_READ_ONLY); - return pfd; } @Override @@ -93,5 +107,16 @@ public class ImageProvider extends ContentProvider { public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) { return 0; } + + public static Uri getContentUri(Message message) { + return Uri + .parse("content://eu.siacs.conversations.images/" + + message.getConversationUuid() + + "/" + + message.getUuid()); + } -} + 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 d2742997..9ea7f2b1 100644 --- a/src/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/eu/siacs/conversations/services/XmppConnectionService.java @@ -10,14 +10,12 @@ import java.util.List; import java.util.Locale; import java.util.Random; -import org.openintents.openpgp.OpenPgpError; import org.openintents.openpgp.util.OpenPgpApi; import org.openintents.openpgp.util.OpenPgpServiceConnection; 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.Contact; @@ -26,15 +24,13 @@ import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.MucOptions; import eu.siacs.conversations.entities.MucOptions.OnRenameListener; import eu.siacs.conversations.entities.Presences; +import eu.siacs.conversations.parser.MessageParser; import eu.siacs.conversations.persistance.DatabaseBackend; import eu.siacs.conversations.persistance.FileBackend; -import eu.siacs.conversations.persistance.OnPhoneContactsMerged; import eu.siacs.conversations.ui.OnAccountListChangedListener; import eu.siacs.conversations.ui.OnConversationListChangedListener; -import eu.siacs.conversations.ui.OnRosterFetchedListener; import eu.siacs.conversations.ui.UiCallback; import eu.siacs.conversations.utils.ExceptionHelper; -import eu.siacs.conversations.utils.MessageParser; import eu.siacs.conversations.utils.OnPhoneContactsLoadedListener; import eu.siacs.conversations.utils.PhoneHelper; import eu.siacs.conversations.utils.UIHelper; @@ -47,7 +43,6 @@ import eu.siacs.conversations.xmpp.OnStatusChanged; import eu.siacs.conversations.xmpp.OnTLSExceptionReceived; import eu.siacs.conversations.xmpp.XmppConnection; import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager; -import eu.siacs.conversations.xmpp.jingle.JingleFile; import eu.siacs.conversations.xmpp.jingle.OnJinglePacketReceived; import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket; import eu.siacs.conversations.xmpp.stanzas.IqPacket; @@ -60,13 +55,13 @@ import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.database.ContentObserver; -import android.database.DatabaseUtils; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; +import android.os.Looper; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.os.SystemClock; @@ -87,6 +82,10 @@ public class XmppConnectionService extends Service { private static final int PING_TIMEOUT = 5; private static final int CONNECT_TIMEOUT = 60; private static final long CARBON_GRACE_PERIOD = 60000L; + + private static String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts"; + + private MessageParser mMessageParser = new MessageParser(this); private List<Account> accounts; private List<Conversation> conversations = null; @@ -111,13 +110,12 @@ public class XmppConnectionService extends Service { @Override public void onChange(boolean selfChange) { super.onChange(selfChange); - Log.d(LOGTAG, "contact list has changed"); - mergePhoneContactsWithRoster(null); + Intent intent = new Intent(getApplicationContext(), XmppConnectionService.class); + intent.setAction(ACTION_MERGE_PHONE_CONTACTS); + startService(intent); } }; - private XmppConnectionService service = this; - private final IBinder mBinder = new XmppConnectionBinder(); private OnMessagePacketReceived messageListener = new OnMessagePacketReceived() { @@ -132,26 +130,25 @@ public class XmppConnectionService extends Service { } if ((packet.getType() == MessagePacket.TYPE_CHAT)) { - String pgpBody = MessageParser.getPgpBody(packet); + String pgpBody = mMessageParser.getPgpBody(packet); if (pgpBody != null) { - message = MessageParser.parsePgpChat(pgpBody, packet, - account, service); + message = mMessageParser.parsePgpChat(pgpBody, packet, + account); message.markUnread(); } else if ((packet.getBody() != null) && (packet.getBody().startsWith("?OTR"))) { - message = MessageParser.parseOtrChat(packet, account, - service); + message = mMessageParser.parseOtrChat(packet, account); if (message != null) { message.markUnread(); } } else if (packet.hasChild("body")) { - message = MessageParser.parsePlainTextChat(packet, account, - service); + message = mMessageParser + .parsePlainTextChat(packet, account); message.markUnread(); } else if (packet.hasChild("received") || (packet.hasChild("sent"))) { - message = MessageParser.parseCarbonMessage(packet, account, - service); + message = mMessageParser + .parseCarbonMessage(packet, account); if (message != null) { if (message.getStatus() == Message.STATUS_SEND) { lastCarbonMessageReceived = SystemClock @@ -165,8 +162,7 @@ public class XmppConnectionService extends Service { } } else if (packet.getType() == MessagePacket.TYPE_GROUPCHAT) { - message = MessageParser - .parseGroupchat(packet, account, service); + message = mMessageParser.parseGroupchat(packet, account); if (message != null) { if (message.getStatus() == Message.STATUS_RECIEVED) { message.markUnread(); @@ -176,7 +172,7 @@ public class XmppConnectionService extends Service { } } } else if (packet.getType() == MessagePacket.TYPE_ERROR) { - MessageParser.parseError(packet, account, service); + mMessageParser.parseError(packet, account); return; } else if (packet.getType() == MessagePacket.TYPE_NORMAL) { if (packet.hasChild("x")) { @@ -247,10 +243,17 @@ public class XmppConnectionService extends Service { } else if (account.getStatus() == Account.STATUS_REGISTRATION_SUCCESSFULL) { databaseBackend.updateAccount(account); reconnectAccount(account, true); - } else { - UIHelper.showErrorNotification(getApplicationContext(), - getAccounts()); + } else if (account.getStatus() != Account.STATUS_CONNECTING) { + 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); } + UIHelper.showErrorNotification(getApplicationContext(), + getAccounts()); } }; @@ -298,17 +301,8 @@ public class XmppConnectionService extends Service { } } else { - Contact contact = findContact(account, fromParts[0]); - if (contact == null) { - if ("subscribe".equals(type)) { - account.getXmppConnection().addPendingSubscription( - fromParts[0]); - } else { - // Log.d(LOGTAG,packet.getFrom()+ - // " could not be found"); - } - return; - } + Contact contact = account.getRoster().getContact( + packet.getFrom()); if (type == null) { if (fromParts.length == 2) { contact.updatePresence(fromParts[1], Presences @@ -325,13 +319,10 @@ public class XmppConnectionService extends Service { } else { msg = ""; } - contact.setPgpKeyId(pgp.fetchKeyId(account,msg,x.getContent())); - Log.d("xmppService",account.getJid()+": fetched key id for "+contact.getJid()+" was:"+contact.getPgpKeyId()); + contact.setPgpKeyId(pgp.fetchKeyId(account, + msg, x.getContent())); } } - replaceContactInConversation(account, - contact.getJid(), contact); - databaseBackend.updateContact(contact, true); } else { // Log.d(LOGTAG,"presence without resource "+packet.toString()); } @@ -341,30 +332,20 @@ public class XmppConnectionService extends Service { } else { contact.removePresence(fromParts[1]); } - replaceContactInConversation(account, contact.getJid(), - contact); - databaseBackend.updateContact(contact, true); } else if (type.equals("subscribe")) { Log.d(LOGTAG, "received subscribe packet from " + packet.getFrom()); - if (contact - .getSubscriptionOption(Contact.Subscription.PREEMPTIVE_GRANT)) { + if (contact.getOption(Contact.Options.PREEMPTIVE_GRANT)) { Log.d(LOGTAG, "preemptive grant; granting"); sendPresenceUpdatesTo(contact); - contact.setSubscriptionOption(Contact.Subscription.FROM); - contact.resetSubscriptionOption(Contact.Subscription.PREEMPTIVE_GRANT); - replaceContactInConversation(account, - contact.getJid(), contact); - databaseBackend.updateContact(contact, false); - if ((contact - .getSubscriptionOption(Contact.Subscription.ASKING)) - && (!contact - .getSubscriptionOption(Contact.Subscription.TO))) { + contact.setOption(Contact.Options.FROM); + contact.resetOption(Contact.Options.PREEMPTIVE_GRANT); + if ((contact.getOption(Contact.Options.ASKING)) + && (!contact.getOption(Contact.Options.TO))) { requestPresenceUpdatesFrom(contact); } } else { - account.getXmppConnection().addPendingSubscription( - fromParts[0]); + contact.setOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST); } } else { // Log.d(LOGTAG, packet.toString()); @@ -378,14 +359,13 @@ public class XmppConnectionService extends Service { @Override public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.hasChild("query","jabber:iq:roster")) { + if (packet.hasChild("query", "jabber:iq:roster")) { String from = packet.getFrom(); - if ((from==null)||(from.equals(account.getJid()))) { + if ((from == null) || (from.equals(account.getJid()))) { Element query = packet.findChild("query"); processRosterItems(account, query); - mergePhoneContactsWithRoster(null); } else { - Log.d(LOGTAG,"unauthorized roster push from: "+from); + Log.d(LOGTAG, "unauthorized roster push from: " + from); } } else if (packet .hasChild("open", "http://jabber.org/protocol/ibb") @@ -393,20 +373,30 @@ public class XmppConnectionService extends Service { .hasChild("data", "http://jabber.org/protocol/ibb")) { XmppConnectionService.this.mJingleConnectionManager .deliverIbbPacket(account, packet); - } else if (packet.hasChild("query","http://jabber.org/protocol/disco#info")) { - IqPacket iqResponse = packet.generateRespone(IqPacket.TYPE_RESULT); - Element query = iqResponse.addChild("query", "http://jabber.org/protocol/disco#info"); - query.addChild("feature").setAttribute("var", "urn:xmpp:jingle:1"); - query.addChild("feature").setAttribute("var", "urn:xmpp:jingle:apps:file-transfer:3"); - query.addChild("feature").setAttribute("var", "urn:xmpp:jingle:transports:s5b:1"); - query.addChild("feature").setAttribute("var", "urn:xmpp:jingle:transports:ibb:1"); + } else if (packet.hasChild("query", + "http://jabber.org/protocol/disco#info")) { + IqPacket iqResponse = packet + .generateRespone(IqPacket.TYPE_RESULT); + Element query = iqResponse.addChild("query", + "http://jabber.org/protocol/disco#info"); + query.addChild("feature").setAttribute("var", + "urn:xmpp:jingle:1"); + query.addChild("feature").setAttribute("var", + "urn:xmpp:jingle:apps:file-transfer:3"); + query.addChild("feature").setAttribute("var", + "urn:xmpp:jingle:transports:s5b:1"); + query.addChild("feature").setAttribute("var", + "urn:xmpp:jingle:transports:ibb:1"); account.getXmppConnection().sendIqPacket(iqResponse, null); } else { - if ((packet.getType() == IqPacket.TYPE_GET)||(packet.getType() == IqPacket.TYPE_SET)) { - IqPacket response = packet.generateRespone(IqPacket.TYPE_ERROR); + if ((packet.getType() == IqPacket.TYPE_GET) + || (packet.getType() == IqPacket.TYPE_SET)) { + IqPacket response = packet + .generateRespone(IqPacket.TYPE_ERROR); Element error = response.addChild("error"); - error.setAttribute("type","cancel"); - error.addChild("feature-not-implemented","urn:ietf:params:xml:ns:xmpp-stanzas"); + error.setAttribute("type", "cancel"); + error.addChild("feature-not-implemented", + "urn:ietf:params:xml:ns:xmpp-stanzas"); account.getXmppConnection().sendIqPacket(response, null); } } @@ -433,7 +423,7 @@ public class XmppConnectionService extends Service { if (this.mPgpEngine == null) { this.mPgpEngine = new PgpEngine(new OpenPgpApi( getApplicationContext(), - pgpServiceConnection.getService()),this); + pgpServiceConnection.getService()), this); } return mPgpEngine; } else { @@ -446,10 +436,12 @@ public class XmppConnectionService extends Service { return this.fileBackend; } - public Message attachImageToConversation(final Conversation conversation, final Uri uri, final UiCallback callback) { + public Message attachImageToConversation(final Conversation conversation, + final Uri uri, final UiCallback callback) { final Message message; if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) { - message = new Message(conversation, "",Message.ENCRYPTION_DECRYPTED); + message = new Message(conversation, "", + Message.ENCRYPTION_DECRYPTED); } else { message = new Message(conversation, "", Message.ENCRYPTION_NONE); } @@ -460,21 +452,21 @@ public class XmppConnectionService extends Service { @Override public void run() { - JingleFile file = getFileBackend().copyImageToPrivateStorage(message, uri); - if (file==null) { - callback.error(R.string.error_copying_image_file); - } else { + try { + getFileBackend().copyImageToPrivateStorage(message, uri); if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) { getPgpEngine().encrypt(message, callback); } else { callback.success(); } + } catch (FileBackend.ImageCopyException e) { + callback.error(e.getResId()); } } }).start(); return message; } - + protected Conversation findMuc(String name, Account account) { for (Conversation conversation : this.conversations) { if (conversation.getContactJid().split("/")[0].equals(name) @@ -488,51 +480,25 @@ public class XmppConnectionService extends Service { private void processRosterItems(Account account, Element elements) { String version = elements.getAttribute("ver"); if (version != null) { - account.setRosterVersion(version); - databaseBackend.updateAccount(account); + account.getRoster().setVersion(version); } for (Element item : elements.getChildren()) { if (item.getName().equals("item")) { String jid = item.getAttribute("jid"); + String name = item.getAttribute("name"); String subscription = item.getAttribute("subscription"); - Contact contact = databaseBackend.findContact(account, jid); - if (contact == null) { - if (!subscription.equals("remove")) { - String name = item.getAttribute("name"); - if (name == null) { - name = jid.split("@")[0]; - } - contact = new Contact(account, name, jid, null); - contact.parseSubscriptionFromElement(item); - databaseBackend.createContact(contact); - } + Contact contact = account.getRoster().getContact(jid); + contact.setServerName(name); + if (subscription.equals("remove")) { + contact.resetOption(Contact.Options.IN_ROSTER); } else { - if (subscription.equals("remove")) { - databaseBackend.deleteContact(contact); - replaceContactInConversation(account, contact.getJid(), - null); - } else { - contact.parseSubscriptionFromElement(item); - databaseBackend.updateContact(contact, false); - replaceContactInConversation(account, contact.getJid(), - contact); - } + contact.setOption(Contact.Options.IN_ROSTER); + contact.parseSubscriptionFromElement(item); } } } } - private void replaceContactInConversation(Account account, String jid, - Contact contact) { - List<Conversation> conversations = getConversations(); - for (Conversation c : conversations) { - if (c.getContactJid().equals(jid) && (c.getAccount() == account)) { - c.setContact(contact); - break; - } - } - } - public class XmppConnectionBinder extends Binder { public XmppConnectionService getService() { return XmppConnectionService.this; @@ -542,7 +508,9 @@ public class XmppConnectionService extends Service { @Override public int onStartCommand(Intent intent, int flags, int startId) { this.wakeLock.acquire(); - // Log.d(LOGTAG,"calling start service. caller was:"+intent.getAction()); + if ((intent.getAction()!=null)&&(intent.getAction().equals(ACTION_MERGE_PHONE_CONTACTS))) { + mergePhoneContactsWithRoster(); + } ConnectivityManager cm = (ConnectivityManager) getApplicationContext() .getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); @@ -563,8 +531,6 @@ public class XmppConnectionService extends Service { statusListener.onStatusChanged(account); } } - - // TODO 3 remaining cases if (account.getStatus() == Account.STATUS_ONLINE) { long lastReceived = account.getXmppConnection().lastPaketReceived; long lastSent = account.getXmppConnection().lastPingSent; @@ -592,15 +558,9 @@ public class XmppConnectionService extends Service { + ": time out during connect reconnecting"); reconnectAccount(account, true); } else { - Log.d(LOGTAG, - "seconds since last connect:" - + ((SystemClock.elapsedRealtime() - account - .getXmppConnection().lastConnect) / 1000)); - Log.d(LOGTAG, - account.getJid() + ": status=" - + account.getStatus()); - // TODO notify user of ssl cert problem or auth problem - // or what ever + if (account.getXmppConnection().getTimeToNextAttempt() <= 0) { + reconnectAccount(account, true); + } } // in any case. reschedule wakup call this.scheduleWakeupCall(PING_MAX_INTERVAL, true); @@ -624,6 +584,10 @@ public class XmppConnectionService extends Service { this.fileBackend = new FileBackend(getApplicationContext()); this.accounts = databaseBackend.getAccounts(); + for (Account account : this.accounts) { + this.databaseBackend.readRoster(account.getRoster()); + } + this.mergePhoneContactsWithRoster(); this.getConversations(); getContentResolver().registerContentObserver( @@ -639,13 +603,24 @@ public class XmppConnectionService extends Service { @Override public void onDestroy() { - Log.d(LOGTAG,"stopping service"); super.onDestroy(); + this.logoutAndSave(); + } + + @Override + public void onTaskRemoved(Intent rootIntent) { + super.onTaskRemoved(rootIntent); + this.logoutAndSave(); + } + + private void logoutAndSave() { for (Account account : accounts) { + databaseBackend.writeRoster(account.getRoster()); if (account.getXmppConnection() != null) { disconnect(account, true); } } + Log.d(LOGTAG,"good bye"); } protected void scheduleWakeupCall(int seconds, boolean ping) { @@ -663,7 +638,6 @@ public class XmppConnectionService extends Service { this.pingIntent, 0); alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, timeToWake, pendingPingIntent); - // Log.d(LOGTAG,"schedule ping in "+seconds+" seconds"); } else { long scheduledTime = this.pingIntent.getLongExtra("time", 0); if (scheduledTime < SystemClock.elapsedRealtime() @@ -674,7 +648,6 @@ public class XmppConnectionService extends Service { context, 0, this.pingIntent, 0); alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, timeToWake, pendingPingIntent); - // Log.d(LOGTAG,"reschedule old ping to ping in "+seconds+" seconds"); } } } else { @@ -715,12 +688,11 @@ public class XmppConnectionService extends Service { connection.setOnBindListener(new OnBindListener() { @Override - public void onBind(Account account) { - databaseBackend.clearPresences(account); + public void onBind(final Account account) { + account.getRoster().clearPresences(); account.clearPresences(); // self presences - if (account.getXmppConnection().hasFeatureRosterManagment()) { - updateRoster(account, null); - } + fetchRosterFromServer(account); + sendPresence(account); connectMultiModeConversations(account); if (convChangedListener != null) { convChangedListener.onConversationListChanged(); @@ -758,11 +730,7 @@ public class XmppConnectionService extends Service { addToConversation = true; } else if (message.getEncryption() == Message.ENCRYPTION_PGP) { message.getConversation().endOtrIfNeeded(); - packet = new MessagePacket(); - packet.setType(MessagePacket.TYPE_CHAT); - packet.setFrom(message.getConversation().getAccount() - .getFullJid()); - packet.setTo(message.getCounterpart()); + packet = prepareMessagePacket(account, message, null); packet.setBody("This is an XEP-0027 encryted message"); packet.addChild("x", "jabber:x:encrypted").setContent( message.getEncryptedBody()); @@ -776,17 +744,26 @@ public class XmppConnectionService extends Service { // don't encrypt if (message.getConversation().getMode() == Conversation.MODE_SINGLE) { message.setStatus(Message.STATUS_SEND); - saveInDb = true; - addToConversation = true; } packet = prepareMessagePacket(account, message, null); send = true; + saveInDb = true; + addToConversation = true; } } } else { - // account is offline - saveInDb = true; - addToConversation = true; + if (message.getEncryption() == Message.ENCRYPTION_PGP) { + String pgpBody = message.getEncryptedBody(); + String decryptedBody = message.getBody(); + message.setBody(pgpBody); + databaseBackend.createMessage(message); + message.setEncryption(Message.ENCRYPTION_DECRYPTED); + message.setBody(decryptedBody); + addToConversation = true; + } else { + saveInDb = true; + addToConversation = true; + } } if (saveInDb) { @@ -806,25 +783,38 @@ public class XmppConnectionService extends Service { private void sendUnsendMessages(Conversation conversation) { for (int i = 0; i < conversation.getMessages().size(); ++i) { - if ((conversation.getMessages().get(i).getStatus() == Message.STATUS_UNSEND) - && (conversation.getMessages().get(i).getEncryption() == Message.ENCRYPTION_NONE)) { - Message message = conversation.getMessages().get(i); - MessagePacket packet = prepareMessagePacket( - conversation.getAccount(), message, null); - conversation.getAccount().getXmppConnection() - .sendMessagePacket(packet); - message.setStatus(Message.STATUS_SEND); - if (conversation.getMode() == Conversation.MODE_SINGLE) { - databaseBackend.updateMessage(message); - } else { - databaseBackend.deleteMessage(message); - conversation.getMessages().remove(i); - i--; - } + if (conversation.getMessages().get(i).getStatus() == Message.STATUS_UNSEND) { + resendMessage(conversation.getMessages().get(i)); } } } + private void resendMessage(Message message) { + Account account = message.getConversation().getAccount(); + MessagePacket packet = null; + if (message.getEncryption() == Message.ENCRYPTION_NONE) { + packet = prepareMessagePacket(account, message, null); + } else if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { + packet = prepareMessagePacket(account, message, null); + packet.setBody("This is an XEP-0027 encryted message"); + if (message.getEncryptedBody() == null) { + markMessage(message, Message.STATUS_SEND_FAILED); + return; + } + packet.addChild("x", "jabber:x:encrypted").setContent( + message.getEncryptedBody()); + } else if (message.getEncryption() == Message.ENCRYPTION_PGP) { + packet = prepareMessagePacket(account, message, null); + packet.setBody("This is an XEP-0027 encryted message"); + packet.addChild("x", "jabber:x:encrypted").setContent( + message.getBody()); + } + if (packet != null) { + account.getXmppConnection().sendMessagePacket(packet); + markMessage(message, Message.STATUS_SEND); + } + } + public MessagePacket prepareMessagePacket(Account account, Message message, Session otrSession) { MessagePacket packet = new MessagePacket(); @@ -860,27 +850,7 @@ public class XmppConnectionService extends Service { return packet; } - private void getRoster(Account account, - final OnRosterFetchedListener listener) { - List<Contact> contacts = databaseBackend.getContactsByAccount(account); - for (int i = 0; i < contacts.size(); ++i) { - contacts.get(i).setAccount(account); - } - if (listener != null) { - listener.onRosterFetched(contacts); - } - } - - public List<Contact> getRoster(Account account) { - List<Contact> contacts = databaseBackend.getContactsByAccount(account); - for (int i = 0; i < contacts.size(); ++i) { - contacts.get(i).setAccount(account); - } - return contacts; - } - - public void updateRoster(final Account account, - final OnRosterFetchedListener listener) { + public void fetchRosterFromServer(Account account) { IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET); if (!"".equals(account.getRosterVersion())) { Log.d(LOGTAG, account.getJid() + ": fetching roster version " @@ -898,62 +868,23 @@ public class XmppConnectionService extends Service { IqPacket packet) { Element roster = packet.findChild("query"); if (roster != null) { - Log.d(LOGTAG, account.getJid() - + ": processing roster"); + account.getRoster().markAllAsNotInRoster(); processRosterItems(account, roster); - StringBuilder mWhere = new StringBuilder(); - mWhere.append("jid NOT IN("); - List<Element> items = roster.getChildren(); - for (int i = 0; i < items.size(); ++i) { - mWhere.append(DatabaseUtils - .sqlEscapeString(items.get(i) - .getAttribute("jid"))); - if (i != items.size() - 1) { - mWhere.append(","); - } - } - mWhere.append(") and accountUuid = \""); - mWhere.append(account.getUuid()); - mWhere.append("\""); - List<Contact> contactsToDelete = databaseBackend - .getContacts(mWhere.toString()); - for (Contact contact : contactsToDelete) { - databaseBackend.deleteContact(contact); - replaceContactInConversation(account, - contact.getJid(), null); - } - - } else { - Log.d(LOGTAG, account.getJid() - + ": empty roster returend"); } - mergePhoneContactsWithRoster(new OnPhoneContactsMerged() { - - @Override - public void phoneContactsMerged() { - if (listener != null) { - getRoster(account, listener); - } - } - }); } }); } - public void mergePhoneContactsWithRoster( - final OnPhoneContactsMerged listener) { + private void mergePhoneContactsWithRoster() { PhoneHelper.loadPhoneContacts(getApplicationContext(), new OnPhoneContactsLoadedListener() { @Override - public void onPhoneContactsLoaded( - Hashtable<String, Bundle> phoneContacts) { - List<Contact> contacts = databaseBackend - .getContactsByAccount(null); - for (int i = 0; i < contacts.size(); ++i) { - Contact contact = contacts.get(i); - if (phoneContacts.containsKey(contact.getJid())) { - Bundle phoneContact = phoneContacts.get(contact - .getJid()); + public void onPhoneContactsLoaded(List<Bundle> phoneContacts) { + for (Bundle phoneContact : phoneContacts) { + for (Account account : accounts) { + String jid = phoneContact.getString("jid"); + Contact contact = account.getRoster() + .getContact(jid); String systemAccount = phoneContact .getInt("phoneid") + "#" @@ -961,28 +892,10 @@ public class XmppConnectionService extends Service { contact.setSystemAccount(systemAccount); contact.setPhotoUri(phoneContact .getString("photouri")); - contact.setDisplayName(phoneContact + contact.setSystemName(phoneContact .getString("displayname")); - databaseBackend.updateContact(contact, false); - replaceContactInConversation( - contact.getAccount(), contact.getJid(), - contact); - } else { - if ((contact.getSystemAccount() != null) - || (contact.getProfilePhoto() != null)) { - contact.setSystemAccount(null); - contact.setPhotoUri(null); - databaseBackend.updateContact(contact, - false); - replaceContactInConversation( - contact.getAccount(), - contact.getJid(), contact); - } } } - if (listener != null) { - listener.phoneContactsMerged(); - } } }); } @@ -998,7 +911,6 @@ public class XmppConnectionService extends Service { for (Conversation conv : this.conversations) { Account account = accountLookupTable.get(conv.getAccountUuid()); conv.setAccount(account); - conv.setContact(findContact(account, conv.getContactJid())); conv.setMessages(databaseBackend.getMessages(conv, 50)); } } @@ -1016,14 +928,6 @@ public class XmppConnectionService extends Service { return this.accounts; } - public Contact findContact(Account account, String jid) { - Contact contact = databaseBackend.findContact(account, jid); - if (contact != null) { - contact.setAccount(account); - } - return contact; - } - public Conversation findOrCreateConversation(Account account, String jid, boolean muc) { for (Conversation conv : this.getConversations()) { @@ -1045,11 +949,9 @@ public class XmppConnectionService extends Service { conversation.setMessages(databaseBackend.getMessages(conversation, 50)); this.databaseBackend.updateConversation(conversation); - conversation.setContact(findContact(account, - conversation.getContactJid())); } else { String conversationName; - Contact contact = findContact(account, jid); + Contact contact = account.getRoster().getContact(jid); if (contact != null) { conversationName = contact.getDisplayName(); } else { @@ -1062,7 +964,6 @@ public class XmppConnectionService extends Service { conversation = new Conversation(conversationName, account, jid, Conversation.MODE_SINGLE); } - conversation.setContact(contact); this.databaseBackend.createConversation(conversation); } this.conversations.add(conversation); @@ -1110,23 +1011,14 @@ public class XmppConnectionService extends Service { accountChangedListener.onAccountListChangedListener(); } - public void deleteContact(Contact contact) { - IqPacket iq = new IqPacket(IqPacket.TYPE_SET); - Element query = iq.query("jabber:iq:roster"); - query.addChild("item").setAttribute("jid", contact.getJid()) - .setAttribute("subscription", "remove"); - contact.getAccount().getXmppConnection().sendIqPacket(iq, null); - replaceContactInConversation(contact.getAccount(), contact.getJid(), - null); - databaseBackend.deleteContact(contact); - } - public void updateAccount(Account account) { this.statusListener.onStatusChanged(account); databaseBackend.updateAccount(account); reconnectAccount(account, false); - if (accountChangedListener != null) + if (accountChangedListener != null) { accountChangedListener.onAccountListChangedListener(); + } + UIHelper.showErrorNotification(getApplicationContext(), getAccounts()); } public void deleteAccount(Account account) { @@ -1135,8 +1027,10 @@ public class XmppConnectionService extends Service { } databaseBackend.deleteAccount(account); this.accounts.remove(account); - if (accountChangedListener != null) + if (accountChangedListener != null) { accountChangedListener.onAccountListChangedListener(); + } + UIHelper.showErrorNotification(getApplicationContext(), getAccounts()); } public void setOnConversationListChangedListener( @@ -1214,8 +1108,8 @@ public class XmppConnectionService extends Service { renameListener.onRename(success); } if (success) { - String jid = conversation.getContactJid().split("/")[0] + "/" - + nick; + String jid = conversation.getContactJid().split("/")[0] + + "/" + nick; conversation.setContactJid(jid); databaseBackend.updateConversation(conversation); } @@ -1277,12 +1171,6 @@ public class XmppConnectionService extends Service { return mBinder; } - public void updateContact(Contact contact) { - databaseBackend.updateContact(contact, false); - replaceContactInConversation(contact.getAccount(), contact.getJid(), - contact); - } - public void updateMessage(Message message) { databaseBackend.updateMessage(message); } @@ -1291,30 +1179,33 @@ public class XmppConnectionService extends Service { SharedPreferences sharedPref = getPreferences(); boolean autoGrant = sharedPref.getBoolean("grant_new_contacts", true); if (autoGrant) { - contact.setSubscriptionOption(Contact.Subscription.PREEMPTIVE_GRANT); - contact.setSubscriptionOption(Contact.Subscription.ASKING); + contact.setOption(Contact.Options.PREEMPTIVE_GRANT); + contact.setOption(Contact.Options.ASKING); } - databaseBackend.createContact(contact); - IqPacket iq = new IqPacket(IqPacket.TYPE_SET); - Element query = new Element("query"); - query.setAttribute("xmlns", "jabber:iq:roster"); - Element item = new Element("item"); - item.setAttribute("jid", contact.getJid()); - item.setAttribute("name", contact.getJid()); - query.addChild(item); - iq.addChild(query); - Account account = contact.getAccount(); - account.getXmppConnection().sendIqPacket(iq, null); + pushContactToServer(contact); if (autoGrant) { requestPresenceUpdatesFrom(contact); - if (account.getXmppConnection().hasPendingSubscription( - contact.getJid())) { + if (contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) { Log.d("xmppService", "contact had pending subscription"); sendPresenceUpdatesTo(contact); } } - replaceContactInConversation(contact.getAccount(), contact.getJid(), - contact); + } + + public void pushContactToServer(Contact contact) { + IqPacket iq = new IqPacket(IqPacket.TYPE_SET); + iq.query("jabber:iq:roster").addChild(contact.asElement()); + Account account = contact.getAccount(); + account.getXmppConnection().sendIqPacket(iq, null); + } + + public void deleteContactOnServer(Contact contact) { + IqPacket iq = new IqPacket(IqPacket.TYPE_SET); + Element item = iq.query("jabber:iq:roster").addChild("item"); + item.setAttribute("jid", contact.getJid()); + item.setAttribute("subscription", "remove"); + Account account = contact.getAccount(); + account.getXmppConnection().sendIqPacket(iq, null); } public void requestPresenceUpdatesFrom(Contact contact) { @@ -1355,18 +1246,17 @@ public class XmppConnectionService extends Service { packet.setAttribute("from", contact.getAccount().getJid()); Log.d(LOGTAG, packet.toString()); contact.getAccount().getXmppConnection().sendPresencePacket(packet); + contact.resetOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST); } - public void sendPgpPresence(Account account, String signature) { + public void sendPresence(Account account) { PresencePacket packet = new PresencePacket(); packet.setAttribute("from", account.getFullJid()); - Element status = new Element("status"); - status.setContent("online"); - packet.addChild(status); - Element x = new Element("x"); - x.setAttribute("xmlns", "jabber:x:signed"); - x.setContent(signature); - packet.addChild(x); + String sig = account.getPgpSignature(); + if (sig != null) { + packet.addChild("status").setContent("online"); + packet.addChild("x", "jabber:x:signed").setContent(sig); + } account.getXmppConnection().sendPresencePacket(packet); } @@ -1374,23 +1264,10 @@ public class XmppConnectionService extends Service { this.databaseBackend.updateConversation(conversation); } - public Contact findContact(String uuid) { - Contact contact = this.databaseBackend.getContact(uuid); - if (contact != null) { - for (Account account : getAccounts()) { - if (contact.getAccountUuid().equals(account.getUuid())) { - contact.setAccount(account); - } - } - } - return contact; - } - public void removeOnTLSExceptionReceivedListener() { this.tlsException = null; } - // TODO dont let thread sleep but schedule wake up public void reconnectAccount(final Account account, final boolean force) { new Thread(new Runnable() { @@ -1447,21 +1324,24 @@ public class XmppConnectionService extends Service { public boolean markMessage(Account account, String recipient, String uuid, int status) { - boolean marked = false; for (Conversation conversation : getConversations()) { if (conversation.getContactJid().equals(recipient) && conversation.getAccount().equals(account)) { - for (Message message : conversation.getMessages()) { - if (message.getUuid().equals(uuid)) { - markMessage(message, status); - marked = true; - break; - } - } - break; + return markMessage(conversation, uuid, status); + } + } + return false; + } + + public boolean markMessage(Conversation conversation, String uuid, + int status) { + for (Message message : conversation.getMessages()) { + if (message.getUuid().equals(uuid)) { + markMessage(message, status); + return true; } } - return marked; + return false; } public void markMessage(Message message, int status) { @@ -1476,12 +1356,22 @@ public class XmppConnectionService extends Service { return PreferenceManager .getDefaultSharedPreferences(getApplicationContext()); } - + public void updateUi(Conversation conversation, boolean notify) { if (convChangedListener != null) { convChangedListener.onConversationListChanged(); } else { - UIHelper.updateNotification(getApplicationContext(), getConversations(), conversation, notify); + UIHelper.updateNotification(getApplicationContext(), + getConversations(), conversation, notify); } } + + public Account findAccountByJid(String accountJid) { + for (Account account : this.accounts) { + if (account.getJid().equals(accountJid)) { + return account; + } + } + return null; + } } diff --git a/src/eu/siacs/conversations/ui/ContactDetailsActivity.java b/src/eu/siacs/conversations/ui/ContactDetailsActivity.java index 06179bc6..72a0909a 100644 --- a/src/eu/siacs/conversations/ui/ContactDetailsActivity.java +++ b/src/eu/siacs/conversations/ui/ContactDetailsActivity.java @@ -1,8 +1,6 @@ package eu.siacs.conversations.ui; -import java.math.BigInteger; import java.util.Iterator; -import java.util.Locale; import org.openintents.openpgp.util.OpenPgpUtils; @@ -17,7 +15,6 @@ import android.os.Bundle; import android.provider.ContactsContract.CommonDataKinds; import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Intents; -import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; @@ -31,6 +28,7 @@ import android.widget.TextView; import android.widget.Toast; 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.Presences; import eu.siacs.conversations.utils.UIHelper; @@ -40,12 +38,14 @@ public class ContactDetailsActivity extends XmppActivity { protected ContactDetailsActivity activity = this; - private String uuid; private Contact contact; - + + private String accountJid; + private String contactJid; + private EditText name; - private TextView contactJid; - private TextView accountJid; + private TextView contactJidTv; + private TextView accountJidTv; private TextView status; private TextView askAgain; private CheckBox send; @@ -56,7 +56,7 @@ public class ContactDetailsActivity extends XmppActivity { @Override public void onClick(DialogInterface dialog, int which) { - activity.xmppConnectionService.deleteContact(contact); + activity.xmppConnectionService.deleteContactOnServer(contact); activity.finish(); } }; @@ -65,8 +65,8 @@ public class ContactDetailsActivity extends XmppActivity { @Override public void onClick(DialogInterface dialog, int which) { - contact.setDisplayName(name.getText().toString()); - activity.xmppConnectionService.updateContact(contact); + contact.setServerName(name.getText().toString()); + activity.xmppConnectionService.pushContactToServer(contact); populateView(); } }; @@ -104,12 +104,13 @@ public class ContactDetailsActivity extends XmppActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getIntent().getAction().equals(ACTION_VIEW_CONTACT)) { - this.uuid = getIntent().getExtras().getString("uuid"); + this.accountJid = getIntent().getExtras().getString("account"); + this.contactJid = getIntent().getExtras().getString("contact"); } setContentView(R.layout.activity_contact_details); - contactJid = (TextView) findViewById(R.id.details_contactjid); - accountJid = (TextView) findViewById(R.id.details_account); + contactJidTv = (TextView) findViewById(R.id.details_contactjid); + accountJidTv = (TextView) findViewById(R.id.details_account); status = (TextView) findViewById(R.id.details_contactstatus); send = (CheckBox) findViewById(R.id.details_send_presence); receive = (CheckBox) findViewById(R.id.details_receive_presence); @@ -170,18 +171,18 @@ public class ContactDetailsActivity extends XmppActivity { private void populateView() { setTitle(contact.getDisplayName()); - if (contact.getSubscriptionOption(Contact.Subscription.FROM)) { + if (contact.getOption(Contact.Options.FROM)) { send.setChecked(true); } else { send.setText(R.string.preemptively_grant); if (contact - .getSubscriptionOption(Contact.Subscription.PREEMPTIVE_GRANT)) { + .getOption(Contact.Options.PREEMPTIVE_GRANT)) { send.setChecked(true); } else { send.setChecked(false); } } - if (contact.getSubscriptionOption(Contact.Subscription.TO)) { + if (contact.getOption(Contact.Options.TO)) { receive.setChecked(true); } else { receive.setText(R.string.ask_for_presence_updates); @@ -195,7 +196,7 @@ public class ContactDetailsActivity extends XmppActivity { } }); - if (contact.getSubscriptionOption(Contact.Subscription.ASKING)) { + if (contact.getOption(Contact.Options.ASKING)) { receive.setChecked(true); } else { receive.setChecked(false); @@ -233,11 +234,11 @@ public class ContactDetailsActivity extends XmppActivity { break; } if (contact.getPresences().size() > 1) { - contactJid.setText(contact.getJid()+" ("+contact.getPresences().size()+")"); + contactJidTv.setText(contact.getJid()+" ("+contact.getPresences().size()+")"); } else { - contactJid.setText(contact.getJid()); + contactJidTv.setText(contact.getJid()); } - accountJid.setText(contact.getAccount().getJid()); + accountJidTv.setText(contact.getAccount().getJid()); UIHelper.prepareContactBadge(this, badge, contact, getApplicationContext()); @@ -286,65 +287,66 @@ public class ContactDetailsActivity extends XmppActivity { @Override public void onBackendConnected() { - if (uuid != null) { - this.contact = xmppConnectionService.findContact(uuid); - if (this.contact != null) { - populateView(); + if ((accountJid != null)&&(contactJid != null)) { + Account account = xmppConnectionService.findAccountByJid(accountJid); + if (account==null) { + return; } + this.contact = account.getRoster().getContact(contactJid); + populateView(); } } @Override protected void onStop() { super.onStop(); - boolean needsUpdating = false; - if (contact.getSubscriptionOption(Contact.Subscription.FROM)) { + boolean updated = false; + if (contact.getOption(Contact.Options.FROM)) { if (!send.isChecked()) { - contact.resetSubscriptionOption(Contact.Subscription.FROM); - contact.resetSubscriptionOption(Contact.Subscription.PREEMPTIVE_GRANT); + contact.resetOption(Contact.Options.FROM); + contact.resetOption(Contact.Options.PREEMPTIVE_GRANT); activity.xmppConnectionService.stopPresenceUpdatesTo(contact); - needsUpdating = true; + updated = true; } } else { if (contact - .getSubscriptionOption(Contact.Subscription.PREEMPTIVE_GRANT)) { + .getOption(Contact.Options.PREEMPTIVE_GRANT)) { if (!send.isChecked()) { - contact.resetSubscriptionOption(Contact.Subscription.PREEMPTIVE_GRANT); - needsUpdating = true; + contact.resetOption(Contact.Options.PREEMPTIVE_GRANT); + updated = true; } } else { if (send.isChecked()) { - contact.setSubscriptionOption(Contact.Subscription.PREEMPTIVE_GRANT); - needsUpdating = true; + contact.setOption(Contact.Options.PREEMPTIVE_GRANT); + updated = true; } } } - if (contact.getSubscriptionOption(Contact.Subscription.TO)) { + if (contact.getOption(Contact.Options.TO)) { if (!receive.isChecked()) { - contact.resetSubscriptionOption(Contact.Subscription.TO); + contact.resetOption(Contact.Options.TO); activity.xmppConnectionService.stopPresenceUpdatesFrom(contact); - needsUpdating = true; + updated = true; } } else { - if (contact.getSubscriptionOption(Contact.Subscription.ASKING)) { + if (contact.getOption(Contact.Options.ASKING)) { if (!receive.isChecked()) { - contact.resetSubscriptionOption(Contact.Subscription.ASKING); + contact.resetOption(Contact.Options.ASKING); activity.xmppConnectionService .stopPresenceUpdatesFrom(contact); - needsUpdating = true; + updated = true; } } else { if (receive.isChecked()) { - contact.setSubscriptionOption(Contact.Subscription.ASKING); + contact.setOption(Contact.Options.ASKING); activity.xmppConnectionService .requestPresenceUpdatesFrom(contact); - needsUpdating = true; + updated = true; } } } - if (needsUpdating) { + if (updated) { Toast.makeText(getApplicationContext(), "Subscription updated", Toast.LENGTH_SHORT).show(); - activity.xmppConnectionService.updateContact(contact); } } diff --git a/src/eu/siacs/conversations/ui/ContactsActivity.java b/src/eu/siacs/conversations/ui/ContactsActivity.java index e403450a..d703b172 100644 --- a/src/eu/siacs/conversations/ui/ContactsActivity.java +++ b/src/eu/siacs/conversations/ui/ContactsActivity.java @@ -18,7 +18,6 @@ import android.os.Bundle; import android.preference.PreferenceManager; import android.text.Editable; import android.text.TextWatcher; -import android.util.Log; import android.util.SparseBooleanArray; import android.view.ActionMode; import android.view.LayoutInflater; @@ -34,13 +33,11 @@ import android.widget.AdapterView.OnItemLongClickListener; import android.widget.ArrayAdapter; import android.widget.EditText; import android.widget.ListView; -import android.widget.ProgressBar; import android.widget.TextView; import android.widget.ImageView; import android.widget.Toast; import android.annotation.SuppressLint; import android.app.AlertDialog; -import android.app.AlertDialog.Builder; import android.content.Context; import android.content.DialogInterface; import android.content.SharedPreferences; @@ -130,8 +127,10 @@ public class ContactsActivity extends XmppActivity { Intent intent = new Intent(getApplicationContext(), ContactDetailsActivity.class); intent.setAction(ContactDetailsActivity.ACTION_VIEW_CONTACT); - intent.putExtra("uuid", selectedContacts.get(0).getUuid()); + intent.putExtra("account", selectedContacts.get(0).getAccount().getJid()); + intent.putExtra("contact",selectedContacts.get(0).getJid()); startActivity(intent); + finish(); break; case R.id.action_invite: invite(); @@ -270,7 +269,7 @@ public class ContactsActivity extends XmppActivity { aggregatedContacts.clear(); for (Contact contact : rosterContacts) { - if (contact.match(searchString)) + if (contact.match(searchString)&&(contact.getOption(Contact.Options.IN_ROSTER))) aggregatedContacts.add(contact); } @@ -287,9 +286,8 @@ public class ContactsActivity extends XmppActivity { if (aggregatedContacts.size() == 0) { if (Validator.isValidJid(searchString)) { - String name = searchString.split("@")[0]; - Contact newContact = new Contact(null, name, searchString, null); - newContact.flagAsNotInRoster(); + Contact newContact = new Contact(searchString); + newContact.resetOption(Contact.Options.IN_ROSTER); aggregatedContacts.add(newContact); contactsHeader.setText("Create new contact"); } else { @@ -463,7 +461,7 @@ public class ContactsActivity extends XmppActivity { } public void startConversation(Contact contact, Account account, boolean muc) { - if (!contact.isInRoster()&&(!muc)) { + if (!contact.getOption(Contact.Options.IN_ROSTER)&&(!muc)) { xmppConnectionService.createContact(contact); } Conversation conversation = xmppConnectionService @@ -515,9 +513,10 @@ public class ContactsActivity extends XmppActivity { getActionBar().setHomeButtonEnabled(false); } this.rosterContacts.clear(); - for (int i = 0; i < accounts.size(); ++i) { - rosterContacts.addAll(xmppConnectionService.getRoster(accounts - .get(i))); + for(Account account : accounts) { + if (account.getStatus() != Account.STATUS_DISABLED) { + rosterContacts.addAll(account.getRoster().getContacts()); + } } updateAggregatedContacts(); } @@ -532,52 +531,12 @@ public class ContactsActivity extends XmppActivity { @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { - case R.id.action_refresh_contacts: - refreshContacts(); - break; default: break; } return super.onOptionsItemSelected(item); } - private void refreshContacts() { - final ProgressBar progress = (ProgressBar) findViewById(R.id.progressBar1); - final EditText searchBar = (EditText) findViewById(R.id.new_conversation_search); - final TextView contactsHeader = (TextView) findViewById(R.id.contacts_header); - final ListView contactList = (ListView) findViewById(R.id.contactList); - searchBar.setVisibility(View.GONE); - contactsHeader.setVisibility(View.GONE); - contactList.setVisibility(View.GONE); - progress.setVisibility(View.VISIBLE); - this.accounts = xmppConnectionService.getAccounts(); - this.rosterContacts.clear(); - for (int i = 0; i < accounts.size(); ++i) { - if (accounts.get(i).getStatus() == Account.STATUS_ONLINE) { - xmppConnectionService.updateRoster(accounts.get(i), - new OnRosterFetchedListener() { - - @Override - public void onRosterFetched( - final List<Contact> roster) { - runOnUiThread(new Runnable() { - - @Override - public void run() { - rosterContacts.addAll(roster); - progress.setVisibility(View.GONE); - searchBar.setVisibility(View.VISIBLE); - contactList.setVisibility(View.VISIBLE); - contactList.setVisibility(View.VISIBLE); - updateAggregatedContacts(); - } - }); - } - }); - } - } - } - @Override public void onActionModeStarted(ActionMode mode) { super.onActionModeStarted(mode); diff --git a/src/eu/siacs/conversations/ui/ConversationActivity.java b/src/eu/siacs/conversations/ui/ConversationActivity.java index 88728245..f1995935 100644 --- a/src/eu/siacs/conversations/ui/ConversationActivity.java +++ b/src/eu/siacs/conversations/ui/ConversationActivity.java @@ -6,19 +6,19 @@ import java.util.ArrayList; import java.util.Hashtable; import java.util.List; -import org.openintents.openpgp.OpenPgpError; - import eu.siacs.conversations.R; 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.ImageProvider; import eu.siacs.conversations.utils.ExceptionHelper; import eu.siacs.conversations.utils.UIHelper; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.preference.PreferenceManager; +import android.provider.MediaStore; import android.app.AlertDialog; import android.app.FragmentTransaction; import android.app.PendingIntent; @@ -65,9 +65,14 @@ public class ConversationActivity extends XmppActivity { 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; - private static final int REQUEST_ATTACH_FILE = 0x73824; public static final int REQUEST_ENCRYPT_MESSAGE = 0x378018; + + 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; protected SlidingPaneLayout spl; @@ -120,6 +125,10 @@ public class ConversationActivity extends XmppActivity { public Conversation getSelectedConversation() { return this.selectedConversation; } + + public void setSelectedConversation(Conversation conversation) { + this.selectedConversation = conversation; + } public ListView getConversationListView() { return this.listView; @@ -214,7 +223,7 @@ public class ConversationActivity extends XmppActivity { } ((TextView) view.findViewById(R.id.conversation_lastupdate)) - .setText(UIHelper.readableTimeDifference(conv + .setText(UIHelper.readableTimeDifference(getContext(), conv .getLatestMessage().getTimeSent())); ImageView profilePicture = (ImageView) view @@ -235,8 +244,8 @@ public class ConversationActivity extends XmppActivity { public void onItemClick(AdapterView<?> arg0, View clickedView, int position, long arg3) { paneShouldBeOpen = false; - if (selectedConversation != conversationList.get(position)) { - selectedConversation = conversationList.get(position); + if (getSelectedConversation() != conversationList.get(position)) { + setSelectedConversation(conversationList.get(position)); swapConversationFragment(); // .onBackendConnected(conversationList.get(position)); } else { spl.closePane(); @@ -327,28 +336,40 @@ public class ConversationActivity extends XmppActivity { return true; } - private void attachFileDialog() { + private void selectPresenceToAttachFile(final int attachmentChoice) { selectPresence(getSelectedConversation(), new OnPresenceSelected() { @Override public void onPresenceSelected(boolean success, String presence) { if (success) { - Intent attachFileIntent = new Intent(); - attachFileIntent.setType("image/*"); - attachFileIntent.setAction(Intent.ACTION_GET_CONTENT); - Intent chooser = Intent.createChooser(attachFileIntent, getString(R.string.attach_file)); - startActivityForResult(chooser, REQUEST_ATTACH_FILE_DIALOG); + if (attachmentChoice==ATTACHMENT_CHOICE_TAKE_PHOTO) { + Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, ImageProvider.getIncomingContentUri()); + if (takePictureIntent.resolveActivity(getPackageManager()) != null) { + startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE); + } + } else if (attachmentChoice==ATTACHMENT_CHOICE_CHOOSE_IMAGE) { + Intent attachFileIntent = new Intent(); + attachFileIntent.setType("image/*"); + attachFileIntent.setAction(Intent.ACTION_GET_CONTENT); + Intent chooser = Intent.createChooser(attachFileIntent, getString(R.string.attach_file)); + startActivityForResult(chooser, REQUEST_ATTACH_FILE_DIALOG); + } else if (attachmentChoice==ATTACHMENT_CHOICE_RECORD_VOICE) { + Intent intent = new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION); + startActivityForResult(intent, REQUEST_RECORD_AUDIO); + } } } @Override public void onSendPlainTextInstead() { + // TODO Auto-generated method stub } },"file"); } - private void attachFile() { + private void attachFile(final int attachmentChoice) { final Conversation conversation = getSelectedConversation(); if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) { if (hasPgp()) { @@ -357,18 +378,17 @@ public class ConversationActivity extends XmppActivity { @Override public void userInputRequried(PendingIntent pi) { - ConversationActivity.this.runIntent(pi, REQUEST_ATTACH_FILE); + ConversationActivity.this.runIntent(pi, attachmentChoice); } @Override public void success() { - attachFileDialog(); + selectPresenceToAttachFile(attachmentChoice); } @Override public void error(int error) { - // TODO Auto-generated method stub - + displayErrorDialog(error); } }); } else { @@ -380,14 +400,14 @@ public class ConversationActivity extends XmppActivity { @Override public void onClick(DialogInterface dialog, int which) { conversation.setNextEncryption(Message.ENCRYPTION_NONE); - attachFileDialog(); + selectPresenceToAttachFile(attachmentChoice); } }); } } } } else if (getSelectedConversation().getNextEncryption() == Message.ENCRYPTION_NONE) { - attachFileDialog(); + selectPresenceToAttachFile(attachmentChoice); } else { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(getString(R.string.otr_file_transfer)); @@ -399,7 +419,7 @@ public class ConversationActivity extends XmppActivity { @Override public void onClick(DialogInterface dialog, int which) { conversation.setNextEncryption(Message.ENCRYPTION_NONE); - attachFile(); + attachFile(attachmentChoice); } }); } else { @@ -408,7 +428,7 @@ public class ConversationActivity extends XmppActivity { @Override public void onClick(DialogInterface dialog, int which) { conversation.setNextEncryption(Message.ENCRYPTION_PGP); - attachFile(); + attachFile(attachmentChoice); } }); } @@ -423,7 +443,28 @@ public class ConversationActivity extends XmppActivity { spl.openPane(); break; case R.id.action_attach_file: - attachFile(); + View menuAttachFile = findViewById(R.id.action_attach_file); + PopupMenu attachFilePopup = new PopupMenu(this, menuAttachFile); + attachFilePopup.inflate(R.menu.attachment_choices); + attachFilePopup.setOnMenuItemClickListener(new OnMenuItemClickListener() { + + @Override + public boolean onMenuItemClick(MenuItem item) { + switch (item.getItemId()) { + case R.id.attach_choose_picture: + attachFile(ATTACHMENT_CHOICE_CHOOSE_IMAGE); + break; + case R.id.attach_take_picture: + attachFile(ATTACHMENT_CHOICE_TAKE_PHOTO); + break; + case R.id.attach_record_voice: + attachFile(ATTACHMENT_CHOICE_RECORD_VOICE); + break; + } + return false; + } + }); + attachFilePopup.show(); break; case R.id.action_add: startActivity(new Intent(this, ContactsActivity.class)); @@ -433,10 +474,11 @@ public class ConversationActivity extends XmppActivity { break; case R.id.action_contact_details: Contact contact = this.getSelectedConversation().getContact(); - if (contact != null) { + if (contact.getOption(Contact.Options.IN_ROSTER)) { Intent intent = new Intent(this, ContactDetailsActivity.class); intent.setAction(ContactDetailsActivity.ACTION_VIEW_CONTACT); - intent.putExtra("uuid", contact.getUuid()); + intent.putExtra("account", this.getSelectedConversation().getAccount().getJid()); + intent.putExtra("contact",contact.getJid()); startActivity(intent); } else { showAddToRosterDialog(getSelectedConversation()); @@ -452,7 +494,7 @@ public class ConversationActivity extends XmppActivity { Intent inviteIntent = new Intent(getApplicationContext(), ContactsActivity.class); inviteIntent.setAction("invite"); - inviteIntent.putExtra("uuid", selectedConversation.getUuid()); + inviteIntent.putExtra("uuid", getSelectedConversation().getUuid()); startActivity(inviteIntent); break; case R.id.action_security: @@ -531,9 +573,9 @@ public class ConversationActivity extends XmppActivity { spl.openPane(); xmppConnectionService.archiveConversation(conversation); if (conversationList.size() > 0) { - selectedConversation = conversationList.get(0); + setSelectedConversation(conversationList.get(0)); } else { - selectedConversation = null; + setSelectedConversation(null); } } @@ -619,7 +661,7 @@ public class ConversationActivity extends XmppActivity { for (int i = 0; i < conversationList.size(); ++i) { if (conversationList.get(i).getUuid().equals(convToView)) { - selectedConversation = conversationList.get(i); + setSelectedConversation(conversationList.get(i)); } } paneShouldBeOpen = false; @@ -642,7 +684,7 @@ public class ConversationActivity extends XmppActivity { if (selectedFragment != null) { selectedFragment.onBackendConnected(); } else { - selectedConversation = conversationList.get(0); + setSelectedConversation(conversationList.get(0)); swapConversationFragment(); } ExceptionHelper.checkForCrash(this, this.xmppConnectionService); @@ -668,67 +710,68 @@ public class ConversationActivity extends XmppActivity { selectedFragment.hidePgpPassphraseBox(); } } else if (requestCode == REQUEST_ATTACH_FILE_DIALOG) { - prepareImageToast = Toast.makeText(getApplicationContext(), getText(R.string.preparing_image), Toast.LENGTH_LONG); - final Conversation conversation = getSelectedConversation(); - if (conversation.getNextEncryption() == Message.ENCRYPTION_NONE) { - prepareImageToast.show(); - this.pendingMessage = xmppConnectionService.attachImageToConversation(conversation, data.getData(),new UiCallback() { - - @Override - public void userInputRequried(PendingIntent pi) { - - } - - @Override - public void success() { - sendPendingImageMessage(); - } - - @Override - public void error(int error) { - pendingMessage = null; - displayErrorDialog(error); - } - }); - } else if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) { - prepareImageToast.show(); - attachPgpFile(conversation,data.getData()); - } else { - Log.d(LOGTAG,"unknown next message encryption: "+conversation.getNextEncryption()); - } + attachImageToConversation(getSelectedConversation(),data.getData()); } else if (requestCode == REQUEST_SEND_PGP_IMAGE) { - } else if (requestCode == REQUEST_ATTACH_FILE) { - attachFile(); + } else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_IMAGE) { + attachFile(ATTACHMENT_CHOICE_CHOOSE_IMAGE); + } else if (requestCode == ATTACHMENT_CHOICE_TAKE_PHOTO) { + attachFile(ATTACHMENT_CHOICE_TAKE_PHOTO); } else if (requestCode == REQUEST_ANNOUNCE_PGP) { announcePgp(getSelectedConversation().getAccount(),getSelectedConversation()); } else if (requestCode == REQUEST_ENCRYPT_MESSAGE) { encryptTextMessage(); + } else if (requestCode == REQUEST_IMAGE_CAPTURE) { + attachImageToConversation(getSelectedConversation(), null); + } 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 attachPgpFile(Conversation conversation, Uri uri) { - pendingMessage = xmppConnectionService.attachImageToConversation(conversation, uri, new UiCallback() { - - @Override - public void userInputRequried(PendingIntent pi) { - ConversationActivity.this.runIntent(pi, ConversationActivity.REQUEST_SEND_PGP_IMAGE); - } + private void attachAudioToConversation(Conversation conversation, Uri uri) { + + } + + private void attachImageToConversation(Conversation conversation, Uri uri) { + prepareImageToast = Toast.makeText(getApplicationContext(), getText(R.string.preparing_image), Toast.LENGTH_LONG); + prepareImageToast.show(); + pendingMessage = xmppConnectionService.attachImageToConversation(conversation, uri, new UiCallback() { - @Override - public void success() { - sendPendingImageMessage(); - } + @Override + public void userInputRequried(PendingIntent pi) { + hidePrepareImageToast(); + ConversationActivity.this.runIntent(pi, ConversationActivity.REQUEST_SEND_PGP_IMAGE); + } + + @Override + public void success() { + sendPendingImageMessage(); + hidePrepareImageToast(); + } + + @Override + public void error(int error) { + hidePrepareImageToast(); + pendingMessage = null; + displayErrorDialog(error); + } + }); + } + + private void hidePrepareImageToast() { + if (prepareImageToast!=null) { + runOnUiThread(new Runnable() { @Override - public void error(int error) { - pendingMessage = null; - displayErrorDialog(error); + public void run() { + prepareImageToast.cancel(); } }); + } } private void sendPendingImageMessage() { @@ -832,8 +875,7 @@ public class ConversationActivity extends XmppActivity { public void onClick(DialogInterface dialog, int which) { String jid = conversation.getContactJid(); Account account = getSelectedConversation().getAccount(); - String name = jid.split("@")[0]; - Contact contact = new Contact(account, name, jid, null); + Contact contact = account.getRoster().getContact(jid); xmppConnectionService.createContact(contact); } }); diff --git a/src/eu/siacs/conversations/ui/ConversationFragment.java b/src/eu/siacs/conversations/ui/ConversationFragment.java index 91a39ecc..41837d8f 100644 --- a/src/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/eu/siacs/conversations/ui/ConversationFragment.java @@ -5,17 +5,15 @@ import java.util.HashMap; import java.util.List; import java.util.Set; -import org.openintents.openpgp.OpenPgpError; - 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.Contact; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.MucOptions; import eu.siacs.conversations.entities.MucOptions.OnRenameListener; +import eu.siacs.conversations.services.ImageProvider; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.xmpp.jingle.JingleConnection; @@ -30,7 +28,6 @@ import android.content.SharedPreferences; import android.content.IntentSender.SendIntentException; import android.graphics.Bitmap; import android.graphics.Typeface; -import android.net.Uri; import android.os.Bundle; import android.preference.PreferenceManager; import android.text.Editable; @@ -236,7 +233,7 @@ public class ConversationFragment extends Fragment { viewHolder.indicator.setVisibility(View.VISIBLE); } - String formatedTime = UIHelper.readableTimeDifference(message + String formatedTime = UIHelper.readableTimeDifference(getContext(), message .getTimeSent()); if (message.getStatus() <= Message.STATUS_RECIEVED) { if ((filesize != null) && (info != null)) { @@ -266,7 +263,9 @@ public class ConversationFragment extends Fragment { } private void displayInfoMessage(ViewHolder viewHolder, int r) { - viewHolder.download_button.setVisibility(View.GONE); + if (viewHolder.download_button != null) { + viewHolder.download_button.setVisibility(View.GONE); + } viewHolder.image.setVisibility(View.GONE); viewHolder.messageBody.setVisibility(View.VISIBLE); viewHolder.messageBody.setText(getString(r)); @@ -329,15 +328,9 @@ public class ConversationFragment extends Fragment { @Override public void onClick(View v) { - Uri uri = Uri - .parse("content://eu.siacs.conversations.images/" - + message.getConversationUuid() - + "/" - + message.getUuid()); - Log.d("xmppService", - "staring intent with uri:" + uri.toString()); Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setDataAndType(uri, "image/*"); + intent.setDataAndType( + ImageProvider.getContentUri(message), "image/*"); startActivity(intent); } }); @@ -399,20 +392,17 @@ public class ConversationFragment extends Fragment { if (type == RECIEVED) { if (item.getConversation().getMode() == Conversation.MODE_MULTI) { - if (item.getCounterpart() != null) { - viewHolder.contact_picture - .setImageBitmap(mBitmapCache.get(item - .getCounterpart(), null, - getActivity() - .getApplicationContext())); - } else { - viewHolder.contact_picture - .setImageBitmap(mBitmapCache.get( - item.getConversation().getName( - useSubject), null, - getActivity() - .getApplicationContext())); - } + viewHolder.contact_picture.setImageBitmap(mBitmapCache + .get(item.getCounterpart(), null, getActivity() + .getApplicationContext())); + viewHolder.contact_picture + .setOnClickListener(new OnClickListener() { + + @Override + public void onClick(View v) { + highlightInConference(item.getCounterpart()); + } + }); } } @@ -468,6 +458,22 @@ public class ConversationFragment extends Fragment { return view; } + protected void highlightInConference(String nick) { + if (chatMsg.getText().toString().isEmpty()) { + chatMsg.setText(nick+": "); + } else { + String oldString = chatMsg.getText().toString(); + if (oldString.endsWith(" ")) { + chatMsg.setText(oldString+nick+" "); + } else { + chatMsg.setText(oldString+" "+nick+" "); + } + } + int position = chatMsg.length(); + Editable etext = chatMsg.getText(); + Selection.setSelection(etext, position); + } + protected Bitmap findSelfPicture() { SharedPreferences sharedPref = PreferenceManager .getDefaultSharedPreferences(getActivity() @@ -494,7 +500,7 @@ public class ConversationFragment extends Fragment { @Override public void onStop() { super.onStop(); - if (this.conversation!=null) { + if (this.conversation != null) { this.conversation.setNextMessage(chatMsg.getText().toString()); } } @@ -585,14 +591,15 @@ public class ConversationFragment extends Fragment { } public void updateMessages() { - if (getView()==null) { + if (getView() == null) { return; } ConversationActivity activity = (ConversationActivity) getActivity(); if (this.conversation != null) { for (Message message : this.conversation.getMessages()) { if ((message.getEncryption() == Message.ENCRYPTION_PGP) - && (message.getStatus() == Message.STATUS_RECIEVED)) { + && ((message.getStatus() == Message.STATUS_RECIEVED) || (message + .getStatus() == Message.STATUS_SEND))) { decryptMessage(message); break; } @@ -632,31 +639,26 @@ public class ConversationFragment extends Fragment { protected void makeFingerprintWarning(int latestEncryption) { final LinearLayout fingerprintWarning = (LinearLayout) getView() .findViewById(R.id.new_fingerprint); - if (conversation.getContact() != null) { - Set<String> knownFingerprints = conversation.getContact() - .getOtrFingerprints(); - if ((latestEncryption == Message.ENCRYPTION_OTR) - && (conversation.hasValidOtrSession() - && (conversation.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) && (!knownFingerprints - .contains(conversation.getOtrFingerprint())))) { - fingerprintWarning.setVisibility(View.VISIBLE); - TextView fingerprint = (TextView) getView().findViewById( - R.id.otr_fingerprint); - fingerprint.setText(conversation.getOtrFingerprint()); - fingerprintWarning.setOnClickListener(new OnClickListener() { + Set<String> knownFingerprints = conversation.getContact() + .getOtrFingerprints(); + if ((latestEncryption == Message.ENCRYPTION_OTR) + && (conversation.hasValidOtrSession() + && (conversation.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) && (!knownFingerprints + .contains(conversation.getOtrFingerprint())))) { + fingerprintWarning.setVisibility(View.VISIBLE); + TextView fingerprint = (TextView) getView().findViewById( + R.id.otr_fingerprint); + fingerprint.setText(conversation.getOtrFingerprint()); + fingerprintWarning.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - AlertDialog dialog = UIHelper - .getVerifyFingerprintDialog( - (ConversationActivity) getActivity(), - conversation, fingerprintWarning); - dialog.show(); - } - }); - } else { - fingerprintWarning.setVisibility(View.GONE); - } + @Override + public void onClick(View v) { + AlertDialog dialog = UIHelper.getVerifyFingerprintDialog( + (ConversationActivity) getActivity(), conversation, + fingerprintWarning); + dialog.show(); + } + }); } else { fingerprintWarning.setVisibility(View.GONE); } @@ -675,35 +677,31 @@ public class ConversationFragment extends Fragment { final Contact contact = message.getConversation().getContact(); if (activity.hasPgp()) { if (contact.getPgpKeyId() != 0) { - xmppService.getPgpEngine().hasKey(contact, - new UiCallback() { - - @Override - public void userInputRequried(PendingIntent pi) { - activity.runIntent( - pi, - ConversationActivity.REQUEST_ENCRYPT_MESSAGE); - } + xmppService.getPgpEngine().hasKey(contact, new UiCallback() { - @Override - public void success() { - activity.encryptTextMessage(); - } + @Override + public void userInputRequried(PendingIntent pi) { + activity.runIntent(pi, + ConversationActivity.REQUEST_ENCRYPT_MESSAGE); + } - @Override - public void error(int error) { - - } - }); + @Override + public void success() { + activity.encryptTextMessage(); + } + + @Override + public void error(int error) { + + } + }); } else { showNoPGPKeyDialog(new DialogInterface.OnClickListener() { @Override - public void onClick(DialogInterface dialog, - int which) { - conversation - .setNextEncryption(Message.ENCRYPTION_NONE); + public void onClick(DialogInterface dialog, int which) { + conversation.setNextEncryption(Message.ENCRYPTION_NONE); message.setEncryption(Message.ENCRYPTION_NONE); xmppService.sendMessage(message, null); chatMsg.setText(""); @@ -712,15 +710,15 @@ public class ConversationFragment extends Fragment { } } } - + public void showNoPGPKeyDialog(DialogInterface.OnClickListener listener) { - AlertDialog.Builder builder = new AlertDialog.Builder( - getActivity()); + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setTitle(getString(R.string.no_pgp_key)); builder.setIconAttribute(android.R.attr.alertDialogIcon); builder.setMessage(getText(R.string.contact_has_no_pgp_key)); builder.setNegativeButton(getString(R.string.cancel), null); - builder.setPositiveButton(getString(R.string.send_unencrypted),listener); + builder.setPositiveButton(getString(R.string.send_unencrypted), + listener); builder.create().show(); } @@ -766,7 +764,6 @@ public class ConversationFragment extends Fragment { private class BitmapCache { private HashMap<String, Bitmap> bitmaps = new HashMap<String, Bitmap>(); - private Bitmap error = null; public Bitmap get(String name, Contact contact, Context context) { if (bitmaps.containsKey(name)) { diff --git a/src/eu/siacs/conversations/ui/EditAccount.java b/src/eu/siacs/conversations/ui/EditAccount.java index 8b1c0fa6..47930747 100644 --- a/src/eu/siacs/conversations/ui/EditAccount.java +++ b/src/eu/siacs/conversations/ui/EditAccount.java @@ -51,27 +51,19 @@ public class EditAccount extends DialogFragment { final CheckBox registerAccount = (CheckBox) view .findViewById(R.id.edit_account_register_new); - final String okButtonDesc; - if (account != null) { jidText.setText(account.getJid()); password.setText(account.getPassword()); - Log.d("xmppService","mein debugger. account != null"); if (account.isOptionSet(Account.OPTION_REGISTER)) { registerAccount.setChecked(true); - builder.setTitle(getString(R.string.register_account)); - okButtonDesc = "Register"; passwordConfirm.setVisibility(View.VISIBLE); passwordConfirm.setText(account.getPassword()); } else { registerAccount.setVisibility(View.GONE); - builder.setTitle("Edit account"); - okButtonDesc = "Edit"; } - } else { - builder.setTitle("Add account"); - okButtonDesc = "Add"; } + builder.setTitle(R.string.account_settings); + registerAccount .setOnCheckedChangeListener(new OnCheckedChangeListener() { @@ -79,26 +71,19 @@ public class EditAccount extends DialogFragment { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - AlertDialog d = (AlertDialog) getDialog(); - Button positiveButton = (Button) d - .getButton(Dialog.BUTTON_POSITIVE); if (isChecked) { - d.setTitle(getString(R.string.register_account)); - positiveButton.setText("Register"); passwordConfirm.setVisibility(View.VISIBLE); confirmPwDesc.setVisibility(View.VISIBLE); } else { - d.setTitle("Add account"); passwordConfirm.setVisibility(View.GONE); - positiveButton.setText("Add"); confirmPwDesc.setVisibility(View.GONE); } } }); builder.setView(view); - builder.setNeutralButton("Cancel", null); - builder.setPositiveButton(okButtonDesc, null); + builder.setNeutralButton(R.string.cancel, null); + builder.setPositiveButton(R.string.save, null); return builder.create(); } @@ -114,7 +99,9 @@ public class EditAccount extends DialogFragment { 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; @@ -123,9 +110,15 @@ public class EditAccount extends DialogFragment { username = parts[0]; server = parts[1]; } else { - jidEdit.setError("Invalid Jabber ID"); + 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); diff --git a/src/eu/siacs/conversations/ui/ManageAccountActivity.java b/src/eu/siacs/conversations/ui/ManageAccountActivity.java index 0b7dac58..ba570364 100644 --- a/src/eu/siacs/conversations/ui/ManageAccountActivity.java +++ b/src/eu/siacs/conversations/ui/ManageAccountActivity.java @@ -73,7 +73,7 @@ public class ManageAccountActivity extends XmppActivity { @Override public void run() { AlertDialog.Builder builder = new AlertDialog.Builder(activity); - builder.setTitle("Untrusted Certificate"); + builder.setTitle(getString(R.string.account_status_error)); builder.setIconAttribute(android.R.attr.alertDialogIcon); View view = (View) getLayoutInflater().inflate(R.layout.cert_warning, null); TextView sha = (TextView) view.findViewById(R.id.sha); @@ -91,8 +91,8 @@ public class ManageAccountActivity extends XmppActivity { hint.setText(getString(R.string.untrusted_cert_hint,account.getServer())); sha.setText(humanReadableSha.toString()); builder.setView(view); - builder.setNegativeButton("Don't connect", null); - builder.setPositiveButton("Trust certificate", new OnClickListener() { + builder.setNegativeButton(getString(R.string.certif_no_trust), null); + builder.setPositiveButton(getString(R.string.certif_trust), new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { @@ -130,55 +130,55 @@ public class ManageAccountActivity extends XmppActivity { .findViewById(R.id.account_status); switch (account.getStatus()) { case Account.STATUS_DISABLED: - statusView.setText("temporarily disabled"); + statusView.setText(getString(R.string.account_status_disabled)); statusView.setTextColor(0xFF1da9da); break; case Account.STATUS_ONLINE: - statusView.setText("online"); + statusView.setText(getString(R.string.account_status_online)); statusView.setTextColor(0xFF83b600); break; case Account.STATUS_CONNECTING: - statusView.setText("connecting\u2026"); + statusView.setText(getString(R.string.account_status_connecting)); statusView.setTextColor(0xFF1da9da); break; case Account.STATUS_OFFLINE: - statusView.setText("offline"); + statusView.setText(getString(R.string.account_status_offline)); statusView.setTextColor(0xFFe92727); break; case Account.STATUS_UNAUTHORIZED: - statusView.setText("unauthorized"); + statusView.setText(getString(R.string.account_status_unauthorized)); statusView.setTextColor(0xFFe92727); break; case Account.STATUS_SERVER_NOT_FOUND: - statusView.setText("server not found"); + statusView.setText(getString(R.string.account_status_not_found)); statusView.setTextColor(0xFFe92727); break; case Account.STATUS_NO_INTERNET: - statusView.setText("no internet"); + statusView.setText(getString(R.string.account_status_no_internet)); statusView.setTextColor(0xFFe92727); break; case Account.STATUS_SERVER_REQUIRES_TLS: - statusView.setText("server requires TLS"); + statusView.setText(getString(R.string.account_status_requires_tls)); statusView.setTextColor(0xFFe92727); break; case Account.STATUS_TLS_ERROR: - statusView.setText("untrusted cerficate"); + statusView.setText(getString(R.string.account_status_error)); statusView.setTextColor(0xFFe92727); break; case Account.STATUS_REGISTRATION_FAILED: - statusView.setText("registration failed"); + statusView.setText(getString(R.string.account_status_regis_fail)); statusView.setTextColor(0xFFe92727); break; case Account.STATUS_REGISTRATION_CONFLICT: - statusView.setText("username already in use"); + statusView.setText(getString(R.string.account_status_regis_conflict)); statusView.setTextColor(0xFFe92727); break; case Account.STATUS_REGISTRATION_SUCCESSFULL: - statusView.setText("registration completed"); + statusView.setText(getString(R.string.account_status_regis_success)); statusView.setTextColor(0xFF83b600); break; case Account.STATUS_REGISTRATION_NOT_SUPPORTED: - statusView.setText("server does not support registration"); + statusView.setText(getString(R.string.account_status_regis_not_sup)); statusView.setTextColor(0xFFe92727); break; default: diff --git a/src/eu/siacs/conversations/ui/ShareWithActivity.java b/src/eu/siacs/conversations/ui/ShareWithActivity.java index 1bc9fc46..e4abfd2d 100644 --- a/src/eu/siacs/conversations/ui/ShareWithActivity.java +++ b/src/eu/siacs/conversations/ui/ShareWithActivity.java @@ -17,7 +17,6 @@ import android.content.SharedPreferences; import android.graphics.Bitmap; import android.os.Bundle; import android.preference.PreferenceManager; -import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.ImageView; @@ -29,15 +28,6 @@ public class ShareWithActivity extends XmppActivity { private LinearLayout conversations; private LinearLayout contacts; - private OnClickListener click = new OnClickListener() { - - @Override - public void onClick(View v) { - // TODO Auto-generated method stub - - } - }; - @Override protected void onCreate(Bundle savedInstanceState) { @@ -71,7 +61,7 @@ public class ShareWithActivity extends XmppActivity { SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); boolean useSubject = preferences.getBoolean("use_subject_in_muc", true); - Set<String> displayedContacts = new HashSet<String>(); + Set<Contact> displayedContacts = new HashSet<Contact>(); conversations.removeAllViews(); List<Conversation> convList = xmppConnectionService.getConversations(); Collections.sort(convList, new Comparator<Conversation>() { @@ -95,15 +85,13 @@ public class ShareWithActivity extends XmppActivity { } }); conversations.addView(view); - if (conversation.getContact() != null) { - displayedContacts.add(conversation.getContact().getUuid()); - } + displayedContacts.add(conversation.getContact()); } contacts.removeAllViews(); - final List<Contact> contactsList = new ArrayList<Contact>(); + List<Contact> contactsList = new ArrayList<Contact>(); for(Account account : xmppConnectionService.getAccounts()) { - for(final Contact contact : xmppConnectionService.getRoster(account)) { - if (!displayedContacts.contains(contact.getUuid())) { + for(Contact contact : account.getRoster().getContacts()) { + if (!displayedContacts.contains(contact)&&(contact.getOption(Contact.Options.IN_ROSTER))) { contactsList.add(contact); } } diff --git a/src/eu/siacs/conversations/ui/XmppActivity.java b/src/eu/siacs/conversations/ui/XmppActivity.java index dc894ad5..02900ac8 100644 --- a/src/eu/siacs/conversations/ui/XmppActivity.java +++ b/src/eu/siacs/conversations/ui/XmppActivity.java @@ -1,7 +1,5 @@ package eu.siacs.conversations.ui; -import java.nio.channels.AlreadyConnectedException; - import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Conversation; @@ -177,7 +175,7 @@ public abstract class XmppActivity extends Activity { @Override public void success() { xmppConnectionService.databaseBackend.updateAccount(account); - xmppConnectionService.sendPgpPresence(account, account.getPgpSignature()); + xmppConnectionService.sendPresence(account); if (conversation!=null) { conversation.setNextEncryption(Message.ENCRYPTION_PGP); } diff --git a/src/eu/siacs/conversations/utils/OnPhoneContactsLoadedListener.java b/src/eu/siacs/conversations/utils/OnPhoneContactsLoadedListener.java index fa8cea04..9a689768 100644 --- a/src/eu/siacs/conversations/utils/OnPhoneContactsLoadedListener.java +++ b/src/eu/siacs/conversations/utils/OnPhoneContactsLoadedListener.java @@ -1,9 +1,9 @@ package eu.siacs.conversations.utils; -import java.util.Hashtable; +import java.util.List; import android.os.Bundle; public interface OnPhoneContactsLoadedListener { - public void onPhoneContactsLoaded(Hashtable<String, Bundle> phoneContacts); + public void onPhoneContactsLoaded(List<Bundle> phoneContacts); } diff --git a/src/eu/siacs/conversations/utils/PhoneHelper.java b/src/eu/siacs/conversations/utils/PhoneHelper.java index 6355e378..773312bb 100644 --- a/src/eu/siacs/conversations/utils/PhoneHelper.java +++ b/src/eu/siacs/conversations/utils/PhoneHelper.java @@ -1,8 +1,8 @@ package eu.siacs.conversations.utils; -import java.util.Hashtable; +import java.util.ArrayList; +import java.util.List; -import android.app.Activity; import android.content.Context; import android.content.CursorLoader; import android.content.Loader; @@ -10,21 +10,15 @@ import android.content.Loader.OnLoadCompleteListener; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; -import android.os.Looper; import android.provider.ContactsContract; import android.provider.ContactsContract.Profile; -import android.provider.MediaStore; public class PhoneHelper { public static void loadPhoneContacts(Context context, final OnPhoneContactsLoadedListener listener) { - if (Looper.myLooper() == null) { - Looper.prepare(); - } - final Looper mLooper = Looper.myLooper(); - final Hashtable<String, Bundle> phoneContacts = new Hashtable<String, Bundle>(); - + final List<Bundle> phoneContacts = new ArrayList<Bundle>(); + final String[] PROJECTION = new String[] { ContactsContract.Data._ID, ContactsContract.Data.DISPLAY_NAME, ContactsContract.Data.PHOTO_THUMBNAIL_URI, @@ -58,15 +52,14 @@ public class PhoneHelper { .getColumnIndex(ContactsContract.Data.PHOTO_THUMBNAIL_URI))); contact.putString("lookup", cursor.getString(cursor .getColumnIndex(ContactsContract.Data.LOOKUP_KEY))); - phoneContacts.put( - cursor.getString(cursor - .getColumnIndex(ContactsContract.CommonDataKinds.Im.DATA)), - contact); + + contact.putString("jid",cursor.getString(cursor + .getColumnIndex(ContactsContract.CommonDataKinds.Im.DATA))); + phoneContacts.add(contact); } if (listener != null) { listener.onPhoneContactsLoaded(phoneContacts); } - mLooper.quit(); } }); mCursorLoader.startLoading(); diff --git a/src/eu/siacs/conversations/utils/UIHelper.java b/src/eu/siacs/conversations/utils/UIHelper.java index 8baa3c25..d626d421 100644 --- a/src/eu/siacs/conversations/utils/UIHelper.java +++ b/src/eu/siacs/conversations/utils/UIHelper.java @@ -12,7 +12,6 @@ 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.entities.MucOptions; import eu.siacs.conversations.entities.MucOptions.User; import eu.siacs.conversations.ui.ConversationActivity; import eu.siacs.conversations.ui.ManageAccountActivity; @@ -27,7 +26,6 @@ import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.content.SharedPreferences; -import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; @@ -38,11 +36,9 @@ import android.net.Uri; import android.preference.PreferenceManager; import android.provider.ContactsContract.Contacts; import android.support.v4.app.NotificationCompat; -import android.support.v4.app.NotificationCompat.InboxStyle; import android.support.v4.app.TaskStackBuilder; import android.text.Html; import android.util.DisplayMetrics; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.widget.LinearLayout; @@ -54,16 +50,16 @@ public class UIHelper { private static final int FG_COLOR = 0xFFE5E5E5; private static final int TRANSPARENT = 0x00000000; - public static String readableTimeDifference(long time) { + public static String readableTimeDifference(Context context, long time) { if (time == 0) { - return "just now"; + return context.getString(R.string.just_now); } Date date = new Date(time); long difference = (System.currentTimeMillis() - time) / 1000; if (difference < 60) { - return "just now"; + return context.getString(R.string.just_now); } else if (difference < 60 * 10) { - return difference / 60 + " min ago"; + return difference / 60 + " " + context.getString(R.string.minutes_ago); } else if (difference < 60 * 60 * 24) { SimpleDateFormat sdf = new SimpleDateFormat("HH:mm",Locale.US); return sdf.format(date); @@ -234,11 +230,7 @@ public class UIHelper { } return bitmap; } - - private static Bitmap getUnknownContactPicture(String[] names, int size) { - return getUnknownContactPicture(names, size, UIHelper.BG_COLOR, UIHelper.FG_COLOR); - } - + private static Bitmap getMucContactPicture(Conversation conversation, int size, int bgColor, int fgColor) { List<User> members = conversation.getMucOptions().getUsers(); if (members.size() == 0) { @@ -255,13 +247,8 @@ public class UIHelper { public static Bitmap getContactPicture(Conversation conversation, int dpSize, Context context, boolean notification) { if(conversation.getMode() == Conversation.MODE_SINGLE) { - if (conversation.getContact() != null){ return getContactPicture(conversation.getContact(), dpSize, context, notification); - } else { - return getContactPicture(conversation.getName(false), dpSize, - context, notification); - } } else{ int fgColor = UIHelper.FG_COLOR, bgColor = (notification) ? @@ -273,10 +260,6 @@ public class UIHelper { } public static Bitmap getContactPicture(Contact contact, int dpSize, Context context, boolean notification) { - int fgColor = UIHelper.FG_COLOR, - bgColor = (notification) ? - UIHelper.BG_COLOR : UIHelper.TRANSPARENT; - String uri = contact.getProfilePhoto(); if (uri==null) { return getContactPicture(contact.getDisplayName(), dpSize, @@ -412,7 +395,7 @@ public class UIHelper { .bigText(bigText.toString())); } else { NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle(); - style.setBigContentTitle(unread.size() + " unread Conversations"); + style.setBigContentTitle(unread.size() + " " + context.getString(R.string.unread_conversations)); StringBuilder names = new StringBuilder(); for (int i = 0; i < unread.size(); ++i) { targetUuid = unread.get(i).getUuid(); @@ -424,7 +407,7 @@ public class UIHelper { style.addLine(Html.fromHtml("<b>" + unread.get(i).getName(useSubject) + "</b> " + unread.get(i).getLatestMessage().getReadableBody(context))); } - mBuilder.setContentTitle(unread.size() + " unread Conversations"); + mBuilder.setContentTitle(unread.size() + " " + context.getString(R.string.unread_conversations)); mBuilder.setContentText(names.toString()); mBuilder.setStyle(style); } @@ -518,7 +501,7 @@ public class UIHelper { public void onClick(DialogInterface dialog, int which) { contact.addOtrFingerprint(conversation.getOtrFingerprint()); msg.setVisibility(View.GONE); - activity.xmppConnectionService.updateContact(contact); + //activity.xmppConnectionService.updateContact(contact); } }); builder.setView(view); diff --git a/src/eu/siacs/conversations/utils/Validator.java b/src/eu/siacs/conversations/utils/Validator.java index fce953ae..51d8b25c 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("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$", Pattern.CASE_INSENSITIVE); + Pattern.compile("\\b^[A-Z0-9._%+-]+@([A-Z0-9.-]+\\.)?\\d{1,3}[.]\\d{1,3}[.]\\d{1,3}[.]\\d{1,3}\\b$|^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$", Pattern.CASE_INSENSITIVE); public static boolean isValidJid(String jid) { Matcher matcher = VALID_JID.matcher(jid); diff --git a/src/eu/siacs/conversations/xml/XmlReader.java b/src/eu/siacs/conversations/xml/XmlReader.java index 0a82a5d8..b4b1647e 100644 --- a/src/eu/siacs/conversations/xml/XmlReader.java +++ b/src/eu/siacs/conversations/xml/XmlReader.java @@ -85,10 +85,8 @@ public class XmlReader { public Element readElement(Tag currentTag) throws XmlPullParserException, IOException { Element element = new Element(currentTag.getName()); - //Log.d(LOGTAG,"trying to read element "+element.getName()); element.setAttributes(currentTag.getAttributes()); Tag nextTag = this.readTag(); - //Log.d(LOGTAG,"next Tag is: "+nextTag.toString()); if(nextTag.isNo()) { element.setContent(nextTag.getName()); nextTag = this.readTag(); @@ -96,15 +94,16 @@ public class XmlReader { if (nextTag == null) { throw new IOException("unterupted mid tag"); } - //Log.d(LOGTAG,"reading till the end of "+element.getName()); while(!nextTag.isEnd(element.getName())) { if (!nextTag.isNo()) { Element child = this.readElement(nextTag); element.addChild(child); } nextTag = this.readTag(); + if (nextTag == null) { + throw new IOException("unterupted mid tag"); + } } - //Log.d(LOGTAG,"return with element"+element); return element; } } diff --git a/src/eu/siacs/conversations/xmpp/XmppConnection.java b/src/eu/siacs/conversations/xmpp/XmppConnection.java index adb27ec8..0ba9677a 100644 --- a/src/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/eu/siacs/conversations/xmpp/XmppConnection.java @@ -21,7 +21,6 @@ import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.Map.Entry; import javax.net.ssl.SSLContext; @@ -31,7 +30,6 @@ import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; -import org.json.JSONException; import org.xmlpull.v1.XmlPullParserException; import android.os.Bundle; @@ -76,9 +74,7 @@ public class XmppConnection implements Runnable { private boolean shouldAuthenticate = true; private Element streamFeatures; private HashMap<String, List<String>> disco = new HashMap<String, List<String>>(); - - private HashSet<String> pendingSubscriptions = new HashSet<String>(); - + private String streamId = null; private int smVersion = 3; @@ -89,6 +85,8 @@ public class XmppConnection implements Runnable { public long lastPingSent = 0; public long lastConnect = 0; public long lastSessionStarted = 0; + + private int attempt = 0; private static final int PACKET_IQ = 0; private static final int PACKET_MESSAGE = 1; @@ -111,9 +109,12 @@ public class XmppConnection implements Runnable { protected void changeStatus(int nextStatus) { if (account.getStatus() != nextStatus) { - if ((nextStatus == Account.STATUS_OFFLINE)&&(account.getStatus() != Account.STATUS_CONNECTING)&&(account.getStatus() != Account.STATUS_ONLINE)) { + if ((nextStatus == Account.STATUS_OFFLINE)&&(account.getStatus() != Account.STATUS_CONNECTING)&&(account.getStatus() != Account.STATUS_ONLINE)&&(account.getStatus() != Account.STATUS_DISABLED)) { return; } + if (nextStatus == Account.STATUS_ONLINE) { + this.attempt = 0; + } account.setStatus(nextStatus); if (statusListener != null) { statusListener.onStatusChanged(account); @@ -124,6 +125,7 @@ public class XmppConnection implements Runnable { protected void connect() { Log.d(LOGTAG,account.getJid()+ ": connecting"); lastConnect = SystemClock.elapsedRealtime(); + this.attempt++; try { shouldAuthenticate = shouldBind = !account.isOptionSet(Account.OPTION_REGISTER); tagReader = new XmlReader(wakeLock); @@ -257,10 +259,11 @@ public class XmppConnection implements Runnable { RequestPacket r = new RequestPacket(smVersion); tagWriter.writeStanzaAsync(r); } else if (nextTag.isStart("resumed")) { + lastPaketReceived = SystemClock.elapsedRealtime(); + Log.d(LOGTAG,account.getJid()+": session resumed"); tagReader.readElement(nextTag); sendPing(); changeStatus(Account.STATUS_ONLINE); - Log.d(LOGTAG,account.getJid()+": session resumed"); } else if (nextTag.isStart("r")) { tagReader.readElement(nextTag); AckPacket ack = new AckPacket(this.stanzasReceived,smVersion); @@ -543,12 +546,6 @@ public class XmppConnection implements Runnable { this.tagWriter.writeStanzaAsync(resume); } else if (this.streamFeatures.hasChild("bind") && shouldBind) { sendBindRequest(); - if (this.streamFeatures.hasChild("session")) { - Log.d(LOGTAG,account.getJid()+": sending deprecated session"); - IqPacket startSession = new IqPacket(IqPacket.TYPE_SET); - startSession.addChild("session","urn:ietf:params:xml:ns:xmpp-session"); //setContent("") - this.sendIqPacket(startSession, null); - } } } @@ -616,25 +613,10 @@ public class XmppConnection implements Runnable { }); } - private void sendInitialPresence() { - PresencePacket packet = new PresencePacket(); - packet.setAttribute("from", account.getFullJid()); - if (account.getKeys().has("pgp_signature")) { - try { - String signature = account.getKeys().getString("pgp_signature"); - packet.addChild("status").setContent("online"); - packet.addChild("x","jabber:x:signed").setContent(signature); - } catch (JSONException e) { - // - } - } - this.sendPresencePacket(packet); - } - private void sendBindRequest() throws IOException { IqPacket iq = new IqPacket(IqPacket.TYPE_SET); iq.addChild("bind", "urn:ietf:params:xml:ns:xmpp-bind").addChild("resource").setContent(account.getResource()); - this.sendIqPacket(iq, new OnIqPacketReceived() { + this.sendUnboundIqPacket(iq, new OnIqPacketReceived() { @Override public void onIqPacketReceived(Account account, IqPacket packet) { String resource = packet.findChild("bind").findChild("jid") @@ -649,15 +631,21 @@ public class XmppConnection implements Runnable { EnablePacket enable = new EnablePacket(smVersion); tagWriter.writeStanzaAsync(enable); } - sendInitialPresence(); sendServiceDiscoveryInfo(account.getServer()); sendServiceDiscoveryItems(account.getServer()); if (bindListener !=null) { bindListener.onBind(account); } + changeStatus(Account.STATUS_ONLINE); } }); + if (this.streamFeatures.hasChild("session")) { + Log.d(LOGTAG,account.getJid()+": sending deprecated session"); + IqPacket startSession = new IqPacket(IqPacket.TYPE_SET); + startSession.addChild("session","urn:ietf:params:xml:ns:xmpp-session"); + this.sendUnboundIqPacket(startSession, null); + } } private void sendServiceDiscoveryInfo(final String server) { @@ -756,6 +744,14 @@ public class XmppConnection implements Runnable { packet.setFrom(account.getFullJid()); this.sendPacket(packet, callback); } + + public void sendUnboundIqPacket(IqPacket packet, OnIqPacketReceived callback) { + if (packet.getId()==null) { + String id = nextRandomId(); + packet.setAttribute("id", id); + } + this.sendPacket(packet, callback); + } public void sendMessagePacket(MessagePacket packet) { this.sendPacket(packet, null); @@ -907,12 +903,13 @@ public class XmppConnection implements Runnable { return findDiscoItemByFeature("http://jabber.org/protocol/muc"); } - public boolean hasPendingSubscription(String jid) { - return this.pendingSubscriptions.contains(jid); + public int getTimeToNextAttempt() { + int interval = (int) (25 * Math.pow(1.5,attempt)); + int secondsSinceLast = (int) ((SystemClock.elapsedRealtime() - this.lastConnect) / 1000); + return interval - secondsSinceLast; } - public void addPendingSubscription(String jid) { - Log.d(LOGTAG,"adding "+jid+" to pending subscriptions"); - this.pendingSubscriptions.add(jid); + public int getAttempt() { + return this.attempt; } } |