diff --git a/src/com/android/messaging/ui/conversation/ConversationMessageView.java b/src/com/android/messaging/ui/conversation/ConversationMessageView.java index 3b630ee..29c0d99 100644 --- a/src/com/android/messaging/ui/conversation/ConversationMessageView.java +++ b/src/com/android/messaging/ui/conversation/ConversationMessageView.java @@ -28,7 +28,6 @@ import android.text.TextUtils; import android.text.format.DateUtils; import android.text.format.Formatter; import android.text.style.URLSpan; -import android.text.util.Linkify; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.Gravity; @@ -67,6 +66,7 @@ import com.android.messaging.util.Assert; import com.android.messaging.util.AvatarUriUtil; import com.android.messaging.util.ContentType; import com.android.messaging.util.ImageUtils; +import com.android.messaging.util.LinkifyHelper; import com.android.messaging.util.OsUtil; import com.android.messaging.util.PhoneUtils; import com.android.messaging.util.UiUtils; @@ -642,7 +642,7 @@ public class ConversationMessageView extends FrameLayout implements View.OnClick mMessageTextView.setText(text); // Linkify phone numbers, web urls, emails, and map addresses to allow users to // click on them and take the default intent. - mMessageTextHasLinks = Linkify.addLinks(mMessageTextView, Linkify.ALL); + mMessageTextHasLinks = LinkifyHelper.addLinks(mMessageTextView); mMessageTextView.setVisibility(View.VISIBLE); } else { mMessageTextView.setVisibility(View.GONE); diff --git a/src/com/android/messaging/util/LinkifyHelper.java b/src/com/android/messaging/util/LinkifyHelper.java new file mode 100644 index 0000000..c626d53 --- /dev/null +++ b/src/com/android/messaging/util/LinkifyHelper.java @@ -0,0 +1,82 @@ +/* + * SPDX-FileCopyrightText: 2025 The LineageOS Project + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.android.messaging.util; + +import android.text.SpannableString; +import android.text.style.URLSpan; +import android.text.util.Linkify; +import android.util.Log; +import android.widget.TextView; + +import androidx.core.util.Pair; + +import java.util.ArrayList; +import java.util.regex.Pattern; + +/* + * Helper class to support Linkify-ing the geo uri scheme, see + * https://en.wikipedia.org/wiki/Geo_URI_scheme + */ +public class LinkifyHelper { + public final static String TAG = "LinkifyHelper"; + public final static boolean VERBOSE = false; + + private final static Pattern GEO_URL_PATTERN = + Pattern.compile("geo:([\\-0-9.]+),([\\-0-9.]+)(?:,([\\-0-9.]+))?(?:\\?(.*))?", + Pattern.CASE_INSENSITIVE); + + + // This could have been simpler, but Linkify.addLinks() removes existing links + // and PHONE_NUMBERS also matches parts of the geo-url + public static boolean addLinks(TextView text) { + boolean ret; + + // We need to add and know the geo spans first since Linkify will replace them with + // phone numbers - therefore we remove those later and replace them with the geo ones + // again + Linkify.addLinks(text, GEO_URL_PATTERN, null); + SpannableString geoSpannable = SpannableString.valueOf(text.getText()); + final URLSpan[] geoSpans = geoSpannable.getSpans(0, geoSpannable.length(), URLSpan.class); + + ArrayList> geoSpanPairs = new ArrayList<>(); + for (URLSpan geoSpan : geoSpans) { + geoSpanPairs.add(new Pair<>(geoSpannable.getSpanStart(geoSpan), + geoSpannable.getSpanEnd(geoSpan))); + } + + // We want "ALL" but that's deprecated due to Linkify.MAP_ADDRESSES + int mask = Linkify.WEB_URLS | Linkify.EMAIL_ADDRESSES | Linkify.PHONE_NUMBERS; + + // This will remove our existing spans + ret = Linkify.addLinks(text, mask); + SpannableString s = SpannableString.valueOf(text.getText()); + + if (geoSpans.length > 0) { + ret = true; + final URLSpan[] allSpans = s.getSpans(0, s.length(), URLSpan.class); + for (int i = allSpans.length - 1; i >= 0; i--) { + int spanStart = s.getSpanStart(allSpans[i]); + int spanEnd = s.getSpanEnd(allSpans[i]); + for (Pair pair : geoSpanPairs) { + if (spanStart >= pair.first && spanStart <= pair.second) { + // We have found a span within a geo span + if (VERBOSE) { + Log.d(TAG, "Removing span between " + spanStart + " and " + spanEnd + + " since it's in range of a geo span (" + pair.first + ", " + + pair.second + ")"); + } + s.removeSpan(allSpans[i]); + } + } + } + text.setText(s); + // Add the geo spans again + Linkify.addLinks(text, GEO_URL_PATTERN, null); + } + + return ret; + } +}