android:autoLink for phone numbers doesn't always work

16,019

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.

Share:
16,019

Related videos on Youtube

Martynas Jurkus
Author by

Martynas Jurkus

Updated on June 04, 2022

Comments

  • Martynas Jurkus
    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
      Muahmmad Tayyib about 5 years
      my 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
    Martynas Jurkus over 7 years
    Why? android:autoLink="phone" should work too. And it works on some devices. And that wasn't the question.
  • Martynas Jurkus
    Martynas Jurkus over 7 years
    Still 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 or Linkify.ALL
  • Mable John
    Mable John over 7 years
    Everything seems working fine here. Let me know your feedback.
  • Martynas Jurkus
    Martynas Jurkus over 7 years
    Took your sample code - no luck. imgur.com/cSLuOdt Used same devices, even other SDK versions. Same result.
  • Mable John
    Mable John over 7 years
    @MartynasJurkus Your image shown that second one was partially linkified
  • Mable John
    Mable John over 7 years
    okay! will check one more time and let me know if you fix this.
  • Martynas Jurkus
    Martynas Jurkus over 6 years
    That 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
    miPlodder over 6 years
    Adding 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
    aaronmarino over 4 years
    This 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
    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
    Mina Farid over 2 years
    Thank you for posting this awesome answer, you saved my day :)
  • mithunc
    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
    mithunc over 2 years
    The second Linkify.addLinks can use LinkifyCompat as well. Also, I didn't need to set movementMethod, I think addLinks does that on its own.