basic otr support
This commit is contained in:
		
							parent
							
								
									e63109215e
								
							
						
					
					
						commit
						42c4c1789a
					
				| @ -31,14 +31,15 @@ public final class R { | ||||
|         public static final int ic_action_add_person=0x7f020002; | ||||
|         public static final int ic_action_delete=0x7f020003; | ||||
|         public static final int ic_action_refresh=0x7f020004; | ||||
|         public static final int ic_action_send=0x7f020005; | ||||
|         public static final int ic_action_send_now=0x7f020006; | ||||
|         public static final int ic_action_unsecure=0x7f020007; | ||||
|         public static final int ic_launcher=0x7f020008; | ||||
|         public static final int ic_profile=0x7f020009; | ||||
|         public static final int message_border=0x7f02000a; | ||||
|         public static final int notification=0x7f02000b; | ||||
|         public static final int section_header=0x7f02000c; | ||||
|         public static final int ic_action_secure=0x7f020005; | ||||
|         public static final int ic_action_send=0x7f020006; | ||||
|         public static final int ic_action_send_now=0x7f020007; | ||||
|         public static final int ic_action_unsecure=0x7f020008; | ||||
|         public static final int ic_launcher=0x7f020009; | ||||
|         public static final int ic_profile=0x7f02000a; | ||||
|         public static final int message_border=0x7f02000b; | ||||
|         public static final int notification=0x7f02000c; | ||||
|         public static final int section_header=0x7f02000d; | ||||
|     } | ||||
|     public static final class id { | ||||
|         public static final int account_confirm_password_desc=0x7f0a0019; | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								libs/bcprov-jdk15on-150.jar
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								libs/bcprov-jdk15on-150.jar
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								libs/otr4j-0.10.jar
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								libs/otr4j-0.10.jar
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								res/drawable-hdpi/ic_action_secure.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								res/drawable-hdpi/ic_action_secure.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 394 B | 
							
								
								
									
										
											BIN
										
									
								
								res/drawable-mdpi/ic_action_secure.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								res/drawable-mdpi/ic_action_secure.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 317 B | 
							
								
								
									
										
											BIN
										
									
								
								res/drawable-xhdpi/ic_action_secure.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								res/drawable-xhdpi/ic_action_secure.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 510 B | 
							
								
								
									
										
											BIN
										
									
								
								res/drawable-xxhdpi/ic_action_secure.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								res/drawable-xxhdpi/ic_action_secure.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 624 B | 
							
								
								
									
										229
									
								
								src/de/gultsch/chat/crypto/OtrEngine.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								src/de/gultsch/chat/crypto/OtrEngine.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,229 @@ | ||||
| package de.gultsch.chat.crypto; | ||||
| 
 | ||||
| import java.math.BigInteger; | ||||
| import java.security.KeyFactory; | ||||
| import java.security.KeyPair; | ||||
| import java.security.KeyPairGenerator; | ||||
| import java.security.NoSuchAlgorithmException; | ||||
| import java.security.PrivateKey; | ||||
| import java.security.PublicKey; | ||||
| import java.security.spec.DSAPrivateKeySpec; | ||||
| import java.security.spec.DSAPublicKeySpec; | ||||
| import java.security.spec.InvalidKeySpecException; | ||||
| 
 | ||||
| import org.json.JSONException; | ||||
| import org.json.JSONObject; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.util.Log; | ||||
| 
 | ||||
| import de.gultsch.chat.entities.Account; | ||||
| import de.gultsch.chat.persistance.DatabaseBackend; | ||||
| import de.gultsch.chat.xml.Element; | ||||
| import de.gultsch.chat.xmpp.MessagePacket; | ||||
| 
 | ||||
| import net.java.otr4j.OtrEngineHost; | ||||
| import net.java.otr4j.OtrException; | ||||
| import net.java.otr4j.OtrPolicy; | ||||
| import net.java.otr4j.OtrPolicyImpl; | ||||
| import net.java.otr4j.session.InstanceTag; | ||||
| import net.java.otr4j.session.SessionID; | ||||
| 
 | ||||
| public class OtrEngine implements OtrEngineHost { | ||||
| 	 | ||||
| 	private static final String LOGTAG = "xmppService"; | ||||
| 	 | ||||
| 	private Account account; | ||||
| 	private OtrPolicy otrPolicy; | ||||
| 	private KeyPair keyPair; | ||||
| 	private Context context; | ||||
| 
 | ||||
| 	public OtrEngine(Context context, Account account) { | ||||
| 		this.account = account; | ||||
| 		this.otrPolicy = new OtrPolicyImpl(); | ||||
| 		this.otrPolicy.setAllowV1(false); | ||||
| 		this.otrPolicy.setAllowV2(true); | ||||
| 		this.otrPolicy.setAllowV3(true); | ||||
| 		this.keyPair = loadKey(account.getKeys()); | ||||
| 	} | ||||
| 	 | ||||
| 	private KeyPair loadKey(JSONObject keys) { | ||||
| 		if (keys == null) { | ||||
| 			return null; | ||||
| 		} | ||||
| 		try { | ||||
| 			BigInteger x = new BigInteger(keys.getString("otr_x"),16); | ||||
| 			BigInteger y = new BigInteger(keys.getString("otr_y"),16); | ||||
| 			BigInteger p = new BigInteger(keys.getString("otr_p"),16); | ||||
| 			BigInteger q = new BigInteger(keys.getString("otr_q"),16); | ||||
| 			BigInteger g = new BigInteger(keys.getString("otr_g"),16); | ||||
| 			KeyFactory keyFactory = KeyFactory.getInstance("DSA"); | ||||
| 			DSAPublicKeySpec pubKeySpec = new DSAPublicKeySpec(y, p, q, g); | ||||
| 			DSAPrivateKeySpec privateKeySpec = new DSAPrivateKeySpec(x, p, q, g); | ||||
| 			PublicKey publicKey = keyFactory.generatePublic(pubKeySpec); | ||||
| 			PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec); | ||||
| 			return new KeyPair(publicKey, privateKey); | ||||
| 		} catch (JSONException e) { | ||||
| 			// TODO Auto-generated catch block | ||||
| 			e.printStackTrace(); | ||||
| 		} catch (NoSuchAlgorithmException e) { | ||||
| 			// TODO Auto-generated catch block | ||||
| 			e.printStackTrace(); | ||||
| 		} catch (InvalidKeySpecException e) { | ||||
| 			// TODO Auto-generated catch block | ||||
| 			e.printStackTrace(); | ||||
| 		} | ||||
| 		return null; | ||||
| 	} | ||||
| 	 | ||||
| 	private void saveKey() { | ||||
| 		PublicKey publicKey = keyPair.getPublic(); | ||||
| 		PrivateKey privateKey = keyPair.getPrivate(); | ||||
| 		KeyFactory keyFactory; | ||||
| 		try { | ||||
| 			keyFactory = KeyFactory.getInstance("DSA"); | ||||
| 			DSAPrivateKeySpec privateKeySpec = keyFactory.getKeySpec(privateKey, DSAPrivateKeySpec.class); | ||||
| 			DSAPublicKeySpec publicKeySpec = keyFactory.getKeySpec(publicKey, DSAPublicKeySpec.class); | ||||
| 			this.account.setKey("otr_x",privateKeySpec.getX().toString(16)); | ||||
| 			this.account.setKey("otr_g",privateKeySpec.getG().toString(16)); | ||||
| 			this.account.setKey("otr_p",privateKeySpec.getP().toString(16)); | ||||
| 			this.account.setKey("otr_q",privateKeySpec.getQ().toString(16)); | ||||
| 			this.account.setKey("otr_y",publicKeySpec.getY().toString(16)); | ||||
| 		} catch (NoSuchAlgorithmException e) { | ||||
| 			e.printStackTrace(); | ||||
| 		} catch (InvalidKeySpecException e) { | ||||
| 			e.printStackTrace(); | ||||
| 		} catch (JSONException e) { | ||||
| 			e.printStackTrace(); | ||||
| 		} | ||||
| 		 | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void askForSecret(SessionID arg0, InstanceTag arg1, String arg2) { | ||||
| 		// TODO Auto-generated method stub | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void finishedSessionMessage(SessionID arg0, String arg1) | ||||
| 			throws OtrException { | ||||
| 		// TODO Auto-generated method stub | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public String getFallbackMessage(SessionID arg0) { | ||||
| 		// TODO Auto-generated method stub | ||||
| 		return null; | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public byte[] getLocalFingerprintRaw(SessionID arg0) { | ||||
| 		// TODO Auto-generated method stub | ||||
| 		return null; | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public KeyPair getLocalKeyPair(SessionID arg0) throws OtrException { | ||||
| 		if (this.keyPair==null) { | ||||
| 			KeyPairGenerator kg; | ||||
| 			try { | ||||
| 			kg = KeyPairGenerator.getInstance("DSA"); | ||||
| 			this.keyPair = kg.genKeyPair(); | ||||
| 			this.saveKey(); | ||||
| 			DatabaseBackend.getInstance(context).updateAccount(account); | ||||
| 			} catch (NoSuchAlgorithmException e) { | ||||
| 				Log.d(LOGTAG,"error generating key pair "+e.getMessage()); | ||||
| 			} | ||||
| 		} | ||||
| 		return this.keyPair; | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public String getReplyForUnreadableMessage(SessionID arg0) { | ||||
| 		// TODO Auto-generated method stub | ||||
| 		return null; | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public OtrPolicy getSessionPolicy(SessionID arg0) { | ||||
| 		return otrPolicy; | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void injectMessage(SessionID session, String body) throws OtrException { | ||||
| 		MessagePacket packet = new MessagePacket(); | ||||
| 		packet.setFrom(account.getFullJid()); //sender | ||||
| 		packet.setTo(session.getAccountID()+"/"+session.getUserID()); //reciepient | ||||
| 		packet.setBody(body); | ||||
| 		Element privateTag = new Element("private"); | ||||
| 		privateTag.setAttribute("xmlns","urn:xmpp:carbons:2"); | ||||
| 		packet.addChild(privateTag); | ||||
| 		account.getXmppConnection().sendMessagePacket(packet); | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void messageFromAnotherInstanceReceived(SessionID arg0) { | ||||
| 		// TODO Auto-generated method stub | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void multipleInstancesDetected(SessionID arg0) { | ||||
| 		// TODO Auto-generated method stub | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void requireEncryptedMessage(SessionID arg0, String arg1) | ||||
| 			throws OtrException { | ||||
| 		// TODO Auto-generated method stub | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void showError(SessionID arg0, String arg1) throws OtrException { | ||||
| 		// TODO Auto-generated method stub | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void smpAborted(SessionID arg0) throws OtrException { | ||||
| 		// TODO Auto-generated method stub | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void smpError(SessionID arg0, int arg1, boolean arg2) | ||||
| 			throws OtrException { | ||||
| 		// TODO Auto-generated method stub | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void unencryptedMessageReceived(SessionID arg0, String arg1) | ||||
| 			throws OtrException { | ||||
| 		// TODO Auto-generated method stub | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void unreadableMessageReceived(SessionID arg0) throws OtrException { | ||||
| 		// TODO Auto-generated method stub | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void unverify(SessionID arg0, String arg1) { | ||||
| 		// TODO Auto-generated method stub | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void verify(SessionID arg0, String arg1, boolean arg2) { | ||||
| 		// TODO Auto-generated method stub | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| @ -1,7 +1,14 @@ | ||||
| package de.gultsch.chat.entities; | ||||
| 
 | ||||
| import org.json.JSONException; | ||||
| import org.json.JSONObject; | ||||
| 
 | ||||
| import de.gultsch.chat.crypto.OtrEngine; | ||||
| import de.gultsch.chat.xmpp.XmppConnection; | ||||
| import android.content.ContentValues; | ||||
| import android.content.Context; | ||||
| import android.database.Cursor; | ||||
| import android.util.JsonReader; | ||||
| import android.util.Log; | ||||
| 
 | ||||
| public class Account  extends AbstractEntity{ | ||||
| @ -15,6 +22,7 @@ public class Account  extends AbstractEntity{ | ||||
| 	public static final String PASSWORD = "password"; | ||||
| 	public static final String OPTIONS = "options"; | ||||
| 	public static final String ROSTERVERSION = "rosterversion"; | ||||
| 	public static final String KEYS = "keys"; | ||||
| 	 | ||||
| 	public static final int OPTION_USETLS = 0; | ||||
| 	public static final int OPTION_DISABLED = 1; | ||||
| @ -34,23 +42,32 @@ public class Account  extends AbstractEntity{ | ||||
| 	protected String rosterVersion; | ||||
| 	protected String resource; | ||||
| 	protected int status = 0; | ||||
| 	protected JSONObject keys = new JSONObject(); | ||||
| 	 | ||||
| 	protected boolean online = false; | ||||
| 	 | ||||
| 	transient OtrEngine otrEngine = null; | ||||
| 	transient XmppConnection xmppConnection = null; | ||||
| 	 | ||||
| 	public Account() { | ||||
| 		this.uuid = "0"; | ||||
| 	} | ||||
| 	 | ||||
| 	public Account(String username, String server, String password) { | ||||
| 		this(java.util.UUID.randomUUID().toString(),username,server,password,0,null); | ||||
| 		this(java.util.UUID.randomUUID().toString(),username,server,password,0,null,""); | ||||
| 	} | ||||
| 	public Account(String uuid, String username, String server,String password, int options, String rosterVersion) { | ||||
| 	public Account(String uuid, String username, String server,String password, int options, String rosterVersion, String keys) { | ||||
| 		this.uuid = uuid; | ||||
| 		this.username = username; | ||||
| 		this.server = server; | ||||
| 		this.password = password; | ||||
| 		this.options = options; | ||||
| 		this.rosterVersion = rosterVersion; | ||||
| 		try { | ||||
| 			this.keys = new JSONObject(keys); | ||||
| 		} catch (JSONException e) { | ||||
| 			 | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	public boolean isOptionSet(int option) { | ||||
| @ -108,6 +125,14 @@ public class Account  extends AbstractEntity{ | ||||
| 	public String getJid() { | ||||
| 		return username+"@"+server; | ||||
| 	} | ||||
| 	 | ||||
| 	public JSONObject getKeys() { | ||||
| 		return keys; | ||||
| 	} | ||||
| 	 | ||||
| 	public void setKey(String keyName, String keyValue) throws JSONException { | ||||
| 		this.keys.put(keyName, keyValue); | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public ContentValues getContentValues() { | ||||
| @ -117,6 +142,8 @@ public class Account  extends AbstractEntity{ | ||||
| 		values.put(SERVER, server); | ||||
| 		values.put(PASSWORD, password); | ||||
| 		values.put(OPTIONS,options); | ||||
| 		values.put(KEYS,this.keys.toString()); | ||||
| 		values.put(ROSTERVERSION,rosterVersion); | ||||
| 		return values; | ||||
| 	} | ||||
| 	 | ||||
| @ -126,8 +153,28 @@ public class Account  extends AbstractEntity{ | ||||
| 				cursor.getString(cursor.getColumnIndex(SERVER)), | ||||
| 				cursor.getString(cursor.getColumnIndex(PASSWORD)), | ||||
| 				cursor.getInt(cursor.getColumnIndex(OPTIONS)), | ||||
| 				cursor.getString(cursor.getColumnIndex(ROSTERVERSION)) | ||||
| 				cursor.getString(cursor.getColumnIndex(ROSTERVERSION)), | ||||
| 				cursor.getString(cursor.getColumnIndex(KEYS)) | ||||
| 				); | ||||
| 	} | ||||
| 
 | ||||
| 	 | ||||
| 	public OtrEngine getOtrEngine(Context context) { | ||||
| 		if (otrEngine==null) { | ||||
| 			otrEngine = new OtrEngine(context,this); | ||||
| 		} | ||||
| 		return this.otrEngine; | ||||
| 	} | ||||
| 
 | ||||
| 	public XmppConnection getXmppConnection() { | ||||
| 		return this.xmppConnection; | ||||
| 	} | ||||
| 
 | ||||
| 	public void setXmppConnection(XmppConnection connection) { | ||||
| 		this.xmppConnection = connection; | ||||
| 	} | ||||
| 
 | ||||
| 	public String getFullJid() { | ||||
| 		return this.getJid()+"/"+this.resource; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -3,7 +3,16 @@ package de.gultsch.chat.entities; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| 
 | ||||
| import de.gultsch.chat.crypto.OtrEngine; | ||||
| import de.gultsch.chat.xmpp.XmppConnection; | ||||
| 
 | ||||
| import net.java.otr4j.OtrException; | ||||
| import net.java.otr4j.session.SessionID; | ||||
| import net.java.otr4j.session.SessionImpl; | ||||
| import net.java.otr4j.session.SessionStatus; | ||||
| 
 | ||||
| import android.content.ContentValues; | ||||
| import android.content.Context; | ||||
| import android.database.Cursor; | ||||
| import android.net.Uri; | ||||
| import android.util.Log; | ||||
| @ -40,6 +49,9 @@ public class Conversation extends AbstractEntity { | ||||
| 	private transient List<Message> messages = null; | ||||
| 	private transient Account account = null; | ||||
| 	private transient Contact contact; | ||||
| 	 | ||||
| 	private transient SessionImpl otrSession; | ||||
| 	private transient String foreignOtrPresence; | ||||
| 
 | ||||
| 	public Conversation(String name, Account account, | ||||
| 			String contactJid, int mode) { | ||||
| @ -85,19 +97,13 @@ public class Conversation extends AbstractEntity { | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	public String getLatestMessage() { | ||||
| 	public Message getLatestMessage() { | ||||
| 		if ((this.messages == null)||(this.messages.size()==0)) { | ||||
| 			return null; | ||||
| 			Message message = new Message(this,"",Message.ENCRYPTION_NONE); | ||||
| 			message.setTime(0); | ||||
| 			return message; | ||||
| 		} else { | ||||
| 			return this.messages.get(this.messages.size() - 1).getBody(); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	public long getLatestMessageDate() { | ||||
| 		if ((this.messages == null)||(this.messages.size()==0)) { | ||||
| 			return this.getCreated(); | ||||
| 		} else { | ||||
| 			return this.messages.get(this.messages.size() - 1).getTimeSent(); | ||||
| 			return this.messages.get(this.messages.size() - 1); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| @ -198,4 +204,30 @@ public class Conversation extends AbstractEntity { | ||||
| 	public void setMode(int mode) { | ||||
| 		this.mode = mode; | ||||
| 	} | ||||
| 	 | ||||
| 	public void startOtrSession(Context context, String presence) { | ||||
| 		Log.d("xmppService","starting otr session with "+presence); | ||||
| 		SessionID sessionId = new SessionID(this.getContactJid(),presence,"xmpp"); | ||||
| 		this.otrSession = new SessionImpl(sessionId, getAccount().getOtrEngine(context)); | ||||
| 	} | ||||
| 	 | ||||
| 	public SessionImpl getOtrSession() { | ||||
| 		return this.otrSession; | ||||
| 	} | ||||
| 
 | ||||
| 	public void resetOtrSession() { | ||||
| 		this.otrSession = null; | ||||
| 	} | ||||
| 	 | ||||
| 	public void endOtrIfNeeded() throws OtrException { | ||||
| 		if (this.otrSession!=null) { | ||||
| 			if (this.otrSession.getSessionStatus() == SessionStatus.ENCRYPTED) { | ||||
| 				this.otrSession.endSession(); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	public boolean hasOtrSession() { | ||||
| 		return (this.otrSession!=null); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -35,7 +35,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { | ||||
| 				+ " TEXT PRIMARY KEY," + Account.USERNAME + " TEXT," | ||||
| 				+ Account.SERVER + " TEXT," + Account.PASSWORD + " TEXT," | ||||
| 				+ Account.ROSTERVERSION + " TEXT," + Account.OPTIONS | ||||
| 				+ " NUMBER)"); | ||||
| 				+ " NUMBER, "+Account.KEYS+" TEXT)"); | ||||
| 		db.execSQL("create table " + Conversation.TABLENAME + " (" | ||||
| 				+ Conversation.UUID + " TEXT PRIMARY KEY, " + Conversation.NAME | ||||
| 				+ " TEXT, " + Conversation.CONTACT + " TEXT, " | ||||
|  | ||||
| @ -6,6 +6,12 @@ import java.util.ArrayList; | ||||
| import java.util.Date; | ||||
| import java.util.Hashtable; | ||||
| import java.util.List; | ||||
| import java.util.Set; | ||||
| 
 | ||||
| import net.java.otr4j.OtrException; | ||||
| import net.java.otr4j.session.Session; | ||||
| import net.java.otr4j.session.SessionImpl; | ||||
| import net.java.otr4j.session.SessionStatus; | ||||
| 
 | ||||
| import de.gultsch.chat.entities.Account; | ||||
| import de.gultsch.chat.entities.Contact; | ||||
| @ -50,8 +56,6 @@ public class XmppConnectionService extends Service { | ||||
| 	private List<Account> accounts; | ||||
| 	private List<Conversation> conversations = null; | ||||
| 
 | ||||
| 	private Hashtable<Account, XmppConnection> connections = new Hashtable<Account, XmppConnection>(); | ||||
| 
 | ||||
| 	private OnConversationListChangedListener convChangedListener = null; | ||||
| 	private OnAccountListChangedListener accountChangedListener = null; | ||||
| 
 | ||||
| @ -73,7 +77,9 @@ public class XmppConnectionService extends Service { | ||||
| 			if ((packet.getType() == MessagePacket.TYPE_CHAT) | ||||
| 					|| (packet.getType() == MessagePacket.TYPE_GROUPCHAT)) { | ||||
| 				boolean notify = true; | ||||
| 				boolean runOtrCheck = false; | ||||
| 				int status = Message.STATUS_RECIEVED; | ||||
| 				int encryption = Message.ENCRYPTION_NONE; | ||||
| 				String body; | ||||
| 				String fullJid; | ||||
| 				if (!packet.hasChild("body")) { | ||||
| @ -106,6 +112,7 @@ public class XmppConnectionService extends Service { | ||||
| 				} else { | ||||
| 					fullJid = packet.getFrom(); | ||||
| 					body = packet.getBody(); | ||||
| 					runOtrCheck = true; | ||||
| 				} | ||||
| 				Conversation conversation = null; | ||||
| 				String[] fromParts = fullJid.split("/"); | ||||
| @ -124,9 +131,51 @@ public class XmppConnectionService extends Service { | ||||
| 					} | ||||
| 				} else { | ||||
| 					counterPart = fullJid; | ||||
| 					if ((runOtrCheck) && body.startsWith("?OTR")) { | ||||
| 						if (!conversation.hasOtrSession()) { | ||||
| 							conversation.startOtrSession( | ||||
| 									getApplicationContext(), fromParts[1]); | ||||
| 						} | ||||
| 						try { | ||||
| 							Session otrSession = conversation.getOtrSession(); | ||||
| 							SessionStatus before = otrSession | ||||
| 									.getSessionStatus(); | ||||
| 							body = otrSession.transformReceiving(body); | ||||
| 							SessionStatus after = otrSession.getSessionStatus(); | ||||
| 							if ((before != after) | ||||
| 									&& (after == SessionStatus.ENCRYPTED)) { | ||||
| 								Log.d(LOGTAG, "otr session etablished"); | ||||
| 								List<Message> messages = conversation | ||||
| 										.getMessages(); | ||||
| 								for (int i = 0; i < messages.size(); ++i) { | ||||
| 									Message msg = messages.get(i); | ||||
| 									if ((msg.getStatus() == Message.STATUS_UNSEND) | ||||
| 											&& (msg.getEncryption() == Message.ENCRYPTION_OTR)) { | ||||
| 										MessagePacket outPacket = prepareMessagePacket( | ||||
| 												account, msg, otrSession); | ||||
| 										msg.setStatus(Message.STATUS_SEND); | ||||
| 										databaseBackend.updateMessage(msg); | ||||
| 										account.getXmppConnection() | ||||
| 												.sendMessagePacket(outPacket); | ||||
| 									} | ||||
| 								} | ||||
| 								if (convChangedListener!=null) { | ||||
| 									convChangedListener.onConversationListChanged(); | ||||
| 								} | ||||
| 							} | ||||
| 						} catch (Exception e) { | ||||
| 							Log.d(LOGTAG, "error receiving otr. resetting"); | ||||
| 							conversation.resetOtrSession(); | ||||
| 							return; | ||||
| 						} | ||||
| 						if (body == null) { | ||||
| 							return; | ||||
| 						} | ||||
| 						encryption = Message.ENCRYPTION_OTR; | ||||
| 					} | ||||
| 				} | ||||
| 				Message message = new Message(conversation, counterPart, body, | ||||
| 						Message.ENCRYPTION_NONE, status); | ||||
| 						encryption, status); | ||||
| 				if (packet.hasChild("delay")) { | ||||
| 					try { | ||||
| 						String stamp = packet.findChild("delay").getAttribute( | ||||
| @ -169,14 +218,14 @@ public class XmppConnectionService extends Service { | ||||
| 				databaseBackend.clearPresences(account); | ||||
| 				connectMultiModeConversations(account); | ||||
| 				List<Conversation> conversations = getConversations(); | ||||
|  				for(int i = 0; i < conversations.size(); ++i) { | ||||
|  					if (conversations.get(i).getAccount()==account) { | ||||
|  						sendUnsendMessages(conversations.get(i)); | ||||
|  					} | ||||
|  				} | ||||
|  				if (convChangedListener!=null) { | ||||
|  					convChangedListener.onConversationListChanged(); | ||||
|  				} | ||||
| 				for (int i = 0; i < conversations.size(); ++i) { | ||||
| 					if (conversations.get(i).getAccount() == account) { | ||||
| 						sendUnsendMessages(conversations.get(i)); | ||||
| 					} | ||||
| 				} | ||||
| 				if (convChangedListener != null) { | ||||
| 					convChangedListener.onConversationListChanged(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	}; | ||||
| @ -216,8 +265,19 @@ public class XmppConnectionService extends Service { | ||||
| 					databaseBackend.updateContact(contact); | ||||
| 				} | ||||
| 			} | ||||
| 			replaceContactInConversation(contact); | ||||
| 		} | ||||
| 	}; | ||||
| 	 | ||||
| 	private void replaceContactInConversation(Contact contact) { | ||||
| 		List<Conversation> conversations = getConversations(); | ||||
| 		for(int i = 0; i < conversations.size(); ++i) { | ||||
| 			if (conversations.get(i).getContact().equals(contact)) { | ||||
| 				conversations.get(i).setContact(contact); | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	public class XmppConnectionBinder extends Binder { | ||||
| 		public XmppConnectionService getService() { | ||||
| @ -228,13 +288,9 @@ public class XmppConnectionService extends Service { | ||||
| 	@Override | ||||
| 	public int onStartCommand(Intent intent, int flags, int startId) { | ||||
| 		for (Account account : accounts) { | ||||
| 			if (!connections.containsKey(account)) { | ||||
| 			if (account.getXmppConnection() == null) { | ||||
| 				if (!account.isOptionSet(Account.OPTION_DISABLED)) { | ||||
| 					this.connections.put(account, | ||||
| 							this.createConnection(account)); | ||||
| 				} else { | ||||
| 					Log.d(LOGTAG, account.getJid() | ||||
| 							+ ": not starting because it's disabled"); | ||||
| 					account.setXmppConnection(this.createConnection(account)); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| @ -250,6 +306,16 @@ public class XmppConnectionService extends Service { | ||||
| 				ContactsContract.Contacts.CONTENT_URI, true, contactObserver); | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void onDestroy() { | ||||
| 		super.onDestroy(); | ||||
| 		for (Account account : accounts) { | ||||
| 			if (account.getXmppConnection() != null) { | ||||
| 				disconnect(account); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	public XmppConnection createConnection(Account account) { | ||||
| 		PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); | ||||
| 		XmppConnection connection = new XmppConnection(account, pm); | ||||
| @ -261,40 +327,105 @@ public class XmppConnectionService extends Service { | ||||
| 		return connection; | ||||
| 	} | ||||
| 
 | ||||
| 	public void sendMessage(Account account, Message message) { | ||||
| 
 | ||||
| 		if (account.getStatus() == Account.STATUS_ONLINE) { | ||||
| 			MessagePacket packet = prepareMessagePacket(account, message); | ||||
| 			connections.get(account).sendMessagePacket(packet); | ||||
| 			if (message.getConversation().getMode() == Conversation.MODE_SINGLE) { | ||||
| 				message.setStatus(Message.STATUS_SEND); | ||||
| 				if (message.getConversation().getMode() == Conversation.MODE_SINGLE) { | ||||
| 					databaseBackend.createMessage(message); | ||||
| 					message.getConversation().getMessages().add(message); | ||||
| 					if (convChangedListener!=null) { | ||||
| 						convChangedListener.onConversationListChanged(); | ||||
| 	private void startOtrSession(Conversation conv) { | ||||
| 		Set<String> 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<Message> 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) { | ||||
| 		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()) { | ||||
| 					//starting otr session. messages will be send later | ||||
| 					startOtrSession(conv); | ||||
| 				} else { | ||||
| 					//otr session aleary exists, creating message packet accordingly | ||||
| 					packet = prepareMessagePacket(account, message, | ||||
| 							conv.getOtrSession()); | ||||
| 					account.getXmppConnection().sendMessagePacket(packet); | ||||
| 					message.setStatus(Message.STATUS_SEND); | ||||
| 				} | ||||
| 				saveInDb = true; | ||||
| 				addToConversation = true; | ||||
| 			} else { | ||||
| 				// don't encrypt | ||||
| 				if (message.getConversation().getMode() == Conversation.MODE_SINGLE) { | ||||
| 					message.setStatus(Message.STATUS_SEND); | ||||
| 					saveInDb = true; | ||||
| 					addToConversation = true; | ||||
| 				} | ||||
| 				 | ||||
| 				packet = prepareMessagePacket(account, message, null); | ||||
| 				account.getXmppConnection().sendMessagePacket(packet); | ||||
| 			} | ||||
| 		} else { | ||||
| 			message.getConversation().getMessages().add(message); | ||||
| 			// account is offline | ||||
| 			saveInDb = true; | ||||
| 			addToConversation = true; | ||||
| 
 | ||||
| 		} | ||||
| 		if (saveInDb) { | ||||
| 			databaseBackend.createMessage(message); | ||||
| 			if (convChangedListener!=null) { | ||||
| 		} | ||||
| 		if (addToConversation) { | ||||
| 			conv.getMessages().add(message); | ||||
| 			if (convChangedListener != null) { | ||||
| 				convChangedListener.onConversationListChanged(); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	private void sendUnsendMessages(Conversation conversation) { | ||||
| 		for (int i = 0; i < conversation.getMessages().size(); ++i) { | ||||
| 			if (conversation.getMessages().get(i).getStatus() == Message.STATUS_UNSEND) { | ||||
| 				Message message = conversation.getMessages() | ||||
| 						.get(i); | ||||
| 				Message message = conversation.getMessages().get(i); | ||||
| 				MessagePacket packet = prepareMessagePacket( | ||||
| 						conversation.getAccount(),message); | ||||
| 				connections.get(conversation.getAccount()).sendMessagePacket( | ||||
| 						packet); | ||||
| 						conversation.getAccount(), message, null); | ||||
| 				conversation.getAccount().getXmppConnection() | ||||
| 						.sendMessagePacket(packet); | ||||
| 				message.setStatus(Message.STATUS_SEND); | ||||
| 				if (conversation.getMode() == Conversation.MODE_SINGLE) { | ||||
| 					databaseBackend.updateMessage(message); | ||||
| @ -307,16 +438,37 @@ public class XmppConnectionService extends Service { | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	private MessagePacket prepareMessagePacket(Account account, Message message) { | ||||
| 	private MessagePacket prepareMessagePacket(Account account, | ||||
| 			Message message, Session otrSession) { | ||||
| 		MessagePacket packet = new MessagePacket(); | ||||
| 		if (message.getConversation().getMode() == Conversation.MODE_SINGLE) { | ||||
| 			packet.setType(MessagePacket.TYPE_CHAT); | ||||
| 			if (otrSession != null) { | ||||
| 				try { | ||||
| 					packet.setBody(otrSession.transformSending(message | ||||
| 							.getBody())); | ||||
| 				} catch (OtrException e) { | ||||
| 					Log.d(LOGTAG, | ||||
| 							account.getJid() | ||||
| 									+ ": could not encrypt message to " | ||||
| 									+ message.getCounterpart()); | ||||
| 				} | ||||
| 				Element privateMarker = new Element("private"); | ||||
| 				privateMarker.setAttribute("xmlns", "urn:xmpp:carbons:2"); | ||||
| 				packet.addChild(privateMarker); | ||||
| 				packet.setTo(otrSession.getSessionID().getAccountID()+"/"+otrSession.getSessionID().getUserID()); | ||||
| 				packet.setFrom(account.getFullJid()); | ||||
| 			} else { | ||||
| 				packet.setBody(message.getBody()); | ||||
| 				packet.setTo(message.getCounterpart()); | ||||
| 				packet.setFrom(account.getJid()); | ||||
| 			} | ||||
| 		} else if (message.getConversation().getMode() == Conversation.MODE_MULTI) { | ||||
| 			packet.setType(MessagePacket.TYPE_GROUPCHAT); | ||||
| 			packet.setBody(message.getBody()); | ||||
| 			packet.setTo(message.getCounterpart()); | ||||
| 			packet.setFrom(account.getJid()); | ||||
| 		} | ||||
| 		packet.setTo(message.getCounterpart()); | ||||
| 		packet.setFrom(account.getJid()); | ||||
| 		packet.setBody(message.getBody()); | ||||
| 		return packet; | ||||
| 	} | ||||
| 
 | ||||
| @ -345,7 +497,7 @@ public class XmppConnectionService extends Service { | ||||
| 						query.setAttribute("xmlns", "jabber:iq:roster"); | ||||
| 						query.setAttribute("ver", ""); | ||||
| 						iqPacket.addChild(query); | ||||
| 						connections.get(account).sendIqPacket(iqPacket, | ||||
| 						account.getXmppConnection().sendIqPacket(iqPacket, | ||||
| 								new OnIqPacketReceived() { | ||||
| 
 | ||||
| 									@Override | ||||
| @ -488,7 +640,7 @@ public class XmppConnectionService extends Service { | ||||
| 			if (muc) { | ||||
| 				conversation.setMode(Conversation.MODE_MULTI); | ||||
| 				if (account.getStatus() == Account.STATUS_ONLINE) { | ||||
| 					joinMuc(account, conversation); | ||||
| 					joinMuc(conversation); | ||||
| 				} | ||||
| 			} else { | ||||
| 				conversation.setMode(Conversation.MODE_SINGLE); | ||||
| @ -506,7 +658,7 @@ public class XmppConnectionService extends Service { | ||||
| 				conversation = new Conversation(conversationName, account, jid, | ||||
| 						Conversation.MODE_MULTI); | ||||
| 				if (account.getStatus() == Account.STATUS_ONLINE) { | ||||
| 					joinMuc(account, conversation); | ||||
| 					joinMuc(conversation); | ||||
| 				} | ||||
| 			} else { | ||||
| 				conversation = new Conversation(conversationName, account, jid, | ||||
| @ -523,6 +675,17 @@ public class XmppConnectionService extends Service { | ||||
| 	} | ||||
| 
 | ||||
| 	public void archiveConversation(Conversation conversation) { | ||||
| 		if (conversation.getMode() == Conversation.MODE_MULTI) { | ||||
| 			leaveMuc(conversation); | ||||
| 		} else { | ||||
| 			try { | ||||
| 				conversation.endOtrIfNeeded(); | ||||
| 			} catch (OtrException e) { | ||||
| 				Log.d(LOGTAG, | ||||
| 						"error ending otr session for " | ||||
| 								+ conversation.getName()); | ||||
| 			} | ||||
| 		} | ||||
| 		this.databaseBackend.updateConversation(conversation); | ||||
| 		this.conversations.remove(conversation); | ||||
| 		if (this.convChangedListener != null) { | ||||
| @ -537,23 +700,18 @@ public class XmppConnectionService extends Service { | ||||
| 	public void createAccount(Account account) { | ||||
| 		databaseBackend.createAccount(account); | ||||
| 		this.accounts.add(account); | ||||
| 		this.connections.put(account, this.createConnection(account)); | ||||
| 		account.setXmppConnection(this.createConnection(account)); | ||||
| 		if (accountChangedListener != null) | ||||
| 			accountChangedListener.onAccountListChangedListener(); | ||||
| 	} | ||||
| 
 | ||||
| 	public void updateAccount(Account account) { | ||||
| 		databaseBackend.updateAccount(account); | ||||
| 		XmppConnection connection = this.connections.get(account); | ||||
| 		if (connection != null) { | ||||
| 			connection.disconnect(); | ||||
| 			this.connections.remove(account); | ||||
| 		if (account.getXmppConnection() != null) { | ||||
| 			disconnect(account); | ||||
| 		} | ||||
| 		if (!account.isOptionSet(Account.OPTION_DISABLED)) { | ||||
| 			this.connections.put(account, this.createConnection(account)); | ||||
| 		} else { | ||||
| 			Log.d(LOGTAG, account.getJid() | ||||
| 					+ ": not starting because it's disabled"); | ||||
| 			account.setXmppConnection(this.createConnection(account)); | ||||
| 		} | ||||
| 		if (accountChangedListener != null) | ||||
| 			accountChangedListener.onAccountListChangedListener(); | ||||
| @ -561,10 +719,8 @@ public class XmppConnectionService extends Service { | ||||
| 
 | ||||
| 	public void deleteAccount(Account account) { | ||||
| 		Log.d(LOGTAG, "called delete account"); | ||||
| 		if (this.connections.containsKey(account)) { | ||||
| 			Log.d(LOGTAG, "found connection. disconnecting"); | ||||
| 			this.connections.get(account).disconnect(); | ||||
| 			this.connections.remove(account); | ||||
| 		if (account.getXmppConnection() != null) { | ||||
| 			this.disconnect(account); | ||||
| 		} | ||||
| 		databaseBackend.deleteAccount(account); | ||||
| 		this.accounts.remove(account); | ||||
| @ -596,33 +752,56 @@ public class XmppConnectionService extends Service { | ||||
| 			Conversation conversation = conversations.get(i); | ||||
| 			if ((conversation.getMode() == Conversation.MODE_MULTI) | ||||
| 					&& (conversation.getAccount() == account)) { | ||||
| 				joinMuc(account, conversation); | ||||
| 				joinMuc(conversation); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	public void joinMuc(Account account, Conversation conversation) { | ||||
| 	public void joinMuc(Conversation conversation) { | ||||
| 		String muc = conversation.getContactJid(); | ||||
| 		PresencePacket packet = new PresencePacket(); | ||||
| 		packet.setAttribute("to", muc + "/" + account.getUsername()); | ||||
| 		packet.setAttribute("to", muc + "/" | ||||
| 				+ conversation.getAccount().getUsername()); | ||||
| 		Element x = new Element("x"); | ||||
| 		x.setAttribute("xmlns", "http://jabber.org/protocol/muc"); | ||||
| 		if (conversation.getMessages().size() != 0) { | ||||
| 			Element history = new Element("history"); | ||||
| 			history.setAttribute( | ||||
| 					"seconds", | ||||
| 			history.setAttribute("seconds", | ||||
| 					(System.currentTimeMillis() - conversation | ||||
| 							.getLatestMessageDate()) / 1000 + ""); | ||||
| 							.getLatestMessage().getTimeSent() / 1000) + ""); | ||||
| 			x.addChild(history); | ||||
| 		} | ||||
| 		packet.addChild(x); | ||||
| 		connections.get(conversation.getAccount()).sendPresencePacket(packet); | ||||
| 		conversation.getAccount().getXmppConnection() | ||||
| 				.sendPresencePacket(packet); | ||||
| 	} | ||||
| 
 | ||||
| 	public void disconnectMultiModeConversations() { | ||||
| 	public void leaveMuc(Conversation conversation) { | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	public void disconnect(Account account) { | ||||
| 		List<Conversation> conversations = getConversations(); | ||||
| 		for (int i = 0; i < conversations.size(); i++) { | ||||
| 			Conversation conversation = conversations.get(i); | ||||
| 			if (conversation.getAccount() == account) { | ||||
| 				if (conversation.getMode() == Conversation.MODE_MULTI) { | ||||
| 					leaveMuc(conversation); | ||||
| 				} else { | ||||
| 					try { | ||||
| 						conversation.endOtrIfNeeded(); | ||||
| 					} catch (OtrException e) { | ||||
| 						Log.d(LOGTAG, "error ending otr session for " | ||||
| 								+ conversation.getName()); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		account.getXmppConnection().disconnect(); | ||||
| 		Log.d(LOGTAG, "disconnected account: " + account.getJid()); | ||||
| 		account.setXmppConnection(null); | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public IBinder onBind(Intent intent) { | ||||
| 		return mBinder; | ||||
|  | ||||
| @ -9,6 +9,7 @@ import de.gultsch.chat.R; | ||||
| import de.gultsch.chat.R.id; | ||||
| import de.gultsch.chat.entities.Contact; | ||||
| import de.gultsch.chat.entities.Conversation; | ||||
| import de.gultsch.chat.entities.Message; | ||||
| import de.gultsch.chat.utils.UIHelper; | ||||
| import android.net.Uri; | ||||
| import android.os.Bundle; | ||||
| @ -114,7 +115,7 @@ public class ConversationActivity extends XmppActivity { | ||||
| 			Collections.sort(this.conversationList, new Comparator<Conversation>() { | ||||
| 				@Override | ||||
| 				public int compare(Conversation lhs, Conversation rhs) { | ||||
| 					return (int) (rhs.getLatestMessageDate() - lhs.getLatestMessageDate()); | ||||
| 					return (int) (rhs.getLatestMessage().getTimeSent() - lhs.getLatestMessage().getTimeSent()); | ||||
| 				} | ||||
| 			}); | ||||
| 		} | ||||
| @ -143,7 +144,7 @@ public class ConversationActivity extends XmppActivity { | ||||
| 				TextView convName = (TextView) view.findViewById(R.id.conversation_name); | ||||
| 				convName.setText(conv.getName()); | ||||
| 				TextView convLastMsg = (TextView) view.findViewById(R.id.conversation_lastmsg); | ||||
| 				convLastMsg.setText(conv.getLatestMessage()); | ||||
| 				convLastMsg.setText(conv.getLatestMessage().getBody()); | ||||
| 				 | ||||
| 				if(!conv.isRead()) { | ||||
| 					convName.setTypeface(null,Typeface.BOLD); | ||||
| @ -154,7 +155,7 @@ public class ConversationActivity extends XmppActivity { | ||||
| 				} | ||||
| 				 | ||||
| 				((TextView) view.findViewById(R.id.conversation_lastupdate)) | ||||
| 				.setText(UIHelper.readableTimeDifference(getItem(position).getLatestMessageDate())); | ||||
| 				.setText(UIHelper.readableTimeDifference(getItem(position).getLatestMessage().getTimeSent())); | ||||
| 				 | ||||
| 				Uri profilePhoto = getItem(position).getProfilePhotoUri(); | ||||
| 				ImageView imageView = (ImageView) view.findViewById(R.id.conversation_image); | ||||
| @ -238,18 +239,23 @@ public class ConversationActivity extends XmppActivity { | ||||
| 	@Override | ||||
| 	public boolean onCreateOptionsMenu(Menu menu) { | ||||
| 		getMenuInflater().inflate(R.menu.conversations, menu); | ||||
| 
 | ||||
| 		MenuItem menuSecure = (MenuItem) menu.findItem(R.id.action_security); | ||||
| 		 | ||||
| 		if (spl.isOpen()) { | ||||
| 			((MenuItem) menu.findItem(R.id.action_archive)).setVisible(false); | ||||
| 			((MenuItem) menu.findItem(R.id.action_details)).setVisible(false); | ||||
| 			((MenuItem) menu.findItem(R.id.action_security)).setVisible(false); | ||||
| 			menuSecure.setVisible(false); | ||||
| 		} else { | ||||
| 			((MenuItem) menu.findItem(R.id.action_add)).setVisible(false); | ||||
| 			if (this.getSelectedConversation()!=null) { | ||||
| 				if (this.getSelectedConversation().getMode() == Conversation.MODE_MULTI) { | ||||
| 					((MenuItem) menu.findItem(R.id.action_security)).setVisible(false); | ||||
| 					((MenuItem) menu.findItem(R.id.action_details)).setVisible(false); | ||||
| 					menuSecure.setVisible(false); | ||||
| 					((MenuItem) menu.findItem(R.id.action_archive)).setTitle("Leave conference"); | ||||
| 				} else { | ||||
| 					if (this.getSelectedConversation().getLatestMessage().getEncryption() != Message.ENCRYPTION_NONE) { | ||||
| 						menuSecure.setIcon(R.drawable.ic_action_secure); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| @ -15,7 +15,6 @@ import android.graphics.Typeface; | ||||
| import android.net.Uri; | ||||
| import android.os.Bundle; | ||||
| import android.preference.PreferenceManager; | ||||
| import android.util.Log; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.View.OnClickListener; | ||||
| @ -36,38 +35,30 @@ public class ConversationFragment extends Fragment { | ||||
| 	protected ArrayAdapter<Message> messageListAdapter; | ||||
| 	protected Contact contact; | ||||
| 	 | ||||
| 	private EditText chatMsg; | ||||
| 	private int nextMessageEncryption = Message.ENCRYPTION_NONE; | ||||
| 	 | ||||
| 	@Override | ||||
| 	public View onCreateView(final LayoutInflater inflater, | ||||
| 			ViewGroup container, Bundle savedInstanceState) { | ||||
| 
 | ||||
| 		this.inflater = inflater; | ||||
| 
 | ||||
| 
 | ||||
| 		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(); | ||||
| 						EditText chatMsg = (EditText) view | ||||
| 								.findViewById(R.id.textinput); | ||||
| 						if (chatMsg.getText().length() < 1) | ||||
| 							return; | ||||
| 						Message message = new Message(conversation, chatMsg | ||||
| 								.getText().toString(), Message.ENCRYPTION_NONE); | ||||
| 								.getText().toString(), nextMessageEncryption); | ||||
| 						activity.xmppConnectionService.sendMessage(conversation.getAccount(),message); | ||||
| 						chatMsg.setText(""); | ||||
| 						 | ||||
| 						/*if (conversation.getMode()==Conversation.MODE_SINGLE) { | ||||
| 							conversation.getMessages().add(message); | ||||
| 							messageList.add(message); | ||||
| 						}*/ | ||||
| 						 | ||||
| 						//activity.updateConversationList(); | ||||
| 						 | ||||
| 						//messagesView.setSelection(messageList.size() - 1); | ||||
| 					} | ||||
| 				}); | ||||
| 
 | ||||
| @ -213,6 +204,22 @@ public class ConversationFragment extends Fragment { | ||||
| 		this.messageList.clear(); | ||||
| 		this.messageList.addAll(this.conversation.getMessages()); | ||||
| 		this.messageListAdapter.notifyDataSetChanged(); | ||||
| 		if (messageList.size()>=1) { | ||||
| 			nextMessageEncryption = this.conversation.getLatestMessage().getEncryption(); | ||||
| 		} | ||||
| 		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; | ||||
| 		} | ||||
| 		int size = this.messageList.size(); | ||||
| 		if (size >= 1) | ||||
| 			messagesView.setSelection(size - 1); | ||||
|  | ||||
| @ -92,7 +92,7 @@ public class UIHelper { | ||||
| 				.getName(), (int) res | ||||
| 				.getDimension(android.R.dimen.notification_large_icon_width))); | ||||
| 		mBuilder.setContentTitle(conversation.getName()); | ||||
| 		mBuilder.setTicker(conversation.getLatestMessage().trim()); | ||||
| 		mBuilder.setTicker(conversation.getLatestMessage().getBody().trim()); | ||||
| 		StringBuilder bigText = new StringBuilder(); | ||||
| 		List<Message> messages = conversation.getMessages(); | ||||
| 		String firstLine = ""; | ||||
|  | ||||
| @ -3,12 +3,7 @@ package de.gultsch.chat.xml; | ||||
| import java.io.IOException; | ||||
| import java.io.OutputStream; | ||||
| import java.io.OutputStreamWriter; | ||||
| import java.util.Collection; | ||||
| import java.util.Iterator; | ||||
| import java.util.concurrent.ArrayBlockingQueue; | ||||
| import java.util.concurrent.BlockingQueue; | ||||
| import java.util.concurrent.LinkedBlockingQueue; | ||||
| import java.util.concurrent.TimeUnit; | ||||
| 
 | ||||
| import android.util.Log; | ||||
| 
 | ||||
|  | ||||
| @ -42,6 +42,7 @@ public class MessagePacket extends Element { | ||||
| 	} | ||||
| 	 | ||||
| 	public void setBody(String text) { | ||||
| 		this.children.remove(findChild("body")); | ||||
| 		Element body = new Element("body"); | ||||
| 		body.setContent(text); | ||||
| 		this.children.add(body); | ||||
|  | ||||
| @ -411,6 +411,7 @@ public class XmppConnection implements Runnable { | ||||
| 	} | ||||
| 
 | ||||
| 	public void sendMessagePacket(MessagePacket packet) { | ||||
| 		Log.d(LOGTAG,"sending message packet "+packet.toString()); | ||||
| 		tagWriter.writeElement(packet); | ||||
| 	} | ||||
| 
 | ||||
| @ -440,6 +441,6 @@ public class XmppConnection implements Runnable { | ||||
| 
 | ||||
| 	public void disconnect() { | ||||
| 		shouldConnect = false; | ||||
| 		tagWriter.writeTag(Tag.end("stream")); | ||||
| 		tagWriter.writeTag(Tag.end("stream:stream")); | ||||
| 	} | ||||
| } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Daniel Gultsch
						Daniel Gultsch