android:autoLink for phone numbers doesn't always work
Solution 1
Here is my investigation.
I created a new project, and added android:autoLink="all"
to a text view in activity_main.xml
. Thanks to the developers of Android Studio, I could see the preview, and I found something interesting:
-
12345
not linked -
123456
not linked -
1234567
linked -
12345678
linked -
123456789
not linked -
1234567890
not likned -
12345678901
linked -
123456789012
not linked
The result is the same on my phone. So I looked into the source code, searched for the keyword autolink, then I found this:
private void setText(CharSequence text, BufferType type,
boolean notifyBefore, int oldlen) {
...
// unconcerned code above
if (mAutoLinkMask != 0) {
Spannable s2;
if (type == BufferType.EDITABLE || text instanceof Spannable) {
s2 = (Spannable) text;
} else {
s2 = mSpannableFactory.newSpannable(text);
}
if (Linkify.addLinks(s2, mAutoLinkMask)) {
text = s2;
type = (type == BufferType.EDITABLE) ? BufferType.EDITABLE : BufferType.SPANNABLE;
/*
* We must go ahead and set the text before changing the
* movement method, because setMovementMethod() may call
* setText() again to try to upgrade the buffer type.
*/
mText = text;
// Do not change the movement method for text that support text selection as it
// would prevent an arbitrary cursor displacement.
if (mLinksClickable && !textCanBeSelected()) {
setMovementMethod(LinkMovementMethod.getInstance());
}
}
}
...
// unconcerned code above
}
So the keyword is Linkify
now. For addLinks
:
public static final boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask) {
...
if ((mask & PHONE_NUMBERS) != 0) {
gatherTelLinks(links, text);
}
...
}
private static final void gatherTelLinks(ArrayList<LinkSpec> links, Spannable s) {
PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
Iterable<PhoneNumberMatch> matches = phoneUtil.findNumbers(s.toString(),
Locale.getDefault().getCountry(), Leniency.POSSIBLE, Long.MAX_VALUE);
for (PhoneNumberMatch match : matches) {
LinkSpec spec = new LinkSpec();
spec.url = "tel:" + PhoneNumberUtils.normalizeNumber(match.rawString());
spec.start = match.start();
spec.end = match.end();
links.add(spec);
}
}
Then, something bad happened, the SDK doesn't have PhoneNumberUtil
, specifically these 3 classes below:
import com.android.i18n.phonenumbers.PhoneNumberMatch;
import com.android.i18n.phonenumbers.PhoneNumberUtil;
import com.android.i18n.phonenumbers.PhoneNumberUtil.Leniency;
For now, the first reason surfaced: Locale.getDefault().getCountry()
.
So I went to setting, found language, selected Chinese. The result is below:
-
12345
linked -
123456
linked -
1234567
linked -
12345678
linked -
123456789
linked -
1234567890
linked -
12345678901
linked -
123456789012
linked
Secondly, for the package of com.android.i18n.phonenumbers
, I found this:
https://android.googlesource.com/platform/external/libphonenumber/+/ics-factoryrom-2-release/java/src/com/android/i18n/phonenumbers
If you are interested, check the link above. Notice in the URL: ics-factoryrom-2-release
. So I highly doubt that this is platform-dependent.
For the solution, CleverAndroid is right, taking full control of LinkMovementMethod
is a good option.
Solution 2
Just do the following
TextView userInput= (TextView) view.findViewById(R.id.textView);
if(userInput != null){
Linkify.addLinks(userInput, Patterns.PHONE,"tel:",Linkify.sPhoneNumberMatchFilter,Linkify.sPhoneNumberTransformFilter);
userInput.setMovementMethod(LinkMovementMethod.getInstance());
}
and also remove the
android:autoLink
from your xml file
Solution 3
I made a universal pattern for phone numbers and added a Linkify mask. Kotlin, extension function:
fun TextView.makeLinkedable(){
val pattern = Pattern.compile("""([\d|\(][\h|\(\d{3}\)|\.|\-|\d]{4,}\d)""",
Pattern.CASE_INSENSITIVE)
LinkifyCompat.addLinks(this, Linkify.ALL)
LinkifyCompat.addLinks(this, pattern, "tel://", null, null, null)
setLinkTextColor(ContextCompat.getColor(context, R.color.blue))
}
Should work for all devices
The point is in this:
val pattern = Pattern.compile("""([\d|\(][\h|\(\d{3}\)|\.|\-|\d]{4,}\d)""",
Pattern.CASE_INSENSITIVE)
LinkifyCompat.addLinks(this, pattern, "tel://", null, null, null)
And in Java:
Pattern pattern = Pattern.compile("([\\d|(][\\h|(\\d{3})|.|\\-|\\d]{4,}\\d{4,})", Pattern.CASE_INSENSITIVE);
LinkifyCompat.addLinks(textView, pattern, "tel://", null, null, null);
Solution 4
I would suggest you just to add country code and all your issue will be resolved,
android:autoLink="phone"
android:text="+91-8000000000"
If we add country code before the number, then no need of other temporary solutions.
Solution 5
Harsh Agrawal answer worked for me in cases where the phone number is 11 digits or more with 3 blocks. e.g. 123 456 78910
TextView textView = findViewById(R.id.text_view);
textView.setText("123 456 78910");
Linkify.addLinks(textView, Patterns.PHONE, "tel:", Linkify.sPhoneNumberMatchFilter,
Linkify.sPhoneNumberTransformFilter);
I had to call Linkify.addLinks
after setting text for it to work.
Note that Linkify.addLinks
already calls setMovementMethod
on the text view.
Related videos on Youtube
Martynas Jurkus
Updated on June 04, 2022Comments
-
Martynas Jurkus about 2 years
I have a simple
TextView
with local phone number 852112222 or (8 5) 211 2222.I need it to be clickable, so naturally I used
android:autoLink="all"
.
But for some reason I don't understand same phone number is not "linkified" on all devices.On plain Genymotion device it didn't work. On my personal OnePlus2 device it worked. Tested on bunch on different devices - no luck.
What could be the issue?
User account preferences? Android version? ORM? Something else?-
Muahmmad Tayyib about 5 yearsmy observation is "+" prefix is required before a number to open dailer via xml directly just like "tel:" prefix is required for setting call intent programmatically.
-
-
Martynas Jurkus over 7 yearsWhy? android:autoLink="phone" should work too. And it works on some devices. And that wasn't the question.
-
Martynas Jurkus over 7 yearsStill same result. On some devices same number is linkified on other it does not. And it doesn't matter if I use
Linkify.PHONE_NUMBERS
orLinkify.ALL
-
Mable John over 7 yearsEverything seems working fine here. Let me know your feedback.
-
Martynas Jurkus over 7 yearsTook your sample code - no luck. imgur.com/cSLuOdt Used same devices, even other SDK versions. Same result.
-
Mable John over 7 years@MartynasJurkus Your image shown that second one was partially linkified
-
Mable John over 7 yearsokay! will check one more time and let me know if you fix this.
-
Martynas Jurkus over 6 yearsThat would work. Although phone numbers is user input and it is not common practice to enter country codes when entering same country phone number so I can't force validation on them.
-
miPlodder over 6 yearsAdding Country Code will help developer to resolve, future issues when his/her Android App is used globally. In that case, user will not be able to make a call due to no country code (Invalid number). So, why not add it beforehand to resolve future issues, and this approach will even help developer from adding other temporary solutions to make
android:autoLink
work. -
aaronmarino over 4 yearsThis crashes on Lollipop for me, I think due to
\h
being a Java 8 addition. This works for me instead"""([\d|\(][\t\p{Zs}|\(\d{3}\)|\.|\-|\d]{4,}\d)"""
-
P Kuijpers almost 3 years+1 this works perfectly for me on (almost) all devices. The only one it doesn't seem to work on was an 3.2 QVGA emulator (Android 7.1.1). If anyone has a clue why, I'd be happy to hear about it. Still, I'm happy with the big improvement over autolink, thanks! ... ps. it did work as expected on a Nexus One - Android 6.0 - emulator. So it's not an v7.1.1 and lower issue, although it might be due to a single specific Android version. I haven't continued to test others, because for me the result works sufficiently nonetheless.
-
Mina Farid over 2 yearsThank you for posting this awesome answer, you saved my day :)
-
mithunc over 2 years@miPlodder The problem is that this is a user facing string, and we may not want to display phone numbers to the user like that. For US numbers, you would probably not want to show numbers as +12345678900, as that would be hard for the user to read and doesn't look nice. You would probably want it to be (234) 567-8900.
-
mithunc over 2 yearsThe second
Linkify.addLinks
can useLinkifyCompat
as well. Also, I didn't need to setmovementMethod
, I thinkaddLinks
does that on its own.