style dynamic tags

This commit is contained in:
Daniel Gultsch 2024-04-09 11:09:14 +02:00
parent 0ec46d1d34
commit e8bdd5de7a
No known key found for this signature in database
GPG key ID: F43D18AD2A0982C2
10 changed files with 144 additions and 152 deletions

View file

@ -81,7 +81,6 @@ public final class Config {
public static final int CONNECT_DISCO_TIMEOUT = 20; public static final int CONNECT_DISCO_TIMEOUT = 20;
public static final int MINI_GRACE_PERIOD = 750; 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 // media file formats. Homogenous Android or Conversations only deployments can switch to opus
// and webp // and webp

View file

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

View file

@ -180,17 +180,10 @@ public class Contact implements ListItem, Blockable {
} }
@Override @Override
public List<Tag> getTags(Context context) { public List<Tag> getTags(final Context context) {
final ArrayList<Tag> tags = new ArrayList<>(); final ArrayList<Tag> tags = new ArrayList<>();
for (final String group : getGroups(true)) { for (final String group : getGroups(true)) {
tags.add(new Tag(group, UIHelper.getColorForName(group))); tags.add(new Tag(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));
} }
return tags; return tags;
} }

View file

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

View file

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

View file

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

View file

@ -1,13 +1,19 @@
package eu.siacs.conversations.utils; package eu.siacs.conversations.utils;
import android.content.Context; import android.content.Context;
import android.content.res.ColorStateList;
import android.text.SpannableStringBuilder; import android.text.SpannableStringBuilder;
import android.text.format.DateFormat; import android.text.format.DateFormat;
import android.text.format.DateUtils; import android.text.format.DateUtils;
import android.util.Pair; import android.util.Pair;
import android.widget.TextView;
import androidx.annotation.ColorInt; 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 com.google.common.base.Strings;
import java.math.BigInteger; import java.math.BigInteger;
@ -37,78 +43,6 @@ import eu.siacs.conversations.xmpp.Jid;
public class UIHelper { 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( private static final List<String> LOCATION_QUESTIONS = Arrays.asList(
"where are you", //en "where are you", //en
"where are you now", //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) { public static int getColorForName(final String name) {
if (Config.XEP_0392) {
return XEP0392Helper.rgbFromNick(name); 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)];
}
}
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) { public static Pair<CharSequence, Boolean> getMessagePreview(final Context context, final Message message) {
return getMessagePreview(context, message, 0); return getMessagePreview(context, message, 0);
@ -589,19 +502,38 @@ public class UIHelper {
return LOCATION_QUESTIONS.contains(body); return LOCATION_QUESTIONS.contains(body);
} }
public static ListItem.Tag getTagForStatus(Context context, Presence.Status status) { public static void setStatus(final TextView textView, Presence.Status status) {
final @StringRes int text;
final @ColorRes int color =
switch (status) { switch (status) {
case CHAT: case CHAT -> {
return new ListItem.Tag(context.getString(R.string.presence_chat), 0xff259b24); text = R.string.presence_chat;
case AWAY: yield R.color.green_800;
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);
} }
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) { public static String filesizeToString(long size) {

View file

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

View file

@ -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>

View file

@ -1,12 +1,15 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android" <TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="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:maxLines="1"
android:paddingLeft="4dp" android:minHeight="10dp"
android:paddingTop="1dp" android:paddingHorizontal="8dp"
android:paddingRight="4dp" android:textAppearance="?textAppearanceLabelMedium"
android:paddingBottom="1dp" android:textColor="@android:color/white"
android:textAllCaps="true" tools:text="Friends" />
android:textAppearance="?textAppearanceLabelLarge" />