happy hanukkah

iNPUTmice created

Change summary

src/main/java/eu/siacs/conversations/entities/Bookmark.java           |  7 
src/main/java/eu/siacs/conversations/entities/Contact.java            | 73 
src/main/java/eu/siacs/conversations/entities/ListItem.java           | 22 
src/main/java/eu/siacs/conversations/parser/IqParser.java             |  1 
src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java | 10 
src/main/java/eu/siacs/conversations/services/AvatarService.java      | 14 
src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java  | 21 
src/main/java/eu/siacs/conversations/utils/UIHelper.java              |  7 
src/main/res/layout/contact.xml                                       |  8 
src/main/res/layout/list_item_tag.xml                                 | 13 
src/main/res/values/strings.xml                                       |  2 
src/main/res/values/styles.xml                                        |  3 
src/main/res/xml/preferences.xml                                      |  5 
13 files changed, 162 insertions(+), 24 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/entities/Bookmark.java 🔗

@@ -1,5 +1,7 @@
 package eu.siacs.conversations.entities;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Locale;
 
 import eu.siacs.conversations.xml.Element;
@@ -88,6 +90,11 @@ public class Bookmark extends Element implements ListItem {
 		}
 	}
 
+	@Override
+	public List<Tag> getTags() {
+		return new ArrayList<Tag>();
+	}
+
 	public String getNick() {
 		Element nick = this.findChild("nick");
 		if (nick != null) {

src/main/java/eu/siacs/conversations/entities/Contact.java 🔗

@@ -9,8 +9,10 @@ import org.json.JSONObject;
 
 import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 
+import eu.siacs.conversations.utils.UIHelper;
 import eu.siacs.conversations.xml.Element;
 import eu.siacs.conversations.xmpp.jid.InvalidJidException;
 import eu.siacs.conversations.xmpp.jid.Jid;
@@ -29,6 +31,7 @@ public class Contact implements ListItem {
 	public static final String AVATAR = "avatar";
     public static final String LAST_PRESENCE = "last_presence";
     public static final String LAST_TIME = "last_time";
+	public static final String GROUPS = "groups";
 
 	protected String accountUuid;
 	protected String systemName;
@@ -40,6 +43,7 @@ public class Contact implements ListItem {
 	protected String photoUri;
 	protected String avatar;
 	protected JSONObject keys = new JSONObject();
+	protected JSONArray groups = new JSONArray();
 	protected Presences presences = new Presences();
 
 	protected Account account;
@@ -48,16 +52,7 @@ public class Contact implements ListItem {
 
 	public Contact(final String account, final String systemName, final String serverName,
 		final Jid jid, final int subscription, final String photoUri,
-		final String systemAccount, final String keys, final String avatar,
-		final Lastseen lastseen) {
-		this(account, systemName, serverName, jid, subscription, photoUri, systemAccount, keys,
-				avatar);
-		this.lastseen = lastseen;
-	}
-
-	public Contact(final String account, final String systemName, final String serverName,
-		final Jid jid, final int subscription, final String photoUri,
-		final String systemAccount, final String keys, final String avatar) {
+		final String systemAccount, final String keys, final String avatar, final Lastseen lastseen, final String groups) {
 		this.accountUuid = account;
 		this.systemName = systemName;
 		this.serverName = serverName;
@@ -71,6 +66,12 @@ public class Contact implements ListItem {
 			this.keys = new JSONObject();
 		}
 		this.avatar = avatar;
+		try {
+			this.groups = (groups == null ? new JSONArray() : new JSONArray(groups));
+		} catch (JSONException e) {
+			this.groups = new JSONArray();
+		}
+		this.lastseen = lastseen;
 	}
 
 	public Contact(final Jid jid) {
@@ -99,6 +100,31 @@ public class Contact implements ListItem {
 		return jid;
 	}
 
+	@Override
+	public List<Tag> getTags() {
+		ArrayList<Tag> tags = new ArrayList<Tag>();
+		for(String group : getGroups()) {
+			tags.add(new Tag(group, UIHelper.getColorForName(group)));
+		}
+		int status = getMostAvailableStatus();
+		switch (getMostAvailableStatus()) {
+			case Presences.CHAT:
+			case Presences.ONLINE:
+				tags.add(new Tag("online",0xff259b24));
+				break;
+			case Presences.AWAY:
+				tags.add(new Tag("away",0xffff9800));
+				break;
+			case Presences.XA:
+				tags.add(new Tag("not available",0xffe51c23));
+				break;
+			case Presences.DND:
+				tags.add(new Tag("dnd",0xffe51c23));
+				break;
+		}
+		return tags;
+	}
+
 	public boolean match(String needle) {
 		return needle == null
 				|| jid.toString().contains(needle.toLowerCase())
@@ -119,6 +145,7 @@ public class Contact implements ListItem {
 		values.put(AVATAR, avatar);
 		values.put(LAST_PRESENCE, lastseen.presence);
 		values.put(LAST_TIME, lastseen.time);
+		values.put(GROUPS,groups.toString());
 		return values;
 	}
 
@@ -142,7 +169,8 @@ public class Contact implements ListItem {
 				cursor.getString(cursor.getColumnIndex(SYSTEMACCOUNT)),
 				cursor.getString(cursor.getColumnIndex(KEYS)),
 				cursor.getString(cursor.getColumnIndex(AVATAR)),
-				lastseen);
+				lastseen,
+				cursor.getString(cursor.getColumnIndex(GROUPS)));
 	}
 
 	public int getSubscription() {
@@ -207,6 +235,17 @@ public class Contact implements ListItem {
 		return systemAccount;
 	}
 
+	public List<String> getGroups() {
+		ArrayList<String> groups = new ArrayList<String>();
+		for(int i = 0; i < this.groups.length(); ++i) {
+			try {
+				groups.add(this.groups.getString(i));
+			} catch (final JSONException ignored) {
+			}
+		}
+		return groups;
+	}
+
 	public ArrayList<String> getOtrFingerprints() {
 		ArrayList<String> fingerprints = new ArrayList<String>();
 		try {
@@ -318,12 +357,24 @@ public class Contact implements ListItem {
 		}
 	}
 
+	public void parseGroupsFromElement(Element item) {
+		this.groups = new JSONArray();
+		for(Element element : item.getChildren()) {
+			if (element.getName().equals("group") && element.getContent() != null) {
+				this.groups.put(element.getContent());
+			}
+		}
+	}
+
 	public Element asElement() {
 		final Element item = new Element("item");
 		item.setAttribute("jid", this.jid.toString());
 		if (this.serverName != null) {
 			item.setAttribute("name", this.serverName);
 		}
+		for(String group : getGroups()) {
+			item.addChild("group").setContent(group);
+		}
 		return item;
 	}
 

src/main/java/eu/siacs/conversations/entities/ListItem.java 🔗

@@ -1,9 +1,31 @@
 package eu.siacs.conversations.entities;
 
+import java.util.List;
+
 import eu.siacs.conversations.xmpp.jid.Jid;
 
 public interface ListItem extends Comparable<ListItem> {
 	public String getDisplayName();
 
 	public Jid getJid();
+
+	public List<Tag> getTags();
+
+	public final class Tag {
+		private String name;
+		private int color;
+
+		public Tag(String name, int color) {
+			this.name = name;
+			this.color = color;
+		}
+
+		public int getColor() {
+			return this.color;
+		}
+
+		public String getName() {
+			return this.name;
+		}
+	}
 }

src/main/java/eu/siacs/conversations/parser/IqParser.java 🔗

@@ -34,6 +34,7 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
 				Contact contact = account.getRoster().getContact(jid);
 				if (!contact.getOption(Contact.Options.DIRTY_PUSH)) {
 					contact.setServerName(name);
+					contact.parseGroupsFromElement(item);
 				}
 				if (subscription != null) {
 					if (subscription.equals("remove")) {

src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java 🔗

@@ -22,7 +22,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
 	private static DatabaseBackend instance = null;
 
 	private static final String DATABASE_NAME = "history";
-	private static final int DATABASE_VERSION = 10;
+	private static final int DATABASE_VERSION = 11;
 
 	private static String CREATE_CONTATCS_STATEMENT = "create table "
 			+ Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, "
@@ -31,7 +31,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
 			+ Contact.PHOTOURI + " TEXT," + Contact.OPTIONS + " NUMBER,"
 			+ Contact.SYSTEMACCOUNT + " NUMBER, " + Contact.AVATAR + " TEXT, "
             + Contact.LAST_PRESENCE + " TEXT, " + Contact.LAST_TIME + " NUMBER, "
-			+ "FOREIGN KEY(" + Contact.ACCOUNT + ") REFERENCES "
+			+ Contact.GROUPS + " TEXT, FOREIGN KEY(" + Contact.ACCOUNT + ") REFERENCES "
 			+ Account.TABLENAME + "(" + Account.UUID
 			+ ") ON DELETE CASCADE, UNIQUE(" + Contact.ACCOUNT + ", "
 			+ Contact.JID + ") ON CONFLICT REPLACE);";
@@ -115,6 +115,12 @@ public class DatabaseBackend extends SQLiteOpenHelper {
 			db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
 					+ Message.RELATIVE_FILE_PATH + " TEXT");
 		}
+		if (oldVersion < 11 && newVersion >= 11) {
+			db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
+					+ Contact.GROUPS + " TEXT");
+			db.execSQL("delete from "+Contact.TABLENAME);
+			db.execSQL("update "+Account.TABLENAME+" set "+Account.ROSTERVERSION+" = NULL");
+		}
 	}
 
 	public static synchronized DatabaseBackend getInstance(Context context) {

src/main/java/eu/siacs/conversations/services/AvatarService.java 🔗

@@ -17,6 +17,7 @@ import eu.siacs.conversations.entities.Contact;
 import eu.siacs.conversations.entities.Conversation;
 import eu.siacs.conversations.entities.ListItem;
 import eu.siacs.conversations.entities.MucOptions;
+import eu.siacs.conversations.utils.UIHelper;
 
 public class AvatarService {
 
@@ -119,7 +120,7 @@ public class AvatarService {
 		if (count == 0) {
 			String name = mucOptions.getConversation().getName();
 			String letter = name.substring(0, 1);
-			int color = this.getColorForName(name);
+			int color = UIHelper.getColorForName(name);
 			drawTile(canvas, letter, color, 0, 0, size, size);
 		} else if (count == 1) {
 			drawTile(canvas, users.get(0), 0, 0, size, size);
@@ -209,7 +210,7 @@ public class AvatarService {
 		int color;
 		if (name.length() > 0) {
 			letter = name.substring(0, 1);
-			color = this.getColorForName(name);
+			color = UIHelper.getColorForName(name);
 		} else {
 			letter = "X";
 			color = PLACEHOLDER_COLOR;
@@ -272,7 +273,7 @@ public class AvatarService {
 		int color;
 		if (name.length() > 0) {
 			letter = name.substring(0, 1);
-			color = this.getColorForName(name);
+			color = UIHelper.getColorForName(name);
 		} else {
 			letter = "X";
 			color = PLACEHOLDER_COLOR;
@@ -286,11 +287,4 @@ public class AvatarService {
 		canvas.drawBitmap(bm, null, dst, null);
 	}
 
-	private int getColorForName(String name) {
-		int holoColors[] = {0xFFe91e63, 0xFF9c27b0, 0xFF673ab7, 0xFF3f51b5,
-				0xFF5677fc, 0xFF03a9f4, 0xFF00bcd4, 0xFF009688, 0xFFff5722,
-				0xFF795548, 0xFF607d8b};
-		return holoColors[(int) ((name.hashCode() & 0xffffffffl) % holoColors.length)];
-	}
-
 }

src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java 🔗

@@ -6,20 +6,26 @@ import eu.siacs.conversations.R;
 import eu.siacs.conversations.entities.ListItem;
 import eu.siacs.conversations.ui.XmppActivity;
 import android.content.Context;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ArrayAdapter;
 import android.widget.ImageView;
+import android.widget.LinearLayout;
 import android.widget.TextView;
 
 public class ListItemAdapter extends ArrayAdapter<ListItem> {
 
 	protected XmppActivity activity;
+	protected boolean showDynamicTags = false;
 
 	public ListItemAdapter(XmppActivity activity, List<ListItem> objects) {
 		super(activity, 0, objects);
 		this.activity = activity;
+		SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
+		this.showDynamicTags = preferences.getBoolean("show_dynamic_tags",false);
 	}
 
 	@Override
@@ -33,6 +39,21 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
 		TextView name = (TextView) view.findViewById(R.id.contact_display_name);
 		TextView jid = (TextView) view.findViewById(R.id.contact_jid);
 		ImageView picture = (ImageView) view.findViewById(R.id.contact_photo);
+		LinearLayout tagLayout = (LinearLayout) view.findViewById(R.id.tags);
+
+		List<ListItem.Tag> tags = item.getTags();
+		if (tags.size() == 0 || !this.showDynamicTags) {
+			tagLayout.setVisibility(View.GONE);
+		} else {
+			tagLayout.setVisibility(View.VISIBLE);
+			tagLayout.removeAllViewsInLayout();
+			for(ListItem.Tag tag : tags) {
+				TextView tv = (TextView) inflater.inflate(R.layout.list_item_tag,tagLayout,false);
+				tv.setText(tag.getName());
+				tv.setBackgroundColor(tag.getColor());
+				tagLayout.addView(tv);
+			}
+		}
 
 		jid.setText(item.getJid().toString());
 		name.setText(item.getDisplayName());

src/main/java/eu/siacs/conversations/utils/UIHelper.java 🔗

@@ -188,4 +188,11 @@ public class UIHelper {
 		}
 		return body;
 	}
+
+	public static int getColorForName(String name) {
+		int colors[] = {0xFFe91e63, 0xFF9c27b0, 0xFF673ab7, 0xFF3f51b5,
+				0xFF5677fc, 0xFF03a9f4, 0xFF00bcd4, 0xFF009688, 0xFFff5722,
+				0xFF795548, 0xFF607d8b};
+		return colors[(int) ((name.hashCode() & 0xffffffffl) % colors.length)];
+	}
 }

src/main/res/layout/contact.xml 🔗

@@ -37,7 +37,13 @@
             android:singleLine="true"
             android:textColor="@color/primarytext"
             android:textSize="?attr/TextSizeBody" />
-
+        <LinearLayout
+            android:id="@+id/tags"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="4dp"
+            android:orientation="horizontal">
+        </LinearLayout>
         <TextView
             android:id="@+id/key"
             android:layout_width="wrap_content"

src/main/res/layout/list_item_tag.xml 🔗

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="wrap_content"
+    android:layout_width="wrap_content"
+    android:paddingTop="1dp"
+    android:paddingBottom="1dp"
+    android:paddingLeft="4dp"
+    android:paddingRight="4dp"
+    android:textSize="?attr/TextSizeInfo"
+    android:textColor="@color/ondarktext"
+    android:textAllCaps="true"
+    android:layout_marginRight="8dp"
+/>

src/main/res/values/strings.xml 🔗

@@ -349,4 +349,6 @@
     <string name="could_not_verify_fingerprint">Could not verify fingerprint</string>
     <string name="manually_verify">Manually verify</string>
     <string name="are_you_sure_verify_fingerprint">Are you sure that you want to verify your contacts OTR fingerprint?</string>
+    <string name="pref_show_dynamic_tags">Show dynamic tags</string>
+    <string name="pref_show_dynamic_tags_summary">Display read-only tags underneath contacts</string>
 </resources>

src/main/res/values/styles.xml 🔗

@@ -4,5 +4,8 @@
         <item name="android:layout_height">1.5dp</item>
         <item name="android:background">@color/divider</item>
     </style>
+    <style name="Tag">
+
+    </style>
 
 </resources>

src/main/res/xml/preferences.xml 🔗

@@ -73,6 +73,11 @@
             android:key="send_button_status"
             android:summary="@string/pref_use_send_button_to_indicate_status_summary"
             android:title="@string/pref_use_send_button_to_indicate_status" />
+        <CheckBoxPreference
+            android:defaultValue="false"
+            android:key="show_dynamic_tags"
+            android:summary="@string/pref_show_dynamic_tags_summary"
+            android:title="@string/pref_show_dynamic_tags" />
     </PreferenceCategory>
     <PreferenceCategory android:title="@string/pref_advanced_options" >
         <PreferenceScreen