subscription mgmt works

Daniel Gultsch created

Change summary

src/de/gultsch/chat/entities/Contact.java               | 60 ++++++-
src/de/gultsch/chat/persistance/DatabaseBackend.java    |  2 
src/de/gultsch/chat/services/XmppConnectionService.java | 78 ++++++++
src/de/gultsch/chat/ui/DialogContactDetails.java        | 93 +++++++++-
4 files changed, 199 insertions(+), 34 deletions(-)

Detailed changes

src/de/gultsch/chat/entities/Contact.java 🔗

@@ -9,6 +9,8 @@ import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 
+import de.gultsch.chat.xml.Element;
+
 import android.content.ContentValues;
 import android.database.Cursor;
 
@@ -29,7 +31,7 @@ public class Contact extends AbstractEntity implements Serializable {
 	protected String accountUuid;
 	protected String displayName;
 	protected String jid;
-	protected String subscription;
+	protected int subscription = 0;
 	protected String systemAccount;
 	protected String photoUri;
 	protected JSONObject keys = new JSONObject();
@@ -52,7 +54,7 @@ public class Contact extends AbstractEntity implements Serializable {
 	}
 
 	public Contact(String uuid, String account, String displayName, String jid,
-			String subscription, String photoUri, String systemAccount,
+			int subscription, String photoUri, String systemAccount,
 			String keys, String presences) {
 		this.uuid = uuid;
 		this.accountUuid = account;
@@ -109,18 +111,14 @@ public class Contact extends AbstractEntity implements Serializable {
 				cursor.getString(cursor.getColumnIndex(ACCOUNT)),
 				cursor.getString(cursor.getColumnIndex(DISPLAYNAME)),
 				cursor.getString(cursor.getColumnIndex(JID)),
-				cursor.getString(cursor.getColumnIndex(SUBSCRIPTION)),
+				cursor.getInt(cursor.getColumnIndex(SUBSCRIPTION)),
 				cursor.getString(cursor.getColumnIndex(PHOTOURI)),
 				cursor.getString(cursor.getColumnIndex(SYSTEMACCOUNT)),
 				cursor.getString(cursor.getColumnIndex(KEYS)),
 				cursor.getString(cursor.getColumnIndex(PRESENCES)));
 	}
-
-	public void setSubscription(String subscription) {
-		this.subscription = subscription;
-	}
-
-	public String getSubscription() {
+	
+	public int getSubscription() {
 		return this.subscription;
 	}
 
@@ -220,4 +218,48 @@ public class Contact extends AbstractEntity implements Serializable {
 
 		}
 	}
+	
+	public void setSubscriptionOption(int option) {
+		this.subscription |= 1 << option;
+	}
+	
+	public void resetSubscriptionOption(int option) {
+		this.subscription &= ~(1 << option);
+	}
+	
+	public boolean getSubscriptionOption(int option) {
+		return ((this.subscription & (1 << option)) != 0);
+	}
+	
+	public void parseSubscriptionFromElement(Element item) {
+		String ask = item.getAttribute("ask");
+		String subscription = item.getAttribute("subscription");
+		
+		if (subscription!=null) {
+			if (subscription.equals("to")) {
+				this.resetSubscriptionOption(Contact.Subscription.FROM);
+				this.setSubscriptionOption(Contact.Subscription.TO);
+			} else if (subscription.equals("from")) {
+				this.resetSubscriptionOption(Contact.Subscription.TO);
+				this.setSubscriptionOption(Contact.Subscription.FROM);
+			} else if (subscription.equals("both")) {
+				this.setSubscriptionOption(Contact.Subscription.TO);
+				this.setSubscriptionOption(Contact.Subscription.FROM);
+			}
+		}
+		
+		if ((ask!=null)&&(ask.equals("subscribe"))) {
+			this.setSubscriptionOption(Contact.Subscription.ASKING);
+		} else {
+			this.resetSubscriptionOption(Contact.Subscription.ASKING);
+		}
+	}
+	
+	
+	public class Subscription {
+		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;
+	}
 }

src/de/gultsch/chat/persistance/DatabaseBackend.java 🔗

@@ -58,7 +58,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
 				+ Contact.DISPLAYNAME + " TEXT," + Contact.JID + " TEXT,"
 				+ Contact.PRESENCES + " TEXT, " + Contact.KEYS
 				+ " TEXT," + Contact.PHOTOURI + " TEXT," + Contact.SUBSCRIPTION
-				+ " TEXT," + Contact.SYSTEMACCOUNT + " NUMBER, "
+				+ " NUMBER," + Contact.SYSTEMACCOUNT + " NUMBER, "
 				+ "FOREIGN KEY(" + Contact.ACCOUNT + ") REFERENCES "
 				+ Account.TABLENAME + "(" + Account.UUID
 				+ ") ON DELETE CASCADE);");

src/de/gultsch/chat/services/XmppConnectionService.java 🔗

@@ -200,6 +200,20 @@ public class XmppConnectionService extends Service {
 					contact.removePresence(fromParts[1]);
 					databaseBackend.updateContact(contact);
 				}
+			} else if (type.equals("subscribe")) {
+				Log.d(LOGTAG,account.getJid()+": "+contact.getJid()+" asked to subscribe");
+				if (contact.getSubscriptionOption(Contact.Subscription.PREEMPTIVE_GRANT)) {
+					Log.d(LOGTAG,"preemptive grant existed. granting");
+					sendPresenceUpdatesTo(contact);
+					contact.setSubscriptionOption(Contact.Subscription.FROM);
+					contact.resetSubscriptionOption(Contact.Subscription.PREEMPTIVE_GRANT);
+					replaceContactInConversation(contact.getJid(), contact);
+					databaseBackend.updateContact(contact);
+				} else {
+					//TODO: ask user to handle it maybe
+				}
+			} else {
+				Log.d(LOGTAG,packet.toString());
 			}
 			replaceContactInConversation(contact.getJid(),contact);
 		}
@@ -227,19 +241,21 @@ public class XmppConnectionService extends Service {
 				String subscription = item.getAttribute("subscription");
 				Contact contact = databaseBackend.findContact(account, jid);
 				if (contact == null) {
-					String name = item.getAttribute("name");
-					if (name == null) {
-						name = jid.split("@")[0];
+					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 = new Contact(account, name, jid, null);
-					contact.setSubscription(subscription);
-					databaseBackend.createContact(contact);
 				} else {
 					if (subscription.equals("remove")) {
 						databaseBackend.deleteContact(contact);
 						replaceContactInConversation(contact.getJid(), null);
 					} else {
-						contact.setSubscription(subscription);
+						contact.parseSubscriptionFromElement(item);
 						databaseBackend.updateContact(contact);
 						replaceContactInConversation(contact.getJid(),contact);
 					}
@@ -506,12 +522,14 @@ public class XmppConnectionService extends Service {
 								contact.setDisplayName(phoneContact
 										.getString("displayname"));
 								databaseBackend.updateContact(contact);
+								replaceContactInConversation(contact.getJid(), contact);
 							} else {
 								if ((contact.getSystemAccount() != null)
 										|| (contact.getProfilePhoto() != null)) {
 									contact.setSystemAccount(null);
 									contact.setPhotoUri(null);
 									databaseBackend.updateContact(contact);
+									replaceContactInConversation(contact.getJid(), contact);
 								}
 							}
 						}
@@ -545,7 +563,11 @@ public class XmppConnectionService extends Service {
 	}
 
 	public Contact findContact(Account account, String jid) {
-		return databaseBackend.findContact(account, jid);
+		Contact contact = databaseBackend.findContact(account, jid);
+		if (contact!=null) {
+			contact.setAccount(account);
+		}
+		return contact;
 	}
 
 	public Conversation findOrCreateConversation(Account account, String jid,
@@ -766,4 +788,44 @@ public class XmppConnectionService extends Service {
 		replaceContactInConversation(contact.getJid(), contact);
 		databaseBackend.createContact(contact);
 	}
+
+	public void requestPresenceUpdatesFrom(Contact contact) {
+		//Requesting a Subscription type=subscribe
+		PresencePacket packet = new PresencePacket();
+		packet.setAttribute("type", "subscribe");
+		packet.setAttribute("to", contact.getJid());
+		packet.setAttribute("from",contact.getAccount().getJid());
+		Log.d(LOGTAG,packet.toString());
+		contact.getAccount().getXmppConnection().sendPresencePacket(packet);
+	}
+	
+	public void stopPresenceUpdatesFrom(Contact contact) {
+		//Unsubscribing  type='unsubscribe'
+		PresencePacket packet = new PresencePacket();
+		packet.setAttribute("type", "unsubscribe");
+		packet.setAttribute("to", contact.getJid());
+		packet.setAttribute("from",contact.getAccount().getJid());
+		Log.d(LOGTAG,packet.toString());
+		contact.getAccount().getXmppConnection().sendPresencePacket(packet);
+	}
+	
+	public void stopPresenceUpdatesTo(Contact contact) {
+		//Canceling a Subscription type=unsubscribed
+		PresencePacket packet = new PresencePacket();
+		packet.setAttribute("type", "unsubscribed");
+		packet.setAttribute("to", contact.getJid());
+		packet.setAttribute("from",contact.getAccount().getJid());
+		Log.d(LOGTAG,packet.toString());
+		contact.getAccount().getXmppConnection().sendPresencePacket(packet);
+	}
+	
+	public void sendPresenceUpdatesTo(Contact contact) {
+		//type='subscribed'
+		PresencePacket packet = new PresencePacket();
+		packet.setAttribute("type", "subscribed");
+		packet.setAttribute("to", contact.getJid());
+		packet.setAttribute("from",contact.getAccount().getJid());
+		Log.d(LOGTAG,packet.toString());
+		contact.getAccount().getXmppConnection().sendPresencePacket(packet);
+	}
 }

src/de/gultsch/chat/ui/DialogContactDetails.java 🔗

@@ -13,6 +13,7 @@ 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.View;
 import android.view.View.OnClickListener;
@@ -28,6 +29,9 @@ public class DialogContactDetails extends DialogFragment {
 	private DialogContactDetails mDetailsDialog = this;
 	private XmppActivity activity;
 	
+	private CheckBox send;
+	private CheckBox receive;
+	
 	private DialogInterface.OnClickListener askRemoveFromRoster = new DialogInterface.OnClickListener() {
 		
 		@Override
@@ -64,6 +68,58 @@ public class DialogContactDetails extends DialogFragment {
 		}
 	};
 	
+	private DialogInterface.OnClickListener updateSubscriptions = new DialogInterface.OnClickListener() {
+		
+		@Override
+		public void onClick(DialogInterface dialog, int which) {
+			boolean needsUpdating = false;
+			if (contact.getSubscriptionOption(Contact.Subscription.FROM)) {
+				if (!send.isChecked()) {
+					contact.resetSubscriptionOption(Contact.Subscription.FROM);
+					contact.resetSubscriptionOption(Contact.Subscription.PREEMPTIVE_GRANT);
+					activity.xmppConnectionService.stopPresenceUpdatesTo(contact);
+					needsUpdating=true;
+				}
+			} else {
+				if (contact.getSubscriptionOption(Contact.Subscription.PREEMPTIVE_GRANT)) {
+					if (!send.isChecked()) {
+						contact.resetSubscriptionOption(Contact.Subscription.PREEMPTIVE_GRANT);
+						needsUpdating=true;
+					}
+				} else {
+					if (send.isChecked()) {
+						contact.setSubscriptionOption(Contact.Subscription.PREEMPTIVE_GRANT);
+						needsUpdating=true;
+					}
+				}
+			}
+			if (contact.getSubscriptionOption(Contact.Subscription.TO)) {
+				if (!receive.isChecked()) {
+					contact.resetSubscriptionOption(Contact.Subscription.TO);
+					activity.xmppConnectionService.stopPresenceUpdatesFrom(contact);
+					needsUpdating=true;
+				}
+			} else {
+				if (contact.getSubscriptionOption(Contact.Subscription.ASKING)) {
+					if (!receive.isChecked()) {
+						contact.resetSubscriptionOption(Contact.Subscription.ASKING);
+						activity.xmppConnectionService.stopPresenceUpdatesFrom(contact);
+						needsUpdating=true;
+					}
+				} else {
+					if (receive.isChecked()) {
+						contact.setSubscriptionOption(Contact.Subscription.ASKING);
+						activity.xmppConnectionService.requestPresenceUpdatesFrom(contact);
+						needsUpdating=true;
+					}
+				}
+			}
+			if (needsUpdating) {
+				activity.xmppConnectionService.updateContact(contact);
+			}
+		}
+	};
+
 	public void setContact(Contact contact) {
 		this.contact = contact;
 	}
@@ -77,21 +133,29 @@ public class DialogContactDetails extends DialogFragment {
 		TextView contactJid = (TextView) view.findViewById(R.id.details_contactjid);
 		TextView accountJid = (TextView) view.findViewById(R.id.details_account);
 		TextView status = (TextView) view.findViewById(R.id.details_contactstatus);
-		CheckBox send = (CheckBox) view.findViewById(R.id.details_send_presence);
-		CheckBox receive = (CheckBox) view.findViewById(R.id.details_receive_presence);
+		send = (CheckBox) view.findViewById(R.id.details_send_presence);
+		receive = (CheckBox) view.findViewById(R.id.details_receive_presence);
 		//ImageView contactPhoto = (ImageView) view.findViewById(R.id.details_contact_picture);
 		QuickContactBadge badge = (QuickContactBadge) view.findViewById(R.id.details_contact_badge);
 		
-		boolean subscriptionSend = false;
-		boolean subscriptionReceive = false;
-		if (contact.getSubscription()!=null) {
-			if (contact.getSubscription().equals("both")) {
-				subscriptionReceive = true;
-				subscriptionSend = true;
-			} else if (contact.getSubscription().equals("from")) {
-				subscriptionSend = true;
-			} else if (contact.getSubscription().equals("to")) {
-				subscriptionReceive = true;
+		if (contact.getSubscriptionOption(Contact.Subscription.FROM)) {
+			send.setChecked(true);
+		} else {
+			send.setText("Preemptively grant subscription request");
+			if (contact.getSubscriptionOption(Contact.Subscription.PREEMPTIVE_GRANT)) {
+				send.setChecked(true);
+			} else {
+				send.setChecked(false);
+			}
+		}
+		if (contact.getSubscriptionOption(Contact.Subscription.TO)) {
+			receive.setChecked(true);
+		} else {
+			receive.setText("Request presence updates");
+			if (contact.getSubscriptionOption(Contact.Subscription.ASKING)) {
+				receive.setChecked(true);
+			} else {
+				receive.setChecked(false);
 			}
 		}
 		
@@ -125,9 +189,6 @@ public class DialogContactDetails extends DialogFragment {
 			status.setTextColor(0xFFe92727);
 			break;
 		}
-		
-		send.setChecked(subscriptionSend);
-		receive.setChecked(subscriptionReceive);
 		contactJid.setText(contact.getJid());
 		accountJid.setText(contact.getAccount().getJid());
 
@@ -151,7 +212,7 @@ public class DialogContactDetails extends DialogFragment {
 		builder.setView(view);
 		builder.setTitle(contact.getDisplayName());
 		
-		builder.setNeutralButton("Done", null);
+		builder.setNeutralButton("Done", this.updateSubscriptions);
 		builder.setPositiveButton("Remove from roster", this.askRemoveFromRoster);
 		return builder.create();
 	}