display irregular unicode code points
This commit is contained in:
parent
52135625d8
commit
6944c12186
|
@ -63,16 +63,6 @@ public class Bookmark extends Element implements ListItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDisplayJid() {
|
|
||||||
Jid jid = getJid();
|
|
||||||
if (jid != null) {
|
|
||||||
return jid.toString();
|
|
||||||
} else {
|
|
||||||
return getAttribute("jid"); //fallback if jid wasn't parsable
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Jid getJid() {
|
public Jid getJid() {
|
||||||
return this.getAttributeAsJid("jid");
|
return this.getAttributeAsJid("jid");
|
||||||
|
|
|
@ -127,15 +127,6 @@ public class Contact implements ListItem, Blockable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDisplayJid() {
|
|
||||||
if (jid != null) {
|
|
||||||
return jid.toString();
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getProfilePhoto() {
|
public String getProfilePhoto() {
|
||||||
return this.photoUri;
|
return this.photoUri;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,6 @@ import rocks.xmpp.addr.Jid;
|
||||||
public interface ListItem extends Comparable<ListItem> {
|
public interface ListItem extends Comparable<ListItem> {
|
||||||
String getDisplayName();
|
String getDisplayName();
|
||||||
|
|
||||||
String getDisplayJid();
|
|
||||||
|
|
||||||
Jid getJid();
|
Jid getJid();
|
||||||
|
|
||||||
List<Tag> getTags(Context context);
|
List<Tag> getTags(Context context);
|
||||||
|
|
|
@ -38,6 +38,7 @@ import eu.siacs.conversations.entities.Contact;
|
||||||
import eu.siacs.conversations.entities.ListItem;
|
import eu.siacs.conversations.entities.ListItem;
|
||||||
import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
|
import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
|
||||||
import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate;
|
import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate;
|
||||||
|
import eu.siacs.conversations.utils.IrregularUnicodeBlockDetector;
|
||||||
import eu.siacs.conversations.utils.UIHelper;
|
import eu.siacs.conversations.utils.UIHelper;
|
||||||
import eu.siacs.conversations.utils.XmppUri;
|
import eu.siacs.conversations.utils.XmppUri;
|
||||||
import eu.siacs.conversations.xml.Namespace;
|
import eu.siacs.conversations.xml.Namespace;
|
||||||
|
@ -129,8 +130,7 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(
|
AlertDialog.Builder builder = new AlertDialog.Builder(
|
||||||
ContactDetailsActivity.this);
|
ContactDetailsActivity.this);
|
||||||
builder.setTitle(getString(R.string.action_add_phone_book));
|
builder.setTitle(getString(R.string.action_add_phone_book));
|
||||||
builder.setMessage(getString(R.string.add_phone_book_text,
|
builder.setMessage(getString(R.string.add_phone_book_text, contact.getJid().toString()));
|
||||||
contact.getDisplayJid()));
|
|
||||||
builder.setNegativeButton(getString(R.string.cancel), null);
|
builder.setNegativeButton(getString(R.string.cancel), null);
|
||||||
builder.setPositiveButton(getString(R.string.add), addToPhonebook);
|
builder.setPositiveButton(getString(R.string.add), addToPhonebook);
|
||||||
builder.create().show();
|
builder.create().show();
|
||||||
|
@ -235,9 +235,7 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
|
||||||
break;
|
break;
|
||||||
case R.id.action_delete_contact:
|
case R.id.action_delete_contact:
|
||||||
builder.setTitle(getString(R.string.action_delete_contact))
|
builder.setTitle(getString(R.string.action_delete_contact))
|
||||||
.setMessage(
|
.setMessage(getString(R.string.remove_contact_text, contact.getJid().toString()))
|
||||||
getString(R.string.remove_contact_text,
|
|
||||||
contact.getDisplayJid()))
|
|
||||||
.setPositiveButton(getString(R.string.delete),
|
.setPositiveButton(getString(R.string.delete),
|
||||||
removeFromRoster).create().show();
|
removeFromRoster).create().show();
|
||||||
break;
|
break;
|
||||||
|
@ -386,12 +384,7 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contact.getPresences().size() > 1) {
|
binding.detailsContactjid.setText(IrregularUnicodeBlockDetector.style(this,contact.getJid()));
|
||||||
binding.detailsContactjid.setText(contact.getDisplayJid() + " ("
|
|
||||||
+ contact.getPresences().size() + ")");
|
|
||||||
} else {
|
|
||||||
binding.detailsContactjid.setText(contact.getDisplayJid());
|
|
||||||
}
|
|
||||||
String account;
|
String account;
|
||||||
if (Config.DOMAIN_LOCK != null) {
|
if (Config.DOMAIN_LOCK != null) {
|
||||||
account = contact.getAccount().getJid().getLocal();
|
account = contact.getAccount().getJid().getLocal();
|
||||||
|
|
|
@ -35,6 +35,7 @@ import eu.siacs.conversations.databinding.KeysCardBinding;
|
||||||
import eu.siacs.conversations.entities.Account;
|
import eu.siacs.conversations.entities.Account;
|
||||||
import eu.siacs.conversations.entities.Conversation;
|
import eu.siacs.conversations.entities.Conversation;
|
||||||
import eu.siacs.conversations.utils.CryptoHelper;
|
import eu.siacs.conversations.utils.CryptoHelper;
|
||||||
|
import eu.siacs.conversations.utils.IrregularUnicodeBlockDetector;
|
||||||
import eu.siacs.conversations.utils.XmppUri;
|
import eu.siacs.conversations.utils.XmppUri;
|
||||||
import eu.siacs.conversations.xmpp.OnKeyStatusUpdated;
|
import eu.siacs.conversations.xmpp.OnKeyStatusUpdated;
|
||||||
import rocks.xmpp.addr.Jid;
|
import rocks.xmpp.addr.Jid;
|
||||||
|
@ -195,9 +196,8 @@ public class TrustKeysActivity extends OmemoActivity implements OnKeyStatusUpdat
|
||||||
for (Map.Entry<Jid, Map<String, Boolean>> entry : foreignKeysToTrust.entrySet()) {
|
for (Map.Entry<Jid, Map<String, Boolean>> entry : foreignKeysToTrust.entrySet()) {
|
||||||
hasForeignKeys = true;
|
hasForeignKeys = true;
|
||||||
KeysCardBinding keysCardBinding = DataBindingUtil.inflate(getLayoutInflater(),R.layout.keys_card, binding.foreignKeys,false);
|
KeysCardBinding keysCardBinding = DataBindingUtil.inflate(getLayoutInflater(),R.layout.keys_card, binding.foreignKeys,false);
|
||||||
//final LinearLayout layout = (LinearLayout) getLayoutInflater().inflate(R.layout.keys_card, foreignKeys, false);
|
|
||||||
final Jid jid = entry.getKey();
|
final Jid jid = entry.getKey();
|
||||||
keysCardBinding.foreignKeysTitle.setText(jid.toString());
|
keysCardBinding.foreignKeysTitle.setText(IrregularUnicodeBlockDetector.style(this,jid));
|
||||||
keysCardBinding.foreignKeysTitle.setOnClickListener(v -> switchToContactDetails(mAccount.getRoster().getContact(jid)));
|
keysCardBinding.foreignKeysTitle.setOnClickListener(v -> switchToContactDetails(mAccount.getRoster().getContact(jid)));
|
||||||
final Map<String, Boolean> fingerprints = entry.getValue();
|
final Map<String, Boolean> fingerprints = entry.getValue();
|
||||||
for (final String fingerprint : fingerprints.keySet()) {
|
for (final String fingerprint : fingerprints.keySet()) {
|
||||||
|
@ -397,7 +397,7 @@ public class TrustKeysActivity extends OmemoActivity implements OnKeyStatusUpdat
|
||||||
fingerprint,
|
fingerprint,
|
||||||
FingerprintStatus.createActive(ownKeysToTrust.get(fingerprint)));
|
FingerprintStatus.createActive(ownKeysToTrust.get(fingerprint)));
|
||||||
}
|
}
|
||||||
List<Jid> acceptedTargets = mConversation == null ? new ArrayList<Jid>() : mConversation.getAcceptedCryptoTargets();
|
List<Jid> acceptedTargets = mConversation == null ? new ArrayList<>() : mConversation.getAcceptedCryptoTargets();
|
||||||
synchronized (this.foreignKeysToTrust) {
|
synchronized (this.foreignKeysToTrust) {
|
||||||
for (Map.Entry<Jid, Map<String, Boolean>> entry : foreignKeysToTrust.entrySet()) {
|
for (Map.Entry<Jid, Map<String, Boolean>> entry : foreignKeysToTrust.entrySet()) {
|
||||||
Jid jid = entry.getKey();
|
Jid jid = entry.getKey();
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package eu.siacs.conversations.ui.adapter;
|
package eu.siacs.conversations.ui.adapter;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.databinding.DataBindingUtil;
|
import android.databinding.DataBindingUtil;
|
||||||
|
@ -27,8 +26,11 @@ import eu.siacs.conversations.databinding.ContactBinding;
|
||||||
import eu.siacs.conversations.entities.ListItem;
|
import eu.siacs.conversations.entities.ListItem;
|
||||||
import eu.siacs.conversations.ui.SettingsActivity;
|
import eu.siacs.conversations.ui.SettingsActivity;
|
||||||
import eu.siacs.conversations.ui.XmppActivity;
|
import eu.siacs.conversations.ui.XmppActivity;
|
||||||
|
import eu.siacs.conversations.ui.util.Color;
|
||||||
import eu.siacs.conversations.utils.EmojiWrapper;
|
import eu.siacs.conversations.utils.EmojiWrapper;
|
||||||
|
import eu.siacs.conversations.utils.IrregularUnicodeBlockDetector;
|
||||||
import eu.siacs.conversations.utils.UIHelper;
|
import eu.siacs.conversations.utils.UIHelper;
|
||||||
|
import rocks.xmpp.addr.Jid;
|
||||||
|
|
||||||
public class ListItemAdapter extends ArrayAdapter<ListItem> {
|
public class ListItemAdapter extends ArrayAdapter<ListItem> {
|
||||||
|
|
||||||
|
@ -108,10 +110,10 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
|
||||||
viewHolder.tags.addView(tv);
|
viewHolder.tags.addView(tv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final String jid = item.getDisplayJid();
|
final Jid jid = item.getJid();
|
||||||
if (jid != null) {
|
if (jid != null) {
|
||||||
viewHolder.jid.setVisibility(View.VISIBLE);
|
viewHolder.jid.setVisibility(View.VISIBLE);
|
||||||
viewHolder.jid.setText(jid);
|
viewHolder.jid.setText(IrregularUnicodeBlockDetector.style(activity, jid));
|
||||||
} else {
|
} else {
|
||||||
viewHolder.jid.setVisibility(View.GONE);
|
viewHolder.jid.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,160 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Daniel Gultsch All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
* other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* 3. Neither the name of the copyright holder nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software without
|
||||||
|
* specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package eu.siacs.conversations.utils;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.annotation.ColorInt;
|
||||||
|
import android.text.Spannable;
|
||||||
|
import android.text.SpannableString;
|
||||||
|
import android.text.SpannableStringBuilder;
|
||||||
|
import android.text.style.ForegroundColorSpan;
|
||||||
|
import android.util.LruCache;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.R;
|
||||||
|
import eu.siacs.conversations.ui.util.Color;
|
||||||
|
import rocks.xmpp.addr.Jid;
|
||||||
|
|
||||||
|
public class IrregularUnicodeBlockDetector {
|
||||||
|
|
||||||
|
private static final Map<Character.UnicodeBlock,Character.UnicodeBlock> NORMALIZATION_MAP;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Map<Character.UnicodeBlock,Character.UnicodeBlock> temp = new HashMap<>();
|
||||||
|
temp.put(Character.UnicodeBlock.LATIN_1_SUPPLEMENT, Character.UnicodeBlock.BASIC_LATIN);
|
||||||
|
NORMALIZATION_MAP = Collections.unmodifiableMap(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Character.UnicodeBlock normalize(Character.UnicodeBlock in) {
|
||||||
|
if (NORMALIZATION_MAP.containsKey(in)) {
|
||||||
|
return NORMALIZATION_MAP.get(in);
|
||||||
|
} else {
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final LruCache<Jid, Pattern> CACHE = new LruCache<>(100);
|
||||||
|
|
||||||
|
public static Spannable style(Context context, Jid jid) {
|
||||||
|
return style(jid, Color.get(context, R.attr.color_warning));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Spannable style(Jid jid, @ColorInt int color) {
|
||||||
|
SpannableStringBuilder builder = new SpannableStringBuilder();
|
||||||
|
if (jid.getLocal() != null) {
|
||||||
|
SpannableString local = new SpannableString(jid.getLocal());
|
||||||
|
Matcher matcher = find(jid).matcher(local);
|
||||||
|
while (matcher.find()) {
|
||||||
|
if (matcher.start() < matcher.end()) {
|
||||||
|
local.setSpan(new ForegroundColorSpan(color), matcher.start(), matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
builder.append(local);
|
||||||
|
builder.append('@');
|
||||||
|
}
|
||||||
|
if (jid.getDomain() != null) {
|
||||||
|
builder.append(jid.getDomain());
|
||||||
|
}
|
||||||
|
if (builder.length() != 0 && jid.getResource() != null) {
|
||||||
|
builder.append('/');
|
||||||
|
builder.append(jid.getResource());
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<Character.UnicodeBlock, List<String>> map(Jid jid) {
|
||||||
|
Map<Character.UnicodeBlock, List<String>> map = new HashMap<>();
|
||||||
|
String local = jid.getLocal();
|
||||||
|
final int length = local.length();
|
||||||
|
for (int offset = 0; offset < length; ) {
|
||||||
|
final int codePoint = local.codePointAt(offset);
|
||||||
|
Character.UnicodeBlock block = normalize(Character.UnicodeBlock.of(codePoint));
|
||||||
|
List<String> codePoints;
|
||||||
|
if (map.containsKey(block)) {
|
||||||
|
codePoints = map.get(block);
|
||||||
|
} else {
|
||||||
|
codePoints = new ArrayList<>();
|
||||||
|
map.put(block, codePoints);
|
||||||
|
}
|
||||||
|
codePoints.add(String.copyValueOf(Character.toChars(codePoint)));
|
||||||
|
offset += Character.charCount(codePoint);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Set<String> eliminateFirstAndGetCodePoints(Map<Character.UnicodeBlock, List<String>> map) {
|
||||||
|
Character.UnicodeBlock block = Character.UnicodeBlock.BASIC_LATIN;
|
||||||
|
int size = 0;
|
||||||
|
for (Map.Entry<Character.UnicodeBlock, List<String>> entry : map.entrySet()) {
|
||||||
|
if (entry.getValue().size() > size) {
|
||||||
|
size = entry.getValue().size();
|
||||||
|
block = entry.getKey();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map.remove(block);
|
||||||
|
Set<String> all = new HashSet<>();
|
||||||
|
for (List<String> codePoints : map.values()) {
|
||||||
|
all.addAll(codePoints);
|
||||||
|
}
|
||||||
|
return all;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Pattern find(Jid jid) {
|
||||||
|
synchronized (CACHE) {
|
||||||
|
Pattern pattern = CACHE.get(jid);
|
||||||
|
if (pattern != null) {
|
||||||
|
return pattern;
|
||||||
|
}
|
||||||
|
pattern = create(eliminateFirstAndGetCodePoints(map(jid)));
|
||||||
|
CACHE.put(jid, pattern);
|
||||||
|
return pattern;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Pattern create(Set<String> codePoints) {
|
||||||
|
final StringBuilder pattern = new StringBuilder();
|
||||||
|
for (String codePoint : codePoints) {
|
||||||
|
if (pattern.length() != 0) {
|
||||||
|
pattern.append('|');
|
||||||
|
}
|
||||||
|
pattern.append(Pattern.quote(codePoint));
|
||||||
|
}
|
||||||
|
return Pattern.compile(pattern.toString());
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
<attr name="color_background_secondary" format="reference|color" />
|
<attr name="color_background_secondary" format="reference|color" />
|
||||||
<attr name="color_background_primary" format="reference|color" />
|
<attr name="color_background_primary" format="reference|color" />
|
||||||
|
<attr name="color_warning" format="reference|color"/>
|
||||||
|
|
||||||
<attr name="ic_send_cancel_offline" format="reference"/>
|
<attr name="ic_send_cancel_offline" format="reference"/>
|
||||||
<attr name="ic_send_location_offline" format="reference"/>
|
<attr name="ic_send_location_offline" format="reference"/>
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
<color name="grey800">#ff424242</color>
|
<color name="grey800">#ff424242</color>
|
||||||
<color name="grey900">#ff282828</color>
|
<color name="grey900">#ff282828</color>
|
||||||
<color name="red500">#fff44336</color>
|
<color name="red500">#fff44336</color>
|
||||||
|
<color name="red_a700">#ffd50000</color>
|
||||||
|
<color name="red_a100">#ffff8a80</color>
|
||||||
<color name="red800">#ffc62828</color>
|
<color name="red800">#ffc62828</color>
|
||||||
<color name="orange500">#ffff9800</color>
|
<color name="orange500">#ffff9800</color>
|
||||||
<color name="green500">#ff259b24</color>
|
<color name="green500">#ff259b24</color>
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
<item name="color_background_primary">@color/grey50</item>
|
<item name="color_background_primary">@color/grey50</item>
|
||||||
<item name="color_background_secondary">@color/grey200</item>
|
<item name="color_background_secondary">@color/grey200</item>
|
||||||
|
<item name="color_warning">@color/red_a700</item>
|
||||||
|
|
||||||
<item name="android:windowActionModeOverlay">true</item>
|
<item name="android:windowActionModeOverlay">true</item>
|
||||||
<item name="android:actionModeBackground">@color/accent</item>
|
<item name="android:actionModeBackground">@color/accent</item>
|
||||||
|
@ -84,6 +85,7 @@
|
||||||
|
|
||||||
<item name="color_background_primary">@color/grey800</item>
|
<item name="color_background_primary">@color/grey800</item>
|
||||||
<item name="color_background_secondary">@color/grey900</item>
|
<item name="color_background_secondary">@color/grey900</item>
|
||||||
|
<item name="color_warning">@color/red_a100</item>
|
||||||
|
|
||||||
<item name="android:windowActionModeOverlay">true</item>
|
<item name="android:windowActionModeOverlay">true</item>
|
||||||
<item name="android:actionModeBackground">@color/accent</item>
|
<item name="android:actionModeBackground">@color/accent</item>
|
||||||
|
|
Loading…
Reference in a new issue