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 | ||||
| 	public Jid getJid() { | ||||
| 		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() { | ||||
| 		return this.photoUri; | ||||
| 	} | ||||
|  | ||||
| @ -10,8 +10,6 @@ import rocks.xmpp.addr.Jid; | ||||
| public interface ListItem extends Comparable<ListItem> { | ||||
| 	String getDisplayName(); | ||||
| 
 | ||||
| 	String getDisplayJid(); | ||||
| 
 | ||||
| 	Jid getJid(); | ||||
| 
 | ||||
| 	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.services.XmppConnectionService.OnAccountUpdate; | ||||
| import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate; | ||||
| import eu.siacs.conversations.utils.IrregularUnicodeBlockDetector; | ||||
| import eu.siacs.conversations.utils.UIHelper; | ||||
| import eu.siacs.conversations.utils.XmppUri; | ||||
| import eu.siacs.conversations.xml.Namespace; | ||||
| @ -129,8 +130,7 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp | ||||
| 				AlertDialog.Builder builder = new AlertDialog.Builder( | ||||
| 						ContactDetailsActivity.this); | ||||
| 				builder.setTitle(getString(R.string.action_add_phone_book)); | ||||
| 				builder.setMessage(getString(R.string.add_phone_book_text, | ||||
| 						contact.getDisplayJid())); | ||||
| 				builder.setMessage(getString(R.string.add_phone_book_text, contact.getJid().toString())); | ||||
| 				builder.setNegativeButton(getString(R.string.cancel), null); | ||||
| 				builder.setPositiveButton(getString(R.string.add), addToPhonebook); | ||||
| 				builder.create().show(); | ||||
| @ -235,9 +235,7 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp | ||||
| 				break; | ||||
| 			case R.id.action_delete_contact: | ||||
| 				builder.setTitle(getString(R.string.action_delete_contact)) | ||||
| 					.setMessage( | ||||
| 							getString(R.string.remove_contact_text, | ||||
| 								contact.getDisplayJid())) | ||||
| 					.setMessage(getString(R.string.remove_contact_text, contact.getJid().toString())) | ||||
| 					.setPositiveButton(getString(R.string.delete), | ||||
| 							removeFromRoster).create().show(); | ||||
| 				break; | ||||
| @ -386,12 +384,7 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if (contact.getPresences().size() > 1) { | ||||
| 			binding.detailsContactjid.setText(contact.getDisplayJid() + " (" | ||||
| 					+ contact.getPresences().size() + ")"); | ||||
| 		} else { | ||||
| 			binding.detailsContactjid.setText(contact.getDisplayJid()); | ||||
| 		} | ||||
| 		binding.detailsContactjid.setText(IrregularUnicodeBlockDetector.style(this,contact.getJid())); | ||||
| 		String account; | ||||
| 		if (Config.DOMAIN_LOCK != null) { | ||||
| 			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.Conversation; | ||||
| import eu.siacs.conversations.utils.CryptoHelper; | ||||
| import eu.siacs.conversations.utils.IrregularUnicodeBlockDetector; | ||||
| import eu.siacs.conversations.utils.XmppUri; | ||||
| import eu.siacs.conversations.xmpp.OnKeyStatusUpdated; | ||||
| 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()) { | ||||
| 				hasForeignKeys = true; | ||||
| 				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(); | ||||
| 				keysCardBinding.foreignKeysTitle.setText(jid.toString()); | ||||
| 				keysCardBinding.foreignKeysTitle.setText(IrregularUnicodeBlockDetector.style(this,jid)); | ||||
| 				keysCardBinding.foreignKeysTitle.setOnClickListener(v -> switchToContactDetails(mAccount.getRoster().getContact(jid))); | ||||
| 				final Map<String, Boolean> fingerprints = entry.getValue(); | ||||
| 				for (final String fingerprint : fingerprints.keySet()) { | ||||
| @ -397,7 +397,7 @@ public class TrustKeysActivity extends OmemoActivity implements OnKeyStatusUpdat | ||||
| 					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) { | ||||
| 			for (Map.Entry<Jid, Map<String, Boolean>> entry : foreignKeysToTrust.entrySet()) { | ||||
| 				Jid jid = entry.getKey(); | ||||
|  | ||||
| @ -1,6 +1,5 @@ | ||||
| package eu.siacs.conversations.ui.adapter; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.content.SharedPreferences; | ||||
| import android.content.res.Resources; | ||||
| import android.databinding.DataBindingUtil; | ||||
| @ -27,8 +26,11 @@ import eu.siacs.conversations.databinding.ContactBinding; | ||||
| import eu.siacs.conversations.entities.ListItem; | ||||
| import eu.siacs.conversations.ui.SettingsActivity; | ||||
| import eu.siacs.conversations.ui.XmppActivity; | ||||
| import eu.siacs.conversations.ui.util.Color; | ||||
| import eu.siacs.conversations.utils.EmojiWrapper; | ||||
| import eu.siacs.conversations.utils.IrregularUnicodeBlockDetector; | ||||
| import eu.siacs.conversations.utils.UIHelper; | ||||
| import rocks.xmpp.addr.Jid; | ||||
| 
 | ||||
| public class ListItemAdapter extends ArrayAdapter<ListItem> { | ||||
| 
 | ||||
| @ -108,10 +110,10 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> { | ||||
| 				viewHolder.tags.addView(tv); | ||||
| 			} | ||||
| 		} | ||||
| 		final String jid = item.getDisplayJid(); | ||||
| 		final Jid jid = item.getJid(); | ||||
| 		if (jid != null) { | ||||
| 			viewHolder.jid.setVisibility(View.VISIBLE); | ||||
| 			viewHolder.jid.setText(jid); | ||||
| 			viewHolder.jid.setText(IrregularUnicodeBlockDetector.style(activity, jid)); | ||||
| 		} else { | ||||
| 			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_primary" format="reference|color" /> | ||||
|     <attr name="color_warning" format="reference|color"/> | ||||
| 
 | ||||
|     <attr name="ic_send_cancel_offline" format="reference"/> | ||||
|     <attr name="ic_send_location_offline" format="reference"/> | ||||
|  | ||||
| @ -19,6 +19,8 @@ | ||||
| 	<color name="grey800">#ff424242</color> | ||||
| 	<color name="grey900">#ff282828</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="orange500">#ffff9800</color> | ||||
| 	<color name="green500">#ff259b24</color> | ||||
|  | ||||
| @ -8,6 +8,7 @@ | ||||
| 
 | ||||
|         <item name="color_background_primary">@color/grey50</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:actionModeBackground">@color/accent</item> | ||||
| @ -84,6 +85,7 @@ | ||||
| 
 | ||||
|         <item name="color_background_primary">@color/grey800</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:actionModeBackground">@color/accent</item> | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Daniel Gultsch
						Daniel Gultsch