limit links to actual URIs and include more well known schemes

Daniel Gultsch created

links without scheme like 'example.com' or no longer linked to avoid
false positives. however now mailto and tel are linked as well

Change summary

src/main/java/eu/siacs/conversations/entities/Message.java             |   4 
src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java |   2 
src/main/java/eu/siacs/conversations/ui/ConversationFragment.java      |   3 
src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java    |   2 
src/main/java/eu/siacs/conversations/ui/text/FixedURLSpan.java         |   9 
src/main/java/eu/siacs/conversations/ui/util/MyLinkify.java            | 115 
src/main/java/eu/siacs/conversations/ui/util/ShareUtil.java            |   2 
src/main/java/eu/siacs/conversations/utils/GeoHelper.java              | 289 
src/main/java/eu/siacs/conversations/utils/Patterns.java               |  70 
src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java          |   4 
10 files changed, 198 insertions(+), 302 deletions(-)

Detailed changes

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

@@ -16,9 +16,9 @@ import eu.siacs.conversations.services.AvatarService;
 import eu.siacs.conversations.ui.util.PresenceSelector;
 import eu.siacs.conversations.utils.CryptoHelper;
 import eu.siacs.conversations.utils.Emoticons;
-import eu.siacs.conversations.utils.GeoHelper;
 import eu.siacs.conversations.utils.MessageUtils;
 import eu.siacs.conversations.utils.MimeUtils;
+import eu.siacs.conversations.utils.Patterns;
 import eu.siacs.conversations.utils.UIHelper;
 import eu.siacs.conversations.xmpp.Jid;
 import java.lang.ref.WeakReference;
@@ -796,7 +796,7 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
 
     public synchronized boolean isGeoUri() {
         if (isGeoUri == null) {
-            isGeoUri = GeoHelper.GEO_URI.matcher(body).matches();
+            isGeoUri = Patterns.URI_GEO.matcher(body).matches();
         }
         return isGeoUri;
     }

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

@@ -558,7 +558,7 @@ public class ConferenceDetailsActivity extends XmppActivity
         if (printableValue(subject)) {
             SpannableStringBuilder spannable = new SpannableStringBuilder(subject);
             StylingHelper.format(spannable, this.binding.mucSubject.getCurrentTextColor());
-            MyLinkify.addLinks(spannable, false);
+            MyLinkify.addLinks(spannable);
             this.binding.mucSubject.setText(spannable);
             this.binding.mucSubject.setTextAppearance(
                     subject.length() > (hasTitle ? 128 : 196)

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

@@ -112,6 +112,7 @@ import eu.siacs.conversations.utils.Compatibility;
 import eu.siacs.conversations.utils.GeoHelper;
 import eu.siacs.conversations.utils.MessageUtils;
 import eu.siacs.conversations.utils.NickValidityChecker;
+import eu.siacs.conversations.utils.Patterns;
 import eu.siacs.conversations.utils.PermissionUtils;
 import eu.siacs.conversations.utils.QuickLoader;
 import eu.siacs.conversations.utils.StylingHelper;
@@ -2614,7 +2615,7 @@ public class ConversationFragment extends XmppFragment
                 }
             }
         } else {
-            if (text != null && GeoHelper.GEO_URI.matcher(text).matches()) {
+            if (text != null && Patterns.URI_GEO.matcher(text).matches()) {
                 mediaPreviewAdapter.addMediaPreviews(
                         Attachment.of(getActivity(), Uri.parse(text), Attachment.Type.LOCATION));
                 toggleInputMethod();

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

@@ -576,7 +576,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
         }
 
         StylingHelper.format(body, viewHolder.messageBody().getCurrentTextColor());
-        MyLinkify.addLinks(body, true);
+        MyLinkify.addLinks(body);
         if (highlightedTerm != null) {
             StylingHelper.highlight(viewHolder.messageBody(), body, highlightedTerm);
         }

src/main/java/eu/siacs/conversations/ui/text/FixedURLSpan.java 🔗

@@ -34,24 +34,21 @@ import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
-import android.os.Build;
 import android.text.Editable;
 import android.text.Spanned;
 import android.text.style.URLSpan;
 import android.view.SoundEffectConstants;
 import android.view.View;
 import android.widget.Toast;
-
-import java.util.Arrays;
-
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.ui.ConversationsActivity;
 import eu.siacs.conversations.ui.ShowLocationActivity;
+import java.util.Arrays;
 
 @SuppressLint("ParcelCreator")
 public class FixedURLSpan extends URLSpan {
 
-    private FixedURLSpan(String url) {
+    private FixedURLSpan(final String url) {
         super(url);
     }
 
@@ -93,7 +90,7 @@ public class FixedURLSpan extends URLSpan {
         try {
             context.startActivity(intent);
             widget.playSoundEffect(SoundEffectConstants.CLICK);
-        } catch (ActivityNotFoundException e) {
+        } catch (final ActivityNotFoundException e) {
             Toast.makeText(context, R.string.no_application_found_to_open_link, Toast.LENGTH_SHORT)
                     .show();
         }

src/main/java/eu/siacs/conversations/ui/util/MyLinkify.java 🔗

@@ -29,107 +29,47 @@
 
 package eu.siacs.conversations.ui.util;
 
-import android.os.Build;
 import android.text.Editable;
 import android.text.style.URLSpan;
 import android.text.util.Linkify;
-
+import com.google.common.base.Splitter;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
-
+import eu.siacs.conversations.ui.text.FixedURLSpan;
+import eu.siacs.conversations.utils.Patterns;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Comparator;
 import java.util.List;
-import java.util.Locale;
 import java.util.Objects;
 
-import eu.siacs.conversations.ui.text.FixedURLSpan;
-import eu.siacs.conversations.utils.GeoHelper;
-import eu.siacs.conversations.utils.Patterns;
-import eu.siacs.conversations.utils.XmppUri;
-
 public class MyLinkify {
 
-    private static final Linkify.TransformFilter WEBURL_TRANSFORM_FILTER = (matcher, url) -> {
-        if (url == null) {
-            return null;
-        }
-        final String lcUrl = url.toLowerCase(Locale.US);
-        if (lcUrl.startsWith("http://") || lcUrl.startsWith("https://")) {
-            return removeTrailingBracket(url);
-        } else {
-            return "http://" + removeTrailingBracket(url);
-        }
-    };
-
-    private static String removeTrailingBracket(final String url) {
-        int numOpenBrackets = 0;
-        for (char c : url.toCharArray()) {
-            if (c == '(') {
-                ++numOpenBrackets;
-            } else if (c == ')') {
-                --numOpenBrackets;
-            }
-        }
-        if (numOpenBrackets != 0 && url.charAt(url.length() - 1) == ')') {
-            return url.substring(0, url.length() - 1);
-        } else {
-            return url;
-        }
-    }
-
-    private static final Linkify.MatchFilter WEBURL_MATCH_FILTER = (cs, start, end) -> {
-        if (start > 0) {
-            if (cs.charAt(start - 1) == '@' || cs.charAt(start - 1) == '.'
-                    || cs.subSequence(Math.max(0, start - 3), start).equals("://")) {
-                return false;
-            }
-        }
-
-        if (end < cs.length()) {
-            // Reject strings that were probably matched only because they contain a dot followed by
-            // by some known TLD (see also comment for WORD_BOUNDARY in Patterns.java)
-            return !isAlphabetic(cs.charAt(end - 1)) || !isAlphabetic(cs.charAt(end));
-        }
-
-        return true;
-    };
-
-    private static final Linkify.MatchFilter XMPPURI_MATCH_FILTER = (s, start, end) -> {
-        XmppUri uri = new XmppUri(s.subSequence(start, end).toString());
-        return uri.isValidJid();
-    };
-
-    private static boolean isAlphabetic(final int code) {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
-            return Character.isAlphabetic(code);
-        }
-
-        switch (Character.getType(code)) {
-            case Character.UPPERCASE_LETTER:
-            case Character.LOWERCASE_LETTER:
-            case Character.TITLECASE_LETTER:
-            case Character.MODIFIER_LETTER:
-            case Character.OTHER_LETTER:
-            case Character.LETTER_NUMBER:
-                return true;
-            default:
-                return false;
-        }
-    }
-
-    public static void addLinks(Editable body, boolean includeGeo) {
-        Linkify.addLinks(body, Patterns.XMPP_PATTERN, "xmpp", XMPPURI_MATCH_FILTER, null);
-        Linkify.addLinks(body, Patterns.AUTOLINK_WEB_URL, "http", WEBURL_MATCH_FILTER, WEBURL_TRANSFORM_FILTER);
-        if (includeGeo) {
-            Linkify.addLinks(body, GeoHelper.GEO_URI, "geo");
-        }
+    private static final Linkify.MatchFilter MATCH_FILTER =
+            (s, start, end) -> {
+                final var match = s.subSequence(start, end);
+                final var scheme =
+                        Iterables.getFirst(Splitter.on(':').limit(2).splitToList(match), null);
+                if (scheme == null) {
+                    return false;
+                }
+                return switch (scheme) {
+                    case "tel" -> Patterns.URI_TEL.matcher(match).matches();
+                    case "http", "https" -> Patterns.URI_HTTP.matcher(match).matches();
+                    case "geo" -> Patterns.URI_GEO.matcher(match).matches();
+                    default -> true;
+                };
+            };
+
+    public static void addLinks(final Editable body) {
+        Linkify.addLinks(body, Patterns.URI_GENERIC, null, MATCH_FILTER, null);
         FixedURLSpan.fix(body);
     }
 
     public static List<String> extractLinks(final Editable body) {
-        MyLinkify.addLinks(body, false);
+        MyLinkify.addLinks(body);
         final Collection<URLSpan> spans =
                 Arrays.asList(body.getSpans(0, body.length() - 1, URLSpan.class));
         final Collection<UrlWrapper> urlWrappers =
@@ -140,11 +80,10 @@ public class MyLinkify {
                                         s == null
                                                 ? null
                                                 : new UrlWrapper(body.getSpanStart(s), s.getURL())),
-                        uw -> uw != null);
-        List<UrlWrapper> sorted = ImmutableList.sortedCopyOf(
-                (a, b) -> Integer.compare(a.position, b.position), urlWrappers);
+                        Objects::nonNull);
+        List<UrlWrapper> sorted =
+                ImmutableList.sortedCopyOf(Comparator.comparingInt(a -> a.position), urlWrappers);
         return Lists.transform(sorted, uw -> uw.url);
-
     }
 
     private static class UrlWrapper {

src/main/java/eu/siacs/conversations/ui/util/ShareUtil.java 🔗

@@ -149,7 +149,7 @@ public class ShareUtil {
     }
 
     public static String getLinkScheme(final SpannableStringBuilder body) {
-        MyLinkify.addLinks(body, false);
+        MyLinkify.addLinks(body);
         for (final String url : MyLinkify.extractLinks(body)) {
             final Uri uri = Uri.parse(url);
             if ("xmpp".equals(uri.getScheme())) {

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

@@ -5,148 +5,167 @@ import android.content.Intent;
 import android.content.SharedPreferences;
 import android.net.Uri;
 import android.preference.PreferenceManager;
-
-import org.osmdroid.util.GeoPoint;
-
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
-import java.util.ArrayList;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.entities.Contact;
 import eu.siacs.conversations.entities.Conversational;
 import eu.siacs.conversations.entities.Message;
 import eu.siacs.conversations.ui.ShareLocationActivity;
 import eu.siacs.conversations.ui.ShowLocationActivity;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.regex.Matcher;
+import org.osmdroid.util.GeoPoint;
 
 public class GeoHelper {
 
-	private static final String SHARE_LOCATION_PACKAGE_NAME = "eu.siacs.conversations.location.request";
-	private static final String SHOW_LOCATION_PACKAGE_NAME = "eu.siacs.conversations.location.show";
-
-	public static Pattern GEO_URI = Pattern.compile("geo:(-?\\d+(?:\\.\\d+)?),(-?\\d+(?:\\.\\d+)?)(?:,-?\\d+(?:\\.\\d+)?)?(?:;crs=[\\w-]+)?(?:;u=\\d+(?:\\.\\d+)?)?(?:;[\\w-]+=(?:[\\w-_.!~*'()]|%[\\da-f][\\da-f])+)*(\\?z=\\d+)?", Pattern.CASE_INSENSITIVE);
-
-	public static boolean isLocationPluginInstalled(Context context) {
-		return new Intent(SHARE_LOCATION_PACKAGE_NAME).resolveActivity(context.getPackageManager()) != null;
-	}
-
-	public static boolean isLocationPluginInstalledAndDesired(Context context) {
-		SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
-		final boolean configured = preferences.getBoolean("use_share_location_plugin", context.getResources().getBoolean(R.bool.use_share_location_plugin));
-		return configured && isLocationPluginInstalled(context);
-	}
-
-	public static Intent getFetchIntent(Context context) {
-		if (isLocationPluginInstalledAndDesired(context)) {
-			return new Intent(SHARE_LOCATION_PACKAGE_NAME);
-		} else {
-			return new Intent(context, ShareLocationActivity.class);
-		}
-	}
-
-	public static GeoPoint parseGeoPoint(final Uri uri) {
-		return parseGeoPoint(uri.toString());
-	}
-
-	private static GeoPoint parseGeoPoint(String body) throws IllegalArgumentException {
-		final Matcher matcher = GEO_URI.matcher(body);
-		if (!matcher.matches()) {
-			throw new IllegalArgumentException("Invalid geo uri");
-		}
-		final double latitude;
-		final double longitude;
-		try {
-			latitude = Double.parseDouble(matcher.group(1));
-			if (latitude > 90.0 || latitude < -90.0) {
-				throw new IllegalArgumentException("Invalid geo uri");
-			}
-			longitude = Double.parseDouble(matcher.group(2));
-			if (longitude > 180.0 || longitude < -180.0) {
-				throw new IllegalArgumentException("Invalid geo uri");
-			}
-		} catch (final NumberFormatException e) {
-			throw new IllegalArgumentException("Invalid geo uri",e);
-		}
-		return new GeoPoint(latitude, longitude);
-	}
-
-	public static ArrayList<Intent> createGeoIntentsFromMessage(Context context, Message message) {
-		final ArrayList<Intent> intents = new ArrayList<>();
-		final GeoPoint geoPoint;
-		try {
-			geoPoint = parseGeoPoint(message.getBody());
-		} catch (IllegalArgumentException e) {
-			return intents;
-		}
-		final Conversational conversation = message.getConversation();
-		final String label = getLabel(context, message);
-
-		if (isLocationPluginInstalledAndDesired(context)) {
-			Intent locationPluginIntent = new Intent(SHOW_LOCATION_PACKAGE_NAME);
-			locationPluginIntent.putExtra("latitude", geoPoint.getLatitude());
-			locationPluginIntent.putExtra("longitude", geoPoint.getLongitude());
-			if (message.getStatus() != Message.STATUS_RECEIVED) {
-				locationPluginIntent.putExtra("jid", conversation.getAccount().getJid().toString());
-				locationPluginIntent.putExtra("name", conversation.getAccount().getJid().getLocal());
-			} else {
-				Contact contact = message.getContact();
-				if (contact != null) {
-					locationPluginIntent.putExtra("name", contact.getDisplayName());
-					locationPluginIntent.putExtra("jid", contact.getJid().toString());
-				} else {
-					locationPluginIntent.putExtra("name", UIHelper.getDisplayedMucCounterpart(message.getCounterpart()));
-				}
-			}
-			intents.add(locationPluginIntent);
-		} else {
-			Intent intent = new Intent(context, ShowLocationActivity.class);
-			intent.setAction(SHOW_LOCATION_PACKAGE_NAME);
-			intent.putExtra("latitude", geoPoint.getLatitude());
-			intent.putExtra("longitude", geoPoint.getLongitude());
-			intents.add(intent);
-		}
-
-		intents.add(geoIntent(geoPoint, label));
-
-		Intent httpIntent = new Intent(Intent.ACTION_VIEW);
-		httpIntent.setData(Uri.parse("https://maps.google.com/maps?q=loc:"+ geoPoint.getLatitude() + "," + geoPoint.getLongitude() +label));
-		intents.add(httpIntent);
-		return intents;
-	}
-
-	public static void view(Context context, Message message) {
-		final GeoPoint geoPoint = parseGeoPoint(message.getBody());
-		final String label = getLabel(context, message);
-		context.startActivity(geoIntent(geoPoint,label));
-	}
-
-	private static Intent geoIntent(GeoPoint geoPoint, String label) {
-		Intent geoIntent = new Intent(Intent.ACTION_VIEW);
-		geoIntent.setData(Uri.parse("geo:" + geoPoint.getLatitude() + "," + geoPoint.getLongitude() + "?q=" + geoPoint.getLatitude() + "," + geoPoint.getLongitude() + "("+ label+")"));
-		return geoIntent;
-	}
-
-	public static boolean openInOsmAnd(Context context, Message message) {
-		try {
-			final GeoPoint geoPoint = parseGeoPoint(message.getBody());
-			final String label = getLabel(context, message);
-			return geoIntent(geoPoint, label).resolveActivity(context.getPackageManager()) != null;
-		} catch (IllegalArgumentException e) {
-			return false;
-		}
-	}
-
-	private static String getLabel(Context context, Message message) {
-		if(message.getStatus() == Message.STATUS_RECEIVED) {
-			try {
-				return URLEncoder.encode(UIHelper.getMessageDisplayName(message),"UTF-8");
-			} catch (UnsupportedEncodingException e) {
-				throw new AssertionError(e);
-			}
-		} else {
-			return context.getString(R.string.me);
-		}
-	}
+    private static final String SHARE_LOCATION_PACKAGE_NAME =
+            "eu.siacs.conversations.location.request";
+    private static final String SHOW_LOCATION_PACKAGE_NAME = "eu.siacs.conversations.location.show";
+
+    public static boolean isLocationPluginInstalled(Context context) {
+        return new Intent(SHARE_LOCATION_PACKAGE_NAME).resolveActivity(context.getPackageManager())
+                != null;
+    }
+
+    public static boolean isLocationPluginInstalledAndDesired(Context context) {
+        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
+        final boolean configured =
+                preferences.getBoolean(
+                        "use_share_location_plugin",
+                        context.getResources().getBoolean(R.bool.use_share_location_plugin));
+        return configured && isLocationPluginInstalled(context);
+    }
+
+    public static Intent getFetchIntent(Context context) {
+        if (isLocationPluginInstalledAndDesired(context)) {
+            return new Intent(SHARE_LOCATION_PACKAGE_NAME);
+        } else {
+            return new Intent(context, ShareLocationActivity.class);
+        }
+    }
+
+    public static GeoPoint parseGeoPoint(final Uri uri) {
+        return parseGeoPoint(uri.toString());
+    }
+
+    private static GeoPoint parseGeoPoint(String body) throws IllegalArgumentException {
+        final Matcher matcher = Patterns.URI_GEO.matcher(body);
+        if (!matcher.matches()) {
+            throw new IllegalArgumentException("Invalid geo uri");
+        }
+        final double latitude;
+        final double longitude;
+        try {
+            latitude = Double.parseDouble(matcher.group(1));
+            if (latitude > 90.0 || latitude < -90.0) {
+                throw new IllegalArgumentException("Invalid geo uri");
+            }
+            longitude = Double.parseDouble(matcher.group(2));
+            if (longitude > 180.0 || longitude < -180.0) {
+                throw new IllegalArgumentException("Invalid geo uri");
+            }
+        } catch (final NumberFormatException e) {
+            throw new IllegalArgumentException("Invalid geo uri", e);
+        }
+        return new GeoPoint(latitude, longitude);
+    }
+
+    public static ArrayList<Intent> createGeoIntentsFromMessage(Context context, Message message) {
+        final ArrayList<Intent> intents = new ArrayList<>();
+        final GeoPoint geoPoint;
+        try {
+            geoPoint = parseGeoPoint(message.getBody());
+        } catch (IllegalArgumentException e) {
+            return intents;
+        }
+        final Conversational conversation = message.getConversation();
+        final String label = getLabel(context, message);
+
+        if (isLocationPluginInstalledAndDesired(context)) {
+            Intent locationPluginIntent = new Intent(SHOW_LOCATION_PACKAGE_NAME);
+            locationPluginIntent.putExtra("latitude", geoPoint.getLatitude());
+            locationPluginIntent.putExtra("longitude", geoPoint.getLongitude());
+            if (message.getStatus() != Message.STATUS_RECEIVED) {
+                locationPluginIntent.putExtra("jid", conversation.getAccount().getJid().toString());
+                locationPluginIntent.putExtra(
+                        "name", conversation.getAccount().getJid().getLocal());
+            } else {
+                Contact contact = message.getContact();
+                if (contact != null) {
+                    locationPluginIntent.putExtra("name", contact.getDisplayName());
+                    locationPluginIntent.putExtra("jid", contact.getJid().toString());
+                } else {
+                    locationPluginIntent.putExtra(
+                            "name", UIHelper.getDisplayedMucCounterpart(message.getCounterpart()));
+                }
+            }
+            intents.add(locationPluginIntent);
+        } else {
+            Intent intent = new Intent(context, ShowLocationActivity.class);
+            intent.setAction(SHOW_LOCATION_PACKAGE_NAME);
+            intent.putExtra("latitude", geoPoint.getLatitude());
+            intent.putExtra("longitude", geoPoint.getLongitude());
+            intents.add(intent);
+        }
+
+        intents.add(geoIntent(geoPoint, label));
+
+        Intent httpIntent = new Intent(Intent.ACTION_VIEW);
+        httpIntent.setData(
+                Uri.parse(
+                        "https://maps.google.com/maps?q=loc:"
+                                + geoPoint.getLatitude()
+                                + ","
+                                + geoPoint.getLongitude()
+                                + label));
+        intents.add(httpIntent);
+        return intents;
+    }
+
+    public static void view(Context context, Message message) {
+        final GeoPoint geoPoint = parseGeoPoint(message.getBody());
+        final String label = getLabel(context, message);
+        context.startActivity(geoIntent(geoPoint, label));
+    }
+
+    private static Intent geoIntent(GeoPoint geoPoint, String label) {
+        Intent geoIntent = new Intent(Intent.ACTION_VIEW);
+        geoIntent.setData(
+                Uri.parse(
+                        "geo:"
+                                + geoPoint.getLatitude()
+                                + ","
+                                + geoPoint.getLongitude()
+                                + "?q="
+                                + geoPoint.getLatitude()
+                                + ","
+                                + geoPoint.getLongitude()
+                                + "("
+                                + label
+                                + ")"));
+        return geoIntent;
+    }
+
+    public static boolean openInOsmAnd(Context context, Message message) {
+        try {
+            final GeoPoint geoPoint = parseGeoPoint(message.getBody());
+            final String label = getLabel(context, message);
+            return geoIntent(geoPoint, label).resolveActivity(context.getPackageManager()) != null;
+        } catch (IllegalArgumentException e) {
+            return false;
+        }
+    }
+
+    private static String getLabel(Context context, Message message) {
+        if (message.getStatus() == Message.STATUS_RECEIVED) {
+            try {
+                return URLEncoder.encode(UIHelper.getMessageDisplayName(message), "UTF-8");
+            } catch (UnsupportedEncodingException e) {
+                throw new AssertionError(e);
+            }
+        } else {
+            return context.getString(R.string.me);
+        }
+    }
 }

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

@@ -1,513 +1,24 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- *
- * Download latest version here:
- * https://android.googlesource.com/platform/frameworks/base.git/+/master/core/java/android/util/Patterns.java
- *
- *
- */
 package eu.siacs.conversations.utils;
-import java.util.regex.Matcher;
+
 import java.util.regex.Pattern;
-/**
- * Commonly used regular expression patterns.
- */
+
 public class Patterns {
 
-    public static final Pattern XMPP_PATTERN = Pattern
-            .compile("xmpp\\:(?:(?:["
-                    + Patterns.GOOD_IRI_CHAR
-                    + "\\;\\/\\?\\@\\&\\=\\#\\~\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])"
-                    + "|(?:\\%[a-fA-F0-9]{2}))+");
+    public static final Pattern URI_GENERIC =
+            Pattern.compile(
+                    "(?<=^|\\s)(tel|xmpp|http|https|geo|mailto):[a-zA-Z0-9\\-._~:/?#\\[\\]@!$&'()*+,;=%]+");
 
-    /**
-     *  Regular expression to match all IANA top-level domains.
-     *  List accurate as of 2011/07/18.  List taken from:
-     *  http://data.iana.org/TLD/tlds-alpha-by-domain.txt
-     *  This pattern is auto-generated by frameworks/ex/common/tools/make-iana-tld-pattern.py
-     *
-     *  @deprecated Due to the recent profileration of gTLDs, this API is
-     *  expected to become out-of-date very quickly. Therefore it is now
-     *  deprecated.
-     */
-    @Deprecated
-    public static final String TOP_LEVEL_DOMAIN_STR =
-            "((aero|arpa|asia|a[cdefgilmnoqrstuwxz])"
-                    + "|(biz|b[abdefghijmnorstvwyz])"
-                    + "|(cat|com|coop|c[acdfghiklmnoruvxyz])"
-                    + "|d[ejkmoz]"
-                    + "|(edu|e[cegrstu])"
-                    + "|f[ijkmor]"
-                    + "|(gov|g[abdefghilmnpqrstuwy])"
-                    + "|h[kmnrtu]"
-                    + "|(info|int|i[delmnoqrst])"
-                    + "|(jobs|j[emop])"
-                    + "|k[eghimnprwyz]"
-                    + "|l[abcikrstuvy]"
-                    + "|(mil|mobi|museum|m[acdeghklmnopqrstuvwxyz])"
-                    + "|(name|net|n[acefgilopruz])"
-                    + "|(org|om)"
-                    + "|(pro|p[aefghklmnrstwy])"
-                    + "|qa"
-                    + "|r[eosuw]"
-                    + "|s[abcdeghijklmnortuvyz]"
-                    + "|(tel|travel|t[cdfghjklmnoprtvwz])"
-                    + "|u[agksyz]"
-                    + "|v[aceginu]"
-                    + "|w[fs]"

src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java 🔗

@@ -1034,7 +1034,7 @@ public class XmppConnection implements Runnable {
             if (Strings.isNullOrEmpty(text)) {
                 throw new StateChangingException(Account.State.UNAUTHORIZED);
             }
-            final Matcher matcher = Patterns.AUTOLINK_WEB_URL.matcher(text);
+            final Matcher matcher = Patterns.URI_HTTP.matcher(text);
             if (matcher.find()) {
                 final HttpUrl url;
                 try {
@@ -1925,7 +1925,7 @@ public class XmppConnection implements Runnable {
                         if (url != null) {
                             setAccountCreationFailed(url);
                         } else if (instructions != null) {
-                            final Matcher matcher = Patterns.AUTOLINK_WEB_URL.matcher(instructions);
+                            final Matcher matcher = Patterns.URI_HTTP.matcher(instructions);
                             if (matcher.find()) {
                                 setAccountCreationFailed(
                                         instructions.substring(matcher.start(), matcher.end()));