diff --git a/gen/de/gultsch/chat/R.java b/gen/de/gultsch/chat/R.java
index 6f1b50dee..857311176 100644
--- a/gen/de/gultsch/chat/R.java
+++ b/gen/de/gultsch/chat/R.java
@@ -42,24 +42,24 @@ public final class R {
public static final int section_header=0x7f02000d;
}
public static final class id {
- public static final int account_confirm_password_desc=0x7f0a0019;
- public static final int account_delete=0x7f0a002d;
- public static final int account_disable=0x7f0a002e;
- public static final int account_enable=0x7f0a002f;
+ public static final int account_confirm_password_desc=0x7f0a001c;
+ public static final int account_delete=0x7f0a0035;
+ public static final int account_disable=0x7f0a0036;
+ public static final int account_enable=0x7f0a0037;
public static final int account_jid=0x7f0a0000;
- public static final int account_list=0x7f0a0022;
- public static final int account_password=0x7f0a0016;
- public static final int account_password_confirm2=0x7f0a001a;
+ public static final int account_list=0x7f0a0027;
+ public static final int account_password=0x7f0a0019;
+ public static final int account_password_confirm2=0x7f0a001d;
public static final int account_status=0x7f0a0002;
- public static final int account_usetls=0x7f0a0017;
- public static final int action_accounts=0x7f0a002a;
- public static final int action_add=0x7f0a0026;
- public static final int action_add_account=0x7f0a002c;
- public static final int action_archive=0x7f0a0029;
- public static final int action_details=0x7f0a0028;
- public static final int action_refresh_contacts=0x7f0a0030;
- public static final int action_security=0x7f0a0027;
- public static final int action_settings=0x7f0a002b;
+ public static final int account_usetls=0x7f0a001a;
+ public static final int action_accounts=0x7f0a002f;
+ public static final int action_add=0x7f0a002b;
+ public static final int action_add_account=0x7f0a0034;
+ public static final int action_archive=0x7f0a002e;
+ public static final int action_details=0x7f0a002d;
+ public static final int action_refresh_contacts=0x7f0a0038;
+ public static final int action_security=0x7f0a002c;
+ public static final int action_settings=0x7f0a0030;
public static final int contactList=0x7f0a0006;
public static final int contact_display_name=0x7f0a0008;
public static final int contact_jid=0x7f0a0009;
@@ -76,21 +76,29 @@ public final class R {
public static final int details_jidbox=0x7f0a000f;
public static final int details_receive_presence=0x7f0a0014;
public static final int details_send_presence=0x7f0a0013;
- public static final int edit_account_register_new=0x7f0a0018;
- public static final int list=0x7f0a0020;
- public static final int message_body=0x7f0a0024;
- public static final int message_photo=0x7f0a0023;
- public static final int message_time=0x7f0a0025;
- public static final int messages_view=0x7f0a001e;
+ public static final int edit_account_register_new=0x7f0a001b;
+ public static final int encryption_choice_none=0x7f0a0031;
+ public static final int encryption_choice_otr=0x7f0a0032;
+ public static final int encryption_choice_pgp=0x7f0a0033;
+ public static final int list=0x7f0a0025;
+ public static final int message_body=0x7f0a0029;
+ public static final int message_photo=0x7f0a0028;
+ public static final int message_time=0x7f0a002a;
+ public static final int messages_view=0x7f0a0021;
public static final int new_conversation_search=0x7f0a0004;
+ public static final int new_fingerprint=0x7f0a0022;
+ public static final int otr_fingerprint=0x7f0a0023;
public static final int progressBar1=0x7f0a0003;
- public static final int selected_conversation=0x7f0a0021;
- public static final int slidingpanelayout=0x7f0a001f;
- public static final int textSendButton=0x7f0a001d;
- public static final int textView1=0x7f0a0015;
+ public static final int selected_conversation=0x7f0a0026;
+ public static final int slidingpanelayout=0x7f0a0024;
+ public static final int textSendButton=0x7f0a0020;
+ public static final int textView1=0x7f0a0018;
public static final int textView2=0x7f0a0001;
- public static final int textinput=0x7f0a001c;
- public static final int textsend=0x7f0a001b;
+ public static final int textinput=0x7f0a001f;
+ public static final int textsend=0x7f0a001e;
+ public static final int verify_otr_fingerprint=0x7f0a0016;
+ public static final int verify_otr_jid=0x7f0a0015;
+ public static final int verify_otr_yourprint=0x7f0a0017;
}
public static final class layout {
public static final int account_row=0x7f030000;
@@ -98,18 +106,20 @@ public final class R {
public static final int contact=0x7f030002;
public static final int conversation_list_row=0x7f030003;
public static final int dialog_contact_details=0x7f030004;
- public static final int edit_account_dialog=0x7f030005;
- public static final int fragment_conversation=0x7f030006;
- public static final int fragment_conversations_overview=0x7f030007;
- public static final int manage_accounts=0x7f030008;
- public static final int message_recieved=0x7f030009;
- public static final int message_sent=0x7f03000a;
+ public static final int dialog_verify_otr=0x7f030005;
+ public static final int edit_account_dialog=0x7f030006;
+ public static final int fragment_conversation=0x7f030007;
+ public static final int fragment_conversations_overview=0x7f030008;
+ public static final int manage_accounts=0x7f030009;
+ public static final int message_recieved=0x7f03000a;
+ public static final int message_sent=0x7f03000b;
}
public static final class menu {
public static final int conversations=0x7f090000;
- public static final int manageaccounts=0x7f090001;
- public static final int manageaccounts_context=0x7f090002;
- public static final int newconversation=0x7f090003;
+ public static final int encryption_choices=0x7f090001;
+ public static final int manageaccounts=0x7f090002;
+ public static final int manageaccounts_context=0x7f090003;
+ public static final int newconversation=0x7f090004;
}
public static final class string {
public static final int action_accounts=0x7f070003;
diff --git a/res/layout/dialog_verify_otr.xml b/res/layout/dialog_verify_otr.xml
new file mode 100644
index 000000000..9f389c73a
--- /dev/null
+++ b/res/layout/dialog_verify_otr.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/fragment_conversation.xml b/res/layout/fragment_conversation.xml
index 3b965af28..5db687481 100644
--- a/res/layout/fragment_conversation.xml
+++ b/res/layout/fragment_conversation.xml
@@ -53,5 +53,35 @@
android:transcriptMode="alwaysScroll"
android:listSelector="@android:color/transparent">
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/menu/encryption_choices.xml b/res/menu/encryption_choices.xml
new file mode 100644
index 000000000..ade176548
--- /dev/null
+++ b/res/menu/encryption_choices.xml
@@ -0,0 +1,17 @@
+
+
diff --git a/src/de/gultsch/chat/crypto/OtrEngine.java b/src/de/gultsch/chat/crypto/OtrEngine.java
index d994f0f9a..f63b2904b 100644
--- a/src/de/gultsch/chat/crypto/OtrEngine.java
+++ b/src/de/gultsch/chat/crypto/OtrEngine.java
@@ -124,6 +124,10 @@ public class OtrEngine implements OtrEngineHost {
return null;
}
+ public PublicKey getPublicKey() {
+ return this.keyPair.getPublic();
+ }
+
@Override
public KeyPair getLocalKeyPair(SessionID arg0) throws OtrException {
if (this.keyPair==null) {
diff --git a/src/de/gultsch/chat/entities/Account.java b/src/de/gultsch/chat/entities/Account.java
index 7f14b0901..1bced45fc 100644
--- a/src/de/gultsch/chat/entities/Account.java
+++ b/src/de/gultsch/chat/entities/Account.java
@@ -1,5 +1,10 @@
package de.gultsch.chat.entities;
+import java.security.interfaces.DSAPublicKey;
+
+import net.java.otr4j.crypto.OtrCryptoEngineImpl;
+import net.java.otr4j.crypto.OtrCryptoException;
+
import org.json.JSONException;
import org.json.JSONObject;
@@ -48,6 +53,8 @@ public class Account extends AbstractEntity{
transient OtrEngine otrEngine = null;
transient XmppConnection xmppConnection = null;
+
+ private String otrFingerprint;
public Account() {
this.uuid = "0";
@@ -177,4 +184,21 @@ public class Account extends AbstractEntity{
public String getFullJid() {
return this.getJid()+"/"+this.resource;
}
+
+ public String getOtrFingerprint() {
+ if (this.otrFingerprint == null) {
+ try {
+ DSAPublicKey pubkey = (DSAPublicKey) this.otrEngine.getPublicKey();
+ StringBuilder builder = new StringBuilder(new OtrCryptoEngineImpl().getFingerprint(pubkey));
+ builder.insert(8, " ");
+ builder.insert(17, " ");
+ builder.insert(26, " ");
+ builder.insert(35, " ");
+ this.otrFingerprint = builder.toString();
+ } catch (OtrCryptoException e) {
+
+ }
+ }
+ return this.otrFingerprint;
+ }
}
diff --git a/src/de/gultsch/chat/entities/Contact.java b/src/de/gultsch/chat/entities/Contact.java
index 9c987e090..5d5710a5c 100644
--- a/src/de/gultsch/chat/entities/Contact.java
+++ b/src/de/gultsch/chat/entities/Contact.java
@@ -1,7 +1,13 @@
package de.gultsch.chat.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 android.content.ContentValues;
import android.database.Cursor;
@@ -16,7 +22,7 @@ public class Contact extends AbstractEntity implements Serializable {
public static final String SUBSCRIPTION = "subscription";
public static final String SYSTEMACCOUNT = "systemaccount";
public static final String PHOTOURI = "photouri";
- public static final String OPENPGPKEY = "pgpkey";
+ public static final String KEYS = "pgpkey";
public static final String PRESENCES = "presences";
public static final String ACCOUNT = "accountUuid";
@@ -26,7 +32,7 @@ public class Contact extends AbstractEntity implements Serializable {
protected String subscription;
protected String systemAccount;
protected String photoUri;
- protected String openPGPKey;
+ protected JSONObject keys;
protected Presences presences = new Presences();
protected Account account;
@@ -45,7 +51,7 @@ public class Contact extends AbstractEntity implements Serializable {
public Contact(String uuid, String account, String displayName, String jid,
String subscription, String photoUri, String systemAccount,
- String pgpKey,String presences) {
+ String keys, String presences) {
this.uuid = uuid;
this.accountUuid = account;
this.displayName = displayName;
@@ -53,7 +59,14 @@ public class Contact extends AbstractEntity implements Serializable {
this.subscription = subscription;
this.photoUri = photoUri;
this.systemAccount = systemAccount;
- this.openPGPKey = pgpKey;
+ if (keys == null) {
+ keys = "";
+ }
+ try {
+ this.keys = new JSONObject(keys);
+ } catch (JSONException e) {
+ this.keys = new JSONObject();
+ }
this.presences = Presences.fromJsonString(presences);
}
@@ -84,7 +97,7 @@ public class Contact extends AbstractEntity implements Serializable {
values.put(SUBSCRIPTION, subscription);
values.put(SYSTEMACCOUNT, systemAccount);
values.put(PHOTOURI, photoUri);
- values.put(OPENPGPKEY, openPGPKey);
+ values.put(KEYS, keys.toString());
values.put(PRESENCES, presences.toJsonString());
return values;
}
@@ -97,14 +110,14 @@ public class Contact extends AbstractEntity implements Serializable {
cursor.getString(cursor.getColumnIndex(SUBSCRIPTION)),
cursor.getString(cursor.getColumnIndex(PHOTOURI)),
cursor.getString(cursor.getColumnIndex(SYSTEMACCOUNT)),
- cursor.getString(cursor.getColumnIndex(OPENPGPKEY)),
+ cursor.getString(cursor.getColumnIndex(KEYS)),
cursor.getString(cursor.getColumnIndex(PRESENCES)));
}
public void setSubscription(String subscription) {
this.subscription = subscription;
}
-
+
public String getSubscription() {
return this.subscription;
}
@@ -141,11 +154,11 @@ public class Contact extends AbstractEntity implements Serializable {
}
}
}
-
+
public Hashtable getPresences() {
return this.presences.getPresences();
}
-
+
public void updatePresence(String resource, int status) {
this.presences.updatePresence(resource, status);
}
@@ -153,19 +166,19 @@ public class Contact extends AbstractEntity implements Serializable {
public void removePresence(String resource) {
this.presences.removePresence(resource);
}
-
+
public int getMostAvailableStatus() {
return this.presences.getMostAvailableStatus();
}
public void setPresences(Presences pres) {
- this.presences = pres;
+ this.presences = pres;
}
-
+
public void setPhotoUri(String uri) {
this.photoUri = uri;
}
-
+
public void setDisplayName(String name) {
this.displayName = name;
}
@@ -173,4 +186,36 @@ public class Contact extends AbstractEntity implements Serializable {
public String getSystemAccount() {
return systemAccount;
}
+
+ public Set getOtrFingerprints() {
+ Set set = new HashSet();
+ try {
+ if (this.keys.has("otr_fingerprints")) {
+ JSONArray fingerprints = this.keys.getJSONArray("otr_fingerprints");
+ for (int i = 0; i < fingerprints.length(); ++i) {
+ set.add(fingerprints.getString(i));
+ }
+ }
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ return set;
+ }
+
+ public void addOtrFingerprint(String print) {
+ try {
+ JSONArray fingerprints;
+ if (!this.keys.has("otr_fingerprints")) {
+ fingerprints = new JSONArray();
+
+ } else {
+ fingerprints = this.keys.getJSONArray("otr_fingerprints");
+ }
+ fingerprints.put(print);
+ this.keys.put("otr_fingerprints", fingerprints);
+ } catch (JSONException e) {
+
+ }
+ }
}
diff --git a/src/de/gultsch/chat/entities/Conversation.java b/src/de/gultsch/chat/entities/Conversation.java
index 7339aadb3..343e83a44 100644
--- a/src/de/gultsch/chat/entities/Conversation.java
+++ b/src/de/gultsch/chat/entities/Conversation.java
@@ -1,5 +1,6 @@
package de.gultsch.chat.entities;
+import java.security.interfaces.DSAPublicKey;
import java.util.ArrayList;
import java.util.List;
@@ -7,6 +8,8 @@ import de.gultsch.chat.crypto.OtrEngine;
import de.gultsch.chat.xmpp.XmppConnection;
import net.java.otr4j.OtrException;
+import net.java.otr4j.crypto.OtrCryptoEngineImpl;
+import net.java.otr4j.crypto.OtrCryptoException;
import net.java.otr4j.session.SessionID;
import net.java.otr4j.session.SessionImpl;
import net.java.otr4j.session.SessionStatus;
@@ -51,7 +54,10 @@ public class Conversation extends AbstractEntity {
private transient Contact contact;
private transient SessionImpl otrSession;
- private transient String foreignOtrPresence;
+
+ private transient String otrFingerprint = null;
+
+ public int nextMessageEncryption = Message.ENCRYPTION_NONE;
public Conversation(String name, Account account,
String contactJid, int mode) {
@@ -209,6 +215,11 @@ public class Conversation extends AbstractEntity {
Log.d("xmppService","starting otr session with "+presence);
SessionID sessionId = new SessionID(this.getContactJid(),presence,"xmpp");
this.otrSession = new SessionImpl(sessionId, getAccount().getOtrEngine(context));
+ try {
+ this.otrSession.startSession();
+ } catch (OtrException e) {
+ Log.d("xmppServic","couldnt start otr");
+ }
}
public SessionImpl getOtrSession() {
@@ -225,9 +236,36 @@ public class Conversation extends AbstractEntity {
this.otrSession.endSession();
}
}
+ this.resetOtrSession();
}
- public boolean hasOtrSession() {
- return (this.otrSession!=null);
+ public boolean hasValidOtrSession() {
+ if (this.otrSession == null) {
+ return false;
+ } else {
+ String foreignPresence = this.otrSession.getSessionID().getUserID();
+ if (!getContact().getPresences().containsKey(foreignPresence)) {
+ this.resetOtrSession();
+ return false;
+ }
+ return true;
+ }
+ }
+
+ public String getOtrFingerprint() {
+ if (this.otrFingerprint == null) {
+ try {
+ DSAPublicKey remotePubKey = (DSAPublicKey) getOtrSession().getRemotePublicKey();
+ StringBuilder builder = new StringBuilder(new OtrCryptoEngineImpl().getFingerprint(remotePubKey));
+ builder.insert(8, " ");
+ builder.insert(17, " ");
+ builder.insert(26, " ");
+ builder.insert(35, " ");
+ this.otrFingerprint = builder.toString();
+ } catch (OtrCryptoException e) {
+
+ }
+ }
+ return this.otrFingerprint;
}
}
diff --git a/src/de/gultsch/chat/entities/Message.java b/src/de/gultsch/chat/entities/Message.java
index a168c645a..73a6e46cc 100644
--- a/src/de/gultsch/chat/entities/Message.java
+++ b/src/de/gultsch/chat/entities/Message.java
@@ -131,5 +131,9 @@ public class Message extends AbstractEntity {
public void setTime(long time) {
this.timeSent = time;
}
+
+ public void setEncryption(int encryption) {
+ this.encryption = encryption;
+ }
}
diff --git a/src/de/gultsch/chat/persistance/DatabaseBackend.java b/src/de/gultsch/chat/persistance/DatabaseBackend.java
index df919f728..4607883c3 100644
--- a/src/de/gultsch/chat/persistance/DatabaseBackend.java
+++ b/src/de/gultsch/chat/persistance/DatabaseBackend.java
@@ -56,7 +56,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
db.execSQL("create table " + Contact.TABLENAME + "(" + Contact.UUID
+ " TEXT PRIMARY KEY, " + Contact.ACCOUNT + " TEXT, "
+ Contact.DISPLAYNAME + " TEXT," + Contact.JID + " TEXT,"
- + Contact.PRESENCES + " TEXT, " + Contact.OPENPGPKEY
+ + Contact.PRESENCES + " TEXT, " + Contact.KEYS
+ " TEXT," + Contact.PHOTOURI + " TEXT," + Contact.SUBSCRIPTION
+ " TEXT," + Contact.SYSTEMACCOUNT + " NUMBER, "
+ "FOREIGN KEY(" + Contact.ACCOUNT + ") REFERENCES "
@@ -227,10 +227,6 @@ public class DatabaseBackend extends SQLiteOpenHelper {
}
}
}
-
- public void mergeMessageIntoConversation(Message message) {
- // select counterpart,body,(timeSent/1000)-180 as min,(timeSent/1000)+180 as max from messages where min<1392035670 and max>1392035670;
- }
public List getContacts(Account account) {
List list = new ArrayList();
diff --git a/src/de/gultsch/chat/services/XmppConnectionService.java b/src/de/gultsch/chat/services/XmppConnectionService.java
index b1c4eca50..63b894abc 100644
--- a/src/de/gultsch/chat/services/XmppConnectionService.java
+++ b/src/de/gultsch/chat/services/XmppConnectionService.java
@@ -132,7 +132,7 @@ public class XmppConnectionService extends Service {
} else {
counterPart = fullJid;
if ((runOtrCheck) && body.startsWith("?OTR")) {
- if (!conversation.hasOtrSession()) {
+ if (!conversation.hasValidOtrSession()) {
conversation.startOtrSession(
getApplicationContext(), fromParts[1]);
}
@@ -162,6 +162,9 @@ public class XmppConnectionService extends Service {
if (convChangedListener!=null) {
convChangedListener.onConversationListChanged();
}
+ } else if ((before != after) && (after == SessionStatus.FINISHED)) {
+ conversation.resetOtrSession();
+ Log.d(LOGTAG,"otr session stoped");
}
} catch (Exception e) {
Log.d(LOGTAG, "error receiving otr. resetting");
@@ -326,61 +329,18 @@ public class XmppConnectionService extends Service {
thread.start();
return connection;
}
-
- private void startOtrSession(Conversation conv) {
- Set presences = conv.getContact().getPresences()
- .keySet();
- if (presences.size() == 0) {
- Log.d(LOGTAG, "counter part isnt online. cant use otr");
- return;
- } else if (presences.size() == 1) {
- conv.startOtrSession(getApplicationContext(),
- (String) presences.toArray()[0]);
- try {
- conv.getOtrSession().startSession();
- } catch (OtrException e) {
- Log.d(LOGTAG, "couldnt actually start");
- }
- } else {
- String latestCounterpartPresence = null;
- List messages = conv.getMessages();
- for (int i = messages.size() - 1; i >= 0; --i) {
- if (messages.get(i).getStatus() == Message.STATUS_RECIEVED) {
- String[] parts = messages.get(i).getCounterpart()
- .split("/");
- if (parts.length == 2) {
- latestCounterpartPresence = parts[1];
- break;
- }
- }
- }
- if (presences.contains(latestCounterpartPresence)) {
- conv.startOtrSession(getApplicationContext(),
- latestCounterpartPresence);
- try {
- conv.getOtrSession().startSession();
- } catch (OtrException e) {
- // TODO Auto-generated catch block
- Log.d(LOGTAG, "couldnt actually start");
- }
- } else {
- Log.d(LOGTAG,
- "could not decide where to send otr connection to");
- }
- }
- }
- public void sendMessage(Account account, Message message) {
+ public void sendMessage(Account account, Message message, String presence) {
Conversation conv = message.getConversation();
boolean saveInDb = false;
boolean addToConversation = false;
if (account.getStatus() == Account.STATUS_ONLINE) {
MessagePacket packet;
if (message.getEncryption() == Message.ENCRYPTION_OTR) {
- if (!conv.hasOtrSession()) {
+ if (!conv.hasValidOtrSession()) {
//starting otr session. messages will be send later
- startOtrSession(conv);
- } else {
+ conv.startOtrSession(getApplicationContext(), presence);
+ } else if (conv.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED){
//otr session aleary exists, creating message packet accordingly
packet = prepareMessagePacket(account, message,
conv.getOtrSession());
@@ -646,6 +606,7 @@ public class XmppConnectionService extends Service {
conversation.setMode(Conversation.MODE_SINGLE);
}
this.databaseBackend.updateConversation(conversation);
+ conversation.setContact(findContact(account, conversation.getContactJid()));
} else {
String conversationName;
Contact contact = findContact(account, jid);
@@ -766,9 +727,9 @@ public class XmppConnectionService extends Service {
x.setAttribute("xmlns", "http://jabber.org/protocol/muc");
if (conversation.getMessages().size() != 0) {
Element history = new Element("history");
- history.setAttribute("seconds",
- (System.currentTimeMillis() - conversation
- .getLatestMessage().getTimeSent() / 1000) + "");
+ long lastMsgTime = conversation.getLatestMessage().getTimeSent();
+ long diff = (System.currentTimeMillis() - lastMsgTime) / 1000;
+ history.setAttribute("seconds",diff+"");
x.addChild(history);
}
packet.addChild(x);
@@ -806,4 +767,8 @@ public class XmppConnectionService extends Service {
public IBinder onBind(Intent intent) {
return mBinder;
}
+
+ public void updateContact(Contact contact) {
+ databaseBackend.updateContact(contact);
+ }
}
\ No newline at end of file
diff --git a/src/de/gultsch/chat/ui/ConversationActivity.java b/src/de/gultsch/chat/ui/ConversationActivity.java
index 011261e7a..b1464031b 100644
--- a/src/de/gultsch/chat/ui/ConversationActivity.java
+++ b/src/de/gultsch/chat/ui/ConversationActivity.java
@@ -32,6 +32,8 @@ import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
+import android.widget.PopupMenu;
+import android.widget.PopupMenu.OnMenuItemClickListener;
import android.widget.TextView;
import android.widget.ImageView;
@@ -295,6 +297,56 @@ public class ConversationActivity extends XmppActivity {
} else {
Log.d("xmppService","contact was null - means not in roster");
}
+ break;
+ case R.id.action_security:
+ final Conversation selConv = getSelectedConversation();
+ View menuItemView = findViewById(R.id.action_security);
+ PopupMenu popup = new PopupMenu(this, menuItemView);
+ final ConversationFragment fragment = (ConversationFragment) getFragmentManager().findFragmentByTag("conversation");
+ if (fragment!=null) {
+ popup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.encryption_choice_none:
+ selConv.nextMessageEncryption = Message.ENCRYPTION_NONE;
+ item.setChecked(true);
+ break;
+ case R.id.encryption_choice_otr:
+ selConv.nextMessageEncryption = Message.ENCRYPTION_OTR;
+ item.setChecked(true);
+ break;
+ case R.id.encryption_choice_pgp:
+ selConv.nextMessageEncryption = Message.ENCRYPTION_PGP;
+ item.setChecked(true);
+ break;
+ default:
+ selConv.nextMessageEncryption = Message.ENCRYPTION_NONE;
+ break;
+ }
+ fragment.updateChatMsgHint();
+ return true;
+ }
+ });
+ popup.inflate(R.menu.encryption_choices);
+ switch (selConv.nextMessageEncryption) {
+ case Message.ENCRYPTION_NONE:
+ popup.getMenu().findItem(R.id.encryption_choice_none).setChecked(true);
+ break;
+ case Message.ENCRYPTION_OTR:
+ popup.getMenu().findItem(R.id.encryption_choice_otr).setChecked(true);
+ break;
+ case Message.ENCRYPTION_PGP:
+ popup.getMenu().findItem(R.id.encryption_choice_pgp).setChecked(true);
+ break;
+ default:
+ popup.getMenu().findItem(R.id.encryption_choice_none).setChecked(true);
+ break;
+ }
+ popup.show();
+ }
+
break;
default:
break;
@@ -344,13 +396,6 @@ public class ConversationActivity extends XmppActivity {
@Override
void onBackendConnected() {
-
- if (contactInserted) {
- Log.d("xmppService","merge phone contacts with roster");
- contactInserted = false;
- xmppConnectionService.mergePhoneContactsWithRoster();
- }
-
xmppConnectionService.setOnConversationListChangedListener(this.onConvChanged);
if (conversationList.size()==0) {
diff --git a/src/de/gultsch/chat/ui/ConversationFragment.java b/src/de/gultsch/chat/ui/ConversationFragment.java
index 8ec511fc2..1f125ab07 100644
--- a/src/de/gultsch/chat/ui/ConversationFragment.java
+++ b/src/de/gultsch/chat/ui/ConversationFragment.java
@@ -1,15 +1,23 @@
package de.gultsch.chat.ui;
import java.util.ArrayList;
+import java.util.Hashtable;
import java.util.List;
+import java.util.Set;
+
+import net.java.otr4j.OtrException;
+import net.java.otr4j.session.SessionStatus;
import de.gultsch.chat.R;
import de.gultsch.chat.entities.Contact;
import de.gultsch.chat.entities.Conversation;
import de.gultsch.chat.entities.Message;
+import de.gultsch.chat.services.XmppConnectionService;
import de.gultsch.chat.utils.PhoneHelper;
import de.gultsch.chat.utils.UIHelper;
+import android.app.AlertDialog;
import android.app.Fragment;
+import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.graphics.Typeface;
import android.net.Uri;
@@ -21,23 +29,97 @@ import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.EditText;
+import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
public class ConversationFragment extends Fragment {
-
+
protected Conversation conversation;
protected ListView messagesView;
protected LayoutInflater inflater;
protected List messageList = new ArrayList();
protected ArrayAdapter messageListAdapter;
protected Contact contact;
-
+
private EditText chatMsg;
- private int nextMessageEncryption = Message.ENCRYPTION_NONE;
-
+
+ private OnClickListener sendMsgListener = new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ ConversationActivity activity = (ConversationActivity) getActivity();
+ final XmppConnectionService xmppService = activity.xmppConnectionService;
+ if (chatMsg.getText().length() < 1)
+ return;
+ final Message message = new Message(conversation, chatMsg.getText()
+ .toString(), conversation.nextMessageEncryption);
+ if (conversation.nextMessageEncryption == Message.ENCRYPTION_OTR) {
+ if (conversation.hasValidOtrSession()) {
+ activity.xmppConnectionService.sendMessage(
+ conversation.getAccount(), message, null);
+ chatMsg.setText("");
+ } else {
+ Hashtable presences = conversation
+ .getContact().getPresences();
+ if (presences.size() == 0) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(
+ getActivity());
+ builder.setTitle("Contact is offline");
+ builder.setIconAttribute(android.R.attr.alertDialogIcon);
+ builder.setMessage("Sending OTR encrypted messages to an offline contact is impossible.");
+ builder.setPositiveButton("Send plain text",
+ new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog,
+ int which) {
+ conversation.nextMessageEncryption = Message.ENCRYPTION_NONE;
+ message.setEncryption(Message.ENCRYPTION_NONE);
+ xmppService.sendMessage(
+ conversation.getAccount(),
+ message, null);
+ chatMsg.setText("");
+ }
+ });
+ builder.setNegativeButton("Cancel", null);
+ builder.create().show();
+ } else if (presences.size() == 1) {
+ xmppService.sendMessage(conversation.getAccount(),
+ message,
+ (String) presences.keySet().toArray()[0]);
+ chatMsg.setText("");
+ }
+ }
+ } else {
+ xmppService.sendMessage(conversation.getAccount(), message,
+ null);
+ chatMsg.setText("");
+ }
+ }
+ };
+
+ public void updateChatMsgHint() {
+ if (conversation.getMode() == Conversation.MODE_MULTI) {
+ chatMsg.setHint("Send message to conference");
+ } else {
+ switch (conversation.nextMessageEncryption) {
+ case Message.ENCRYPTION_NONE:
+ chatMsg.setHint("Send plain text message");
+ break;
+ case Message.ENCRYPTION_OTR:
+ chatMsg.setHint("Send OTR encrypted message");
+ break;
+ case Message.ENCRYPTION_PGP:
+ chatMsg.setHint("Send openPGP encryted messeage");
+ default:
+ break;
+ }
+ }
+ }
+
@Override
public View onCreateView(final LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
@@ -47,40 +129,32 @@ public class ConversationFragment extends Fragment {
final View view = inflater.inflate(R.layout.fragment_conversation,
container, false);
chatMsg = (EditText) view.findViewById(R.id.textinput);
- ((ImageButton) view.findViewById(R.id.textSendButton))
- .setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- ConversationActivity activity = (ConversationActivity) getActivity();
- if (chatMsg.getText().length() < 1)
- return;
- Message message = new Message(conversation, chatMsg
- .getText().toString(), nextMessageEncryption);
- activity.xmppConnectionService.sendMessage(conversation.getAccount(),message);
- chatMsg.setText("");
- }
- });
+ ImageButton sendButton = (ImageButton) view
+ .findViewById(R.id.textSendButton);
+ sendButton.setOnClickListener(this.sendMsgListener);
messagesView = (ListView) view.findViewById(R.id.messages_view);
-
+
SharedPreferences sharedPref = PreferenceManager
- .getDefaultSharedPreferences(getActivity().getApplicationContext());
- boolean showPhoneSelfContactPicture = sharedPref.getBoolean("show_phone_selfcontact_picture",true);
-
+ .getDefaultSharedPreferences(getActivity()
+ .getApplicationContext());
+ boolean showPhoneSelfContactPicture = sharedPref.getBoolean(
+ "show_phone_selfcontact_picture", true);
+
final Uri selfiUri;
if (showPhoneSelfContactPicture) {
- selfiUri = PhoneHelper.getSefliUri(getActivity());
+ selfiUri = PhoneHelper.getSefliUri(getActivity());
} else {
selfiUri = null;
}
-
+
messageListAdapter = new ArrayAdapter(this.getActivity()
- .getApplicationContext(), R.layout.message_sent, this.messageList) {
+ .getApplicationContext(), R.layout.message_sent,
+ this.messageList) {
private static final int SENT = 0;
private static final int RECIEVED = 1;
-
+
@Override
public int getViewTypeCount() {
return 2;
@@ -111,32 +185,42 @@ public class ConversationFragment extends Fragment {
break;
}
}
- ImageView imageView = (ImageView) view.findViewById(R.id.message_photo);
+ ImageView imageView = (ImageView) view
+ .findViewById(R.id.message_photo);
if (type == RECIEVED) {
- if(item.getConversation().getMode()==Conversation.MODE_SINGLE) {
+ if (item.getConversation().getMode() == Conversation.MODE_SINGLE) {
Uri uri = item.getConversation().getProfilePhotoUri();
- if (uri!=null) {
+ if (uri != null) {
imageView.setImageURI(uri);
} else {
- imageView.setImageBitmap(UIHelper.getUnknownContactPicture(item.getConversation().getName(), 200));
+ imageView.setImageBitmap(UIHelper
+ .getUnknownContactPicture(item
+ .getConversation().getName(), 200));
}
- } else if (item.getConversation().getMode()==Conversation.MODE_MULTI) {
- if (item.getCounterpart()!=null) {
- imageView.setImageBitmap(UIHelper.getUnknownContactPicture(item.getCounterpart(), 200));
+ } else if (item.getConversation().getMode() == Conversation.MODE_MULTI) {
+ if (item.getCounterpart() != null) {
+ imageView.setImageBitmap(UIHelper
+ .getUnknownContactPicture(
+ item.getCounterpart(), 200));
} else {
- imageView.setImageBitmap(UIHelper.getUnknownContactPicture(item.getConversation().getName(), 200));
+ imageView.setImageBitmap(UIHelper
+ .getUnknownContactPicture(item
+ .getConversation().getName(), 200));
}
}
} else {
- if (selfiUri!=null) {
+ if (selfiUri != null) {
imageView.setImageURI(selfiUri);
} else {
- imageView.setImageBitmap(UIHelper.getUnknownContactPicture(conversation.getAccount().getJid(),200));
+ imageView.setImageBitmap(UIHelper
+ .getUnknownContactPicture(conversation
+ .getAccount().getJid(), 200));
}
}
- TextView messageBody = (TextView) view.findViewById(R.id.message_body);
+ TextView messageBody = (TextView) view
+ .findViewById(R.id.message_body);
String body = item.getBody();
- if (body!=null) {
+ if (body != null) {
messageBody.setText(body.trim());
}
TextView time = (TextView) view.findViewById(R.id.message_time);
@@ -144,13 +228,16 @@ public class ConversationFragment extends Fragment {
time.setTypeface(null, Typeface.ITALIC);
time.setText("sending\u2026");
} else {
- time.setTypeface(null,Typeface.NORMAL);
- if ((item.getConversation().getMode()==Conversation.MODE_SINGLE)||(type != RECIEVED)) {
+ time.setTypeface(null, Typeface.NORMAL);
+ if ((item.getConversation().getMode() == Conversation.MODE_SINGLE)
+ || (type != RECIEVED)) {
time.setText(UIHelper.readableTimeDifference(item
- .getTimeSent()));
- } else {
- time.setText(item.getCounterpart()+" \u00B7 "+UIHelper.readableTimeDifference(item
.getTimeSent()));
+ } else {
+ time.setText(item.getCounterpart()
+ + " \u00B7 "
+ + UIHelper.readableTimeDifference(item
+ .getTimeSent()));
}
}
return view;
@@ -165,7 +252,7 @@ public class ConversationFragment extends Fragment {
public void onStart() {
super.onStart();
final ConversationActivity activity = (ConversationActivity) getActivity();
-
+
if (activity.xmppConnectionServiceBound) {
this.conversation = activity.getSelectedConversation();
updateMessages();
@@ -182,7 +269,7 @@ public class ConversationFragment extends Fragment {
}
}
}
-
+
public void onBackendConnected() {
final ConversationActivity activity = (ConversationActivity) getActivity();
this.conversation = activity.getSelectedConversation();
@@ -201,32 +288,50 @@ public class ConversationFragment extends Fragment {
}
public void updateMessages() {
+ ConversationActivity activity = (ConversationActivity) getActivity();
this.messageList.clear();
this.messageList.addAll(this.conversation.getMessages());
this.messageListAdapter.notifyDataSetChanged();
- if (messageList.size()>=1) {
- nextMessageEncryption = this.conversation.getLatestMessage().getEncryption();
+ if (messageList.size() >= 1) {
+ int latestEncryption = this.conversation.getLatestMessage()
+ .getEncryption();
+ conversation.nextMessageEncryption = latestEncryption;
+ makeFingerprintWarning(latestEncryption);
}
getActivity().invalidateOptionsMenu();
- switch (nextMessageEncryption) {
- case Message.ENCRYPTION_NONE:
- chatMsg.setHint("Send plain text message");
- break;
- case Message.ENCRYPTION_OTR:
- chatMsg.setHint("Send OTR encrypted message");
- break;
- case Message.ENCRYPTION_PGP:
- chatMsg.setHint("Send openPGP encryted messeage");
- default:
- break;
- }
+ updateChatMsgHint();
int size = this.messageList.size();
if (size >= 1)
messagesView.setSelection(size - 1);
- ConversationActivity activity = (ConversationActivity) getActivity();
if (!activity.shouldPaneBeOpen()) {
conversation.markRead();
activity.updateConversationList();
}
}
+
+ protected void makeFingerprintWarning(int latestEncryption) {
+ final LinearLayout fingerprintWarning = (LinearLayout) getView()
+ .findViewById(R.id.new_fingerprint);
+ Set 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);
+ }
+ }
}
diff --git a/src/de/gultsch/chat/utils/UIHelper.java b/src/de/gultsch/chat/utils/UIHelper.java
index df3f9c1cc..52292a0f4 100644
--- a/src/de/gultsch/chat/utils/UIHelper.java
+++ b/src/de/gultsch/chat/utils/UIHelper.java
@@ -7,15 +7,19 @@ import java.util.Date;
import java.util.List;
import de.gultsch.chat.R;
+import de.gultsch.chat.entities.Account;
import de.gultsch.chat.entities.Contact;
import de.gultsch.chat.entities.Conversation;
import de.gultsch.chat.entities.Message;
import de.gultsch.chat.ui.ConversationActivity;
import android.app.Activity;
+import android.app.AlertDialog;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
@@ -29,7 +33,11 @@ import android.provider.ContactsContract.Contacts;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.LinearLayout;
import android.widget.QuickContactBadge;
+import android.widget.TextView;
public class UIHelper {
public static String readableTimeDifference(long time) {
@@ -153,4 +161,33 @@ public class UIHelper {
}
}
+
+ public static AlertDialog getVerifyFingerprintDialog(final ConversationActivity activity,final Conversation conversation, final LinearLayout msg) {
+ final Contact contact = conversation.getContact();
+ final Account account = conversation.getAccount();
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+ builder.setTitle("Verify fingerprint");
+ LayoutInflater inflater = activity.getLayoutInflater();
+ View view = inflater.inflate(R.layout.dialog_verify_otr, null);
+ TextView jid = (TextView) view.findViewById(R.id.verify_otr_jid);
+ TextView fingerprint = (TextView) view.findViewById(R.id.verify_otr_fingerprint);
+ TextView yourprint = (TextView) view.findViewById(R.id.verify_otr_yourprint);
+
+ jid.setText(contact.getJid());
+ fingerprint.setText(conversation.getOtrFingerprint());
+ yourprint.setText(account.getOtrFingerprint());
+ builder.setNegativeButton("Cancel", null);
+ builder.setPositiveButton("Verify", new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ contact.addOtrFingerprint(conversation.getOtrFingerprint());
+ msg.setVisibility(View.GONE);
+ activity.xmppConnectionService.updateContact(contact);
+ }
+ });
+ builder.setView(view);
+ return builder.create();
+ }
}