style dynamic tags

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/Config.java                     |   1 
src/main/java/eu/siacs/conversations/entities/Bookmark.java          |  20 
src/main/java/eu/siacs/conversations/entities/Contact.java           |  11 
src/main/java/eu/siacs/conversations/entities/ListItem.java          |   8 
src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java  |  33 
src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java |  47 
src/main/java/eu/siacs/conversations/utils/UIHelper.java             | 148 
src/main/java/eu/siacs/conversations/utils/XEP0392Helper.java        |   4 
src/main/res/drawable/background_label.xml                           |   5 
src/main/res/layout/list_item_tag.xml                                |  17 
10 files changed, 143 insertions(+), 151 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/Config.java 🔗

@@ -81,7 +81,6 @@ public final class Config {
     public static final int CONNECT_DISCO_TIMEOUT = 20;
     public static final int MINI_GRACE_PERIOD = 750;
 
-    public static final boolean XEP_0392 = true; // enables XEP-0392 v0.6.0
 
     // media file formats. Homogenous Android or Conversations only deployments can switch to opus
     // and webp

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

@@ -5,6 +5,9 @@ import android.content.Context;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -161,15 +164,18 @@ public class Bookmark extends Element implements ListItem {
 	}
 
 	@Override
-	public List<Tag> getTags(Context context) {
-		ArrayList<Tag> tags = new ArrayList<>();
-		for (Element element : getChildren()) {
-			if (element.getName().equals("group") && element.getContent() != null) {
-				String group = element.getContent();
-				tags.add(new Tag(group, UIHelper.getColorForName(group,true)));
+	public List<Tag> getTags(final Context context) {
+		final ImmutableList.Builder<Tag> tags = new ImmutableList.Builder<>();
+		for (final Element element : getChildren()) {
+			final String content = element.getContent();
+			if (Strings.isNullOrEmpty(content)) {
+				continue;
+			}
+			if (element.getName().equals("group")) {
+				tags.add(new Tag(content));
 			}
 		}
-		return tags;
+		return tags.build();
 	}
 
 	public String getNick() {

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

@@ -180,17 +180,10 @@ public class Contact implements ListItem, Blockable {
     }
 
     @Override
-    public List<Tag> getTags(Context context) {
+    public List<Tag> getTags(final Context context) {
         final ArrayList<Tag> tags = new ArrayList<>();
         for (final String group : getGroups(true)) {
-            tags.add(new Tag(group, UIHelper.getColorForName(group)));
-        }
-        Presence.Status status = getShownStatus();
-        if (status != Presence.Status.OFFLINE) {
-            tags.add(UIHelper.getTagForStatus(context, status));
-        }
-        if (isBlocked()) {
-            tags.add(new Tag(context.getString(R.string.blocked), 0xff2e2f3b));
+            tags.add(new Tag(group));
         }
         return tags;
     }

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

@@ -17,15 +17,9 @@ public interface ListItem extends Comparable<ListItem>, AvatarService.Avatarable
 
 	final class Tag {
 		private final String name;
-		private final int color;
 
-		public Tag(final String name, final int color) {
+		public Tag(final String name) {
 			this.name = name;
-			this.color = color;
-		}
-
-		public int getColor() {
-			return this.color;
 		}
 
 		public String getName() {

src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java 🔗

@@ -6,6 +6,7 @@ import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
+import android.content.res.ColorStateList;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
@@ -28,6 +29,7 @@ import android.widget.Toast;
 
 import androidx.annotation.NonNull;
 import androidx.appcompat.app.AlertDialog;
+import androidx.core.content.ContextCompat;
 import androidx.databinding.DataBindingUtil;
 
 import com.google.android.material.color.MaterialColors;
@@ -49,6 +51,7 @@ import eu.siacs.conversations.databinding.ActivityContactDetailsBinding;
 import eu.siacs.conversations.entities.Account;
 import eu.siacs.conversations.entities.Contact;
 import eu.siacs.conversations.entities.ListItem;
+import eu.siacs.conversations.entities.Presence;
 import eu.siacs.conversations.services.AbstractQuickConversationsService;
 import eu.siacs.conversations.services.QuickConversationsService;
 import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
@@ -66,6 +69,7 @@ import eu.siacs.conversations.utils.Emoticons;
 import eu.siacs.conversations.utils.IrregularUnicodeDetector;
 import eu.siacs.conversations.utils.PhoneNumberUtilWrapper;
 import eu.siacs.conversations.utils.UIHelper;
+import eu.siacs.conversations.utils.XEP0392Helper;
 import eu.siacs.conversations.utils.XmppUri;
 import eu.siacs.conversations.xml.Namespace;
 import eu.siacs.conversations.xmpp.Jid;
@@ -504,18 +508,39 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
         }
         binding.keysWrapper.setVisibility(hasKeys ? View.VISIBLE : View.GONE);
 
-        List<ListItem.Tag> tagList = contact.getTags(this);
-        if (tagList.isEmpty() || !this.showDynamicTags) {
+        final List<ListItem.Tag> tagList = contact.getTags(this);
+        final boolean hasMetaTags = contact.isBlocked() || contact.getShownStatus() != Presence.Status.OFFLINE;
+        if ((tagList.isEmpty() && !hasMetaTags) || !this.showDynamicTags) {
             binding.tags.setVisibility(View.GONE);
         } else {
             binding.tags.setVisibility(View.VISIBLE);
             binding.tags.removeAllViewsInLayout();
             for (final ListItem.Tag tag : tagList) {
+                final String name = tag.getName();
                 final TextView tv = (TextView) inflater.inflate(R.layout.list_item_tag, binding.tags, false);
-                tv.setText(tag.getName());
-                tv.setBackgroundColor(tag.getColor());
+                tv.setText(name);
+                tv.setBackgroundTintList(ColorStateList.valueOf(MaterialColors.harmonizeWithPrimary(this,XEP0392Helper.rgbFromNick(name))));
                 binding.tags.addView(tv);
             }
+            if (contact.isBlocked()) {
+                final TextView tv =
+                        (TextView)
+                                inflater.inflate(
+                                        R.layout.list_item_tag, binding.tags, false);
+                tv.setText(R.string.blocked);
+                tv.setBackgroundTintList(ColorStateList.valueOf(MaterialColors.harmonizeWithPrimary(tv.getContext(), ContextCompat.getColor(tv.getContext(),R.color.gray_800))));
+                binding.tags.addView(tv);
+            } else {
+                final Presence.Status status = contact.getShownStatus();
+                if (status != Presence.Status.OFFLINE) {
+                    final TextView tv =
+                            (TextView)
+                                    inflater.inflate(
+                                            R.layout.list_item_tag, binding.tags, false);
+                    UIHelper.setStatus(tv, status);
+                    binding.tags.addView(tv);
+                }
+            }
         }
     }
 

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

@@ -1,6 +1,7 @@
 package eu.siacs.conversations.ui.adapter;
 
 import android.content.SharedPreferences;
+import android.content.res.ColorStateList;
 import android.preference.PreferenceManager;
 import android.util.Log;
 import android.view.LayoutInflater;
@@ -11,18 +12,24 @@ import android.widget.ImageView;
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
+import androidx.core.content.ContextCompat;
 import androidx.databinding.DataBindingUtil;
 
+import com.google.android.material.color.MaterialColors;
 import com.wefika.flowlayout.FlowLayout;
 
 import eu.siacs.conversations.AppSettings;
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.databinding.ItemContactBinding;
+import eu.siacs.conversations.entities.Contact;
 import eu.siacs.conversations.entities.ListItem;
+import eu.siacs.conversations.entities.Presence;
 import eu.siacs.conversations.ui.XmppActivity;
 import eu.siacs.conversations.ui.util.AvatarWorkerTask;
 import eu.siacs.conversations.utils.IrregularUnicodeDetector;
+import eu.siacs.conversations.utils.UIHelper;
+import eu.siacs.conversations.utils.XEP0392Helper;
 import eu.siacs.conversations.xmpp.Jid;
 
 import java.util.List;
@@ -54,7 +61,7 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
 	@Override
 	public View getView(int position, View view, @NonNull ViewGroup parent) {
 		LayoutInflater inflater = activity.getLayoutInflater();
-		ListItem item = getItem(position);
+		final ListItem item = getItem(position);
 		ViewHolder viewHolder;
 		if (view == null) {
 			final ItemContactBinding binding = DataBindingUtil.inflate(inflater,R.layout.item_contact,parent,false);
@@ -68,18 +75,46 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
 		}
 		//view.setBackground(StyledAttributes.getDrawable(view.getContext(),R.attr.list_item_background));
 		final List<ListItem.Tag> tags = item.getTags(activity);
-		if (tags.isEmpty() || !this.showDynamicTags) {
+		final boolean hasMetaTags;
+		if (item instanceof Contact contact) {
+			hasMetaTags = contact.isBlocked() || contact.getShownStatus() != Presence.Status.OFFLINE;
+		} else {
+			hasMetaTags = false;
+		}
+		if ((tags.isEmpty() && !hasMetaTags) || !this.showDynamicTags) {
 			viewHolder.tags.setVisibility(View.GONE);
 		} else {
 			viewHolder.tags.setVisibility(View.VISIBLE);
 			viewHolder.tags.removeAllViewsInLayout();
-			for (ListItem.Tag tag : tags) {
-				TextView tv = (TextView) inflater.inflate(R.layout.list_item_tag, viewHolder.tags, false);
-				tv.setText(tag.getName());
-				tv.setBackgroundColor(tag.getColor());
+			for (final ListItem.Tag tag : tags) {
+				final String name = tag.getName();
+				final TextView tv = (TextView) inflater.inflate(R.layout.list_item_tag, viewHolder.tags, false);
+				tv.setText(name);
+				tv.setBackgroundTintList(ColorStateList.valueOf(MaterialColors.harmonizeWithPrimary(getContext(),XEP0392Helper.rgbFromNick(name))));
 				tv.setOnClickListener(this.onTagTvClick);
 				viewHolder.tags.addView(tv);
 			}
+			if (item instanceof Contact contact) {
+				if (contact.isBlocked()) {
+					final TextView tv =
+							(TextView)
+									inflater.inflate(
+											R.layout.list_item_tag, viewHolder.tags, false);
+					tv.setText(R.string.blocked);
+					tv.setBackgroundTintList(ColorStateList.valueOf(MaterialColors.harmonizeWithPrimary(tv.getContext(),ContextCompat.getColor(tv.getContext(),R.color.gray_800))));
+					viewHolder.tags.addView(tv);
+				} else {
+                    final Presence.Status status = contact.getShownStatus();
+                    if (status != Presence.Status.OFFLINE) {
+                        final TextView tv =
+                                (TextView)
+                                        inflater.inflate(
+                                                R.layout.list_item_tag, viewHolder.tags, false);
+						UIHelper.setStatus(tv, status);
+						viewHolder.tags.addView(tv);
+                    }
+                }
+			}
 		}
 		final Jid jid = item.getJid();
 		if (jid != null) {

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

@@ -1,13 +1,19 @@
 package eu.siacs.conversations.utils;
 
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.text.SpannableStringBuilder;
 import android.text.format.DateFormat;
 import android.text.format.DateUtils;
 import android.util.Pair;
+import android.widget.TextView;
 
 import androidx.annotation.ColorInt;
+import androidx.annotation.ColorRes;
+import androidx.annotation.StringRes;
+import androidx.core.content.ContextCompat;
 
+import com.google.android.material.color.MaterialColors;
 import com.google.common.base.Strings;
 
 import java.math.BigInteger;
@@ -37,78 +43,6 @@ import eu.siacs.conversations.xmpp.Jid;
 
 public class UIHelper {
 
-    private static final int[] UNSAFE_COLORS = {
-            0xFFF44336, //red 500
-            0xFFE53935, //red 600
-            0xFFD32F2F, //red 700
-            0xFFC62828, //red 800
-
-            0xFFEF6C00, //orange 800
-
-            0xFFF4511E, //deep orange 600
-            0xFFE64A19, //deep orange 700
-            0xFFD84315, //deep orange 800,
-    };
-
-    private static final int[] SAFE_COLORS = {
-            0xFFE91E63, //pink 500
-            0xFFD81B60, //pink 600
-            0xFFC2185B, //pink 700
-            0xFFAD1457, //pink 800
-
-            0xFF9C27B0, //purple 500
-            0xFF8E24AA, //purple 600
-            0xFF7B1FA2, //purple 700
-            0xFF6A1B9A, //purple 800
-
-            0xFF673AB7, //deep purple 500,
-            0xFF5E35B1, //deep purple 600
-            0xFF512DA8, //deep purple 700
-            0xFF4527A0, //deep purple 800,
-
-            0xFF3F51B5, //indigo 500,
-            0xFF3949AB,//indigo 600
-            0xFF303F9F,//indigo 700
-            0xFF283593, //indigo 800
-
-            0xFF2196F3, //blue 500
-            0xFF1E88E5, //blue 600
-            0xFF1976D2, //blue 700
-            0xFF1565C0, //blue 800
-
-            0xFF03A9F4, //light blue 500
-            0xFF039BE5, //light blue 600
-            0xFF0288D1, //light blue 700
-            0xFF0277BD, //light blue 800
-
-            0xFF00BCD4, //cyan 500
-            0xFF00ACC1, //cyan 600
-            0xFF0097A7, //cyan 700
-            0xFF00838F, //cyan 800
-
-            0xFF009688, //teal 500,
-            0xFF00897B, //teal 600
-            0xFF00796B, //teal 700
-            0xFF00695C, //teal 800,
-
-            //0xFF558B2F, //light green 800
-
-            //0xFFC0CA33, //lime 600
-            0xFF9E9D24, //lime 800
-
-            0xFF795548, //brown 500,
-            //0xFF4E342E, //brown 800
-            0xFF607D8B, //blue grey 500,
-            //0xFF37474F //blue grey 800
-    };
-
-    private static final int[] COLORS;
-
-    static {
-        COLORS = Arrays.copyOf(SAFE_COLORS, SAFE_COLORS.length + UNSAFE_COLORS.length);
-        System.arraycopy(UNSAFE_COLORS, 0, COLORS, SAFE_COLORS.length, UNSAFE_COLORS.length);
-    }
-
     private static final List<String> LOCATION_QUESTIONS = Arrays.asList(
             "where are you", //en
             "where are you now", //en
@@ -226,32 +160,11 @@ public class UIHelper {
         }
     }
 
-    public static int getColorForName(String name) {
-        return getColorForName(name, false);
-    }
 
-    public static int getColorForName(String name, boolean safe) {
-        if (Config.XEP_0392) {
-            return XEP0392Helper.rgbFromNick(name);
-        }
-        if (name == null || name.isEmpty()) {
-            return 0xFF202020;
-        }
-        if (safe) {
-            return SAFE_COLORS[(int) (getLongForName(name) % SAFE_COLORS.length)];
-        } else {
-            return COLORS[(int) (getLongForName(name) % COLORS.length)];
-        }
+    public static int getColorForName(final String name) {
+        return XEP0392Helper.rgbFromNick(name);
     }
 
-    private static long getLongForName(String name) {
-        try {
-            final MessageDigest messageDigest = MessageDigest.getInstance("MD5");
-            return Math.abs(new BigInteger(messageDigest.digest(name.getBytes())).longValue());
-        } catch (Exception e) {
-            return 0;
-        }
-    }
 
     public static Pair<CharSequence, Boolean> getMessagePreview(final Context context, final Message message) {
         return getMessagePreview(context, message, 0);
@@ -589,19 +502,38 @@ public class UIHelper {
         return LOCATION_QUESTIONS.contains(body);
     }
 
-    public static ListItem.Tag getTagForStatus(Context context, Presence.Status status) {
-        switch (status) {
-            case CHAT:
-                return new ListItem.Tag(context.getString(R.string.presence_chat), 0xff259b24);
-            case AWAY:
-                return new ListItem.Tag(context.getString(R.string.presence_away), 0xffff9800);
-            case XA:
-                return new ListItem.Tag(context.getString(R.string.presence_xa), 0xfff44336);
-            case DND:
-                return new ListItem.Tag(context.getString(R.string.presence_dnd), 0xfff44336);
-            default:
-                return new ListItem.Tag(context.getString(R.string.presence_online), 0xff259b24);
-        }
+    public static void setStatus(final TextView textView, Presence.Status status) {
+        final @StringRes int text;
+        final @ColorRes int color =
+                switch (status) {
+                    case CHAT -> {
+                        text = R.string.presence_chat;
+                        yield R.color.green_800;
+                    }
+                    case ONLINE -> {
+                        text = R.string.presence_online;
+                        yield R.color.green_800;
+                    }
+                    case AWAY -> {
+                        text = R.string.presence_away;
+                        yield R.color.amber_800;
+                    }
+                    case XA -> {
+                        text = R.string.presence_xa;
+                        yield R.color.orange_800;
+                    }
+                    case DND -> {
+                        text = R.string.presence_dnd;
+                        yield R.color.red_800;
+                    }
+                    default -> throw new IllegalStateException();
+                };
+        textView.setText(text);
+        textView.setBackgroundTintList(
+                ColorStateList.valueOf(
+                        MaterialColors.harmonizeWithPrimary(
+                                textView.getContext(),
+                                ContextCompat.getColor(textView.getContext(), color))));
     }
 
     public static String filesizeToString(long size) {

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

@@ -7,7 +7,7 @@ import org.hsluv.HUSLColorConverter;
 import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
 
-class XEP0392Helper {
+public class XEP0392Helper {
 
     private static double angle(String nickname) {
         try {
@@ -20,7 +20,7 @@ class XEP0392Helper {
         }
     }
 
-    static int rgbFromNick(String name) {
+    public static int rgbFromNick(String name) {
         double[] hsluv = new double[3];
         hsluv[0] = angle(name) * 360;
         hsluv[1] = 100;

src/main/res/drawable/background_label.xml 🔗

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="@android:color/white" />
+    <corners android:radius="5dp" />
+</shape>

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

@@ -1,12 +1,15 @@
 <?xml version="1.0" encoding="utf-8"?>
 <TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:layout_margin="2dp"
+    android:layout_marginHorizontal="2dp"
+    android:layout_marginVertical="4dp"
+    android:background="@drawable/background_label"
+    android:backgroundTint="@color/green_700"
     android:maxLines="1"
-    android:paddingLeft="4dp"
-    android:paddingTop="1dp"
-    android:paddingRight="4dp"
-    android:paddingBottom="1dp"
-    android:textAllCaps="true"
-    android:textAppearance="?textAppearanceLabelLarge" />
+    android:minHeight="10dp"
+    android:paddingHorizontal="8dp"
+    android:textAppearance="?textAppearanceLabelMedium"
+    android:textColor="@android:color/white"
+    tools:text="Friends" />