saket / better-link-movement-method Goto Github PK
View Code? Open in Web Editor NEWAttempts to improve how clickable links are detected, highlighted and handled in TextView
License: Apache License 2.0
Attempts to improve how clickable links are detected, highlighted and handled in TextView
License: Apache License 2.0
I feel like this library is missing an important feature - long press handling on the links.
Hi there,
First I want to thank you for this library I have been using it for a long time.
Lately I have decided to support markdown in my app and I tried to use this one
Here's my problem
When I add a link like this
[inline-style link](https://www.google.com)
LinkClick listener gets url as inline-style link not https://www.google.com
I couldn't really tell which library caused this but I wanted to ask you anyway
edit: I solved by tapping into the other library's interface.
Do you plan to use LinkMovementMethodCompat?
As far as I can tell there's no reason to only support URLSpan
when ClickableSpan
s are commonly used for non-URL linking inside text.
When i use Japanese keyboard and click to TextView with BetterLinkMovementMethod. Keyboard auto switch to QWERTY
Small nit, just thought I would make you aware 😸
Sometimes links highlight properly, but sometimes they have an extra highlight behind them.
Here's a sample project that demonstrates this issue.
(Also, is this project actively maintained? I noticed some commits from 2021, but the last release was in 2018.)
In sample app make the textView text-selectable (e.g. by adding android:textIsSelectable="true"
)
java.lang.IndexOutOfBoundsException: setSpan (-1 ... -1) starts before 0
...
at me.saket.bettermovementmethod.BetterLinkMovementMethod.highlightUrl(BetterLinkMovementMethod.java:342)
at me.saket.bettermovementmethod.BetterLinkMovementMethod.onTouchEvent(BetterLinkMovementMethod.java:222)
OS: Naugate and Oreo
I am working on a project where I need to use TextView in Listview with verious String types like URL, Phone Number, Email etc. I have implement your lib and it works in activities but in the listview, I am facing some problem.
Assume, I have a dynamic listview with String content. At first, the popup menu doesn't show from the linkify. If I scroll up/down, it works again. Need to add that in Lolipop, it works like a charm.
Is there any solution regarding this.
Here is the sample of my XML and Java Code -
<TextView
android:id="@+id/body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:autoLink="all"
android:focusable="true"
android:linksClickable="false"
android:longClickable="true"
android:textColorHighlight="@color/gray"
android:textColorLink="@color/white"
android:textSize="@dimen/font_size_small"
tools:text="@tools:sample/full_names" />
--
in list adaper -
public View getView(int position, View view, ViewGroup parent) {
...
BetterLinkMovementMethod.linkify(Linkify.ALL, mViewGroup).setOnLinkClickListener(urlClickListener);
}
private BetterLinkMovementMethod.OnLinkClickListener urlClickListener =
new BetterLinkMovementMethod.OnLinkClickListener() {
@OverRide
public boolean onClick(TextView textView, String url) {
if (isPhoneNumber(url)) {
PhoneLinkPopupMenu phonePopupMenu =
new PhoneLinkPopupMenu(activity, textView, url.replace("tel:", ""));
phonePopupMenu.show();
}
return true;
}
};
When i use this lib in adapter, onClick/onLongClick events are working for only links. If i type simple text and tring to click or long click no action, even parent view does not handle these actions.
viewHolder.itemView.messageTv?.let {
it.movementMethod = BetterLinkMovementMethod.getInstance()
Linkify.addLinks(it, Linkify.WEB_URLS)
BetterLinkMovementMethod
.linkify(Linkify.WEB_URLS, it)
.setOnLinkClickListener { _: TextView?, url: String? ->
val uri = Uri.parse(url)
val intent = Intent(Intent.ACTION_VIEW, uri)
intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.packageName)
if (intent.resolveActivity(context.packageManager) != null) {
context.startActivity(intent)
}
true
}
.setOnLinkLongClickListener { _: TextView?, _: String? ->
context.onItemLongClick(viewHolder.adapterPosition)
true
}
}
In the current implementation, it's possible that a TextView already has background highlighting apart from URLs, where BetterLinkMovementMethod
might end up removing it. Using a custom span class for background color should solve this problem.
Can BetterLinkMovementMethod return a SpannableString?
I need to get the source span and then add additional manipulations. Thanks for this great library!
Swapped usage of Linkify
with BetterLinkMovementMethod
and at the very next app launch, boom. The app builds perfectly however, it crashes on the Splash screen with the following trace.
W/Linkify.v16: reflect applyLink failed:
miui.reflect.NoSuchMethodException: Couldn't find method android/text/util/Linkify.applyLink(Ljava/lang/String;IILandroid/text/Spannable;)V
at com.miui.internal.os.Native.getMethod(Native Method)
at miui.reflect.Method.of(Method.java:35)
at com.miui.internal.variable.v16.Android_Text_Util_Linkify_class.<clinit>(Android_Text_Util_Linkify_class.java:70)
at java.lang.Class.classForName(Native Method)
at java.lang.Class.forName(Class.java:454)
at java.lang.Class.forName(Class.java:379)
at com.miui.internal.variable.AbsClassFactory.create(AbsClassFactory.java:24)
at com.miui.internal.variable.Android_Text_Util_Linkify_class$Factory.<init>(Android_Text_Util_Linkify_class.java:40)
at com.miui.internal.variable.Android_Text_Util_Linkify_class$Factory.<init>(Android_Text_Util_Linkify_class.java:26)
at com.miui.internal.variable.Android_Text_Util_Linkify_class$Factory$1.createInstance(Android_Text_Util_Linkify_class.java:32)
at com.miui.internal.variable.Android_Text_Util_Linkify_class$Factory$1.createInstance(Android_Text_Util_Linkify_class.java:29)
at miui.util.SoftReferenceSingleton.get(SoftReferenceSingleton.java:25)
at com.miui.internal.variable.Android_Text_Util_Linkify_class$Factory.getInstance(Android_Text_Util_Linkify_class.java:44)
at com.miui.internal.initialization.OverrideHelper.doInitialize(OverrideHelper.java:37)
at miui.core.SdkManager.initialize(SdkManager.java:99)
at java.lang.reflect.Method.invoke(Native Method)
at miui.external.Application.initializeSdk(Application.java:85)
at miui.external.Application.<init>(Application.java:55)
at com.xiaomi.activate.Application.<init>(Application.java:9)
at java.lang.Class.newInstance(Native Method)
at android.app.AppComponentFactory.instantiateApplication(AppComponentFactory.java:76)
at android.app.Instrumentation.newApplication(Instrumentation.java:1155)
at android.app.LoadedApk.makeApplication(LoadedApk.java:1222)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6572)
at android.app.ActivityThread.access$1500(ActivityThread.java:233)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1914)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:224)
at android.app.ActivityThread.main(ActivityThread.java:7561)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:995)
Code:
BetterLinkMovementMethod
.linkify(Linkify.ALL,textView)
.setOnLinkClickListener { textView, url ->
onClick(url)
true
}
In order to successfully use the app, I have to uninstall the previous build from my phone and then again reinstall the new build. Doing the new reinstall after the old uninstall, magically fixes this issue and the app runs fine.
This issue happens every time I build my app and try to install a new build. Only after uninstalling the new build, I can proceed further. After removing the BetterLinkMovementMethod
dependency from gradle and rebuild, the app again behaved normally, something surely is going wrong with this lib.
This happens only when I try to install a new build on my device without uninstalling the previous build. In all the other scenarios, this works fine.
Environment Details.
OS: Android 10 QP1A.190711.020
MIUI: 12.0.8 Global Stable
Device: Redmi Note 9 (Xiaomi M2003J15SC)
Ellipsize doesn't add ... at end of text with maxLines=4 when setting text with BetterLinkMovementMethod:
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="4"/>
This is a platform issue as well; Ellipsize doesn't work with spannableStrings.
A workaround that seems to work is to use
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
flag for .setSpan()
,
instead of Spannable.SPAN_INCLUSIVE_INCLUSIVE
src: https://stackoverflow.com/a/21652979/2639476
This does seem to work for links at the beginning of text too, so not sure what side-effects the flag change may introduce.
I have texts with <a href="url">abc</a>
and urls that are not inside tag. I would like to turn both to spannable links. So far it's not working. Only linkifyHtml is being applied.
Namely, if the parent view has a context menu registered, it will still trigger sometimes, even if I cancel all touch events in the long press handler. I think it's a timer race between the parent and the movement handler.
The library is working fine only if we have link between tags for example:
Some text http://www.google.com
Some text http://www.google.com
In this way it will highlight the link and clickable also, but in case 2
Some text Go to Google
Some text Go to Google
Link is not generating neither it is clickable, can you provide any solution for this
I have tried using
1.BetterLinkMovementMethod.linkify(Linkify.ALL, textview) and
2.BetterLinkMovementMethod.linkifyHtml(textview)
both
:)
Seems it still works fine...
One of my apps requires a click listener on the TextView
and/or parent views (e.g. in a RecyclerView
) and also has linkified text in it.
After the MovementMethod
is applied, the click listeners do not work anymore. This is an age-old bug, but there are workarounds.
A similar library solves this by having a custom TextView
that intercepts onTouch
events and handles them differently when a touch on a link is in progress or not.
Maybe a similar approach can be introduced to this library? Currently i am using this gross hack:
// BetterLinkTextView.kt
override fun onTouchEvent(event: MotionEvent?): Boolean {
super.onTouchEvent(event)
if (movementMethod is BetterLinkMovementMethod) {
val span = getTag(R.id.bettermovementmethod_highlight_background_span)
return span != null && (text as? Spannable)?.getSpanStart(span) ?: -1 >= 0
}
return false
}
This could be improved by having a first class api if a click is in progress and/or by having a TextView
doing this in the library.
Thanks for looking into this!
If we pass incomplete reference URLs in <a href>
tags and capture the URLs and map them to correct methods or activities, it works. But when accessibility is ON it fails and we get error
W/URLSpan: Actvity was not found for intent, Intent { act=android.intent.action.VIEW dat=/wps/portal/CONTACT_US (has extras) }
How to handle such issues?
As an Android Developer, we would like to implement instrumentation test if we behave correctly or not.
So we need to be able to fire the click. If possible can you add this functionality?
BetterLinkMovementMethod.linkify(Linkify.ALL, textView).setOnLinkClickListener { textView, url ->
println("onLinkClick $url")
false
}
textView.text = "https://www.google.com"
Environment: Emulator API 30
Library: 2.2.0
do not consider \n new line character put all text in one paragraph
Hi Saket, first of all thanks for creatng this awesome library, it makes much easier to manage links in my app.
But, I am facing this crash on long click.
FATAL EXCEPTION: main
Process: me.saket.betterlinkmovement.sample, PID: 4362
java.lang.StringIndexOutOfBoundsException: length=238; index=-1
at java.lang.String.substring(String.java:1968)
at android.text.SpannableStringInternal.<init>(SpannableStringInternal.java:33)
at android.text.SpannableString.<init>(SpannableString.java:34)
at android.text.SpannableString.subSequence(SpannableString.java:54)
at me.saket.bettermovementmethod.BetterLinkMovementMethod$ClickableSpanWithText.ofSpan(BetterLinkMovementMethod.java:436)
at me.saket.bettermovementmethod.BetterLinkMovementMethod.dispatchUrlLongClick(BetterLinkMovementMethod.java:395)
at me.saket.bettermovementmethod.BetterLinkMovementMethod$1.onTimerReached(BetterLinkMovementMethod.java:232)
at me.saket.bettermovementmethod.BetterLinkMovementMethod$LongPressTimer.run(BetterLinkMovementMethod.java:413)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
Steps to reproduce:
Linkify version 2.2.0. Link text is not highlighted, click listener isn't called. Here's my code:
<TextView
android:id="@+id/tvLogin"
style="?bodyText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginTop="24dp"
android:gravity="center_horizontal"
android:text="@string/label_have_account" />
<string name="label_have_account">Have an account? <a href="http://www.google.com">Log in</a></string>
BetterLinkMovementMethod.linkify(Linkify.ALL, tvLogin)
.setOnLinkClickListener { _, _ -> navigator.login(); true }
Hey @saket, Amazing library. Has really helped out a lot. I have a new feature request to make. Currently, only single click and long click listeners are available in Better-Link-Movement-Method. It would be great if a double-tap listener is also made available in addition to these listeners.
There are a lot of use cases, one of the most prominent being double-tap to like a post (like that on Instagram). So anyone who is building a feed that contains posts with text, images, and videos might want to include the double-tap to like feature. A lot of the posts do have links in them, now for the links, one can use Better-Link-Movement-Method to handle the click events, but then the double-tap to like feature goes for a toss. To have an onTouchListener on the entire textView and use gestureDetector to detect double click is an option but then on clicking the link the onTouch of the textView is triggered thus link handling is undone.
The Solution I have used in my case is to extend BetterLinkMovement to my own class and then create an OnDoubleClickListener there to handle the double clicks. If we can include the listener in the library itself, that would help out of a lot of people. I can work on this feature if you want
I want to create functionality where I can linkify all of a textView, but only handle cases where it is a phone number on my own. For URL's, emails, etc. I want it to default to Linkify's default behavior:
BetterLinkMovementMethod.linkify(Linkify.ALL, thistextView
.setOnLinkClickListener((textView, url) -> {
// Is there a way here to check for phone numbers without having to check for regex?
if (url.matches.(phoneNumberRegex)) {
//handle phone number click ourselves
return true
} else {
// we want to default to the default action intent of opening a URL/email and everything else EXCEPT phone number
return false;
}
})
right now we are doing this:
LinkifyCompat.addLinks(textView, Linkify.ALL);
BetterLinkMovementMethod.linkify(Linkify.PHONE_NUMBERS, textView).setOnLinkClickListener(onLinkClickListener);
but you can see that it will only link phone numbers at this point. This is not an issue with the library, but asking more for suggestions or if there's any available API to use? Thanks!!
Hi! I have the following issue. If I have two types of links in one TextView (e.g. <a href="link">label</a>
and https://example.com
one of the links will not be clickable. I see that this is so for the standard Linkify method. Did you saw something like that?
Or issue with your app, your loan or our products and services you can contact us using any of the above methods. \n Click here to see more information about our complaints procedure.
Hi, I got this crash. Any ideas?
Caused by android.content.pm.PackageManager$NameNotFoundException: com.google.android.webview at android.app.ApplicationPackageManager.getPackageInfo(ApplicationPackageManager.java:138) at android.webkit.WebViewFactory.fetchPackageInfo(WebViewFactory.java:107) at android.webkit.WebViewFactory.getProviderClass(WebViewFactory.java:183) at android.webkit.WebViewFactory.getProvider(WebViewFactory.java:158) at android.webkit.WebView.getFactory(WebView.java:2277) at android.webkit.WebView.findAddress(WebView.java:1683) at android.text.util.Linkify.gatherMapLinks(Linkify.java:472) at android.text.util.Linkify.addLinks(Linkify.java:237) at android.text.util.Linkify.addLinks(Linkify.java:267) at me.saket.bettermovementmethod.BetterLinkMovementMethod.addLinks(BetterLinkMovementMethod.java:202) at me.saket.bettermovementmethod.BetterLinkMovementMethod.rAddLinks(BetterLinkMovementMethod.java:194) at me.saket.bettermovementmethod.BetterLinkMovementMethod.rAddLinks(BetterLinkMovementMethod.java:190) at me.saket.bettermovementmethod.BetterLinkMovementMethod.rAddLinks(BetterLinkMovementMethod.java:190) at me.saket.bettermovementmethod.BetterLinkMovementMethod.linkify(BetterLinkMovementMethod.java:104)
Can I use my custom pattern (regex) instead of Linkify.WEB_URLS
when using the BetterLinkMovementMethod
?
Is it also possible make the link bold and rest of the text as it is.
Recently, I noticed the following crash being reported in the Google Play Developer Console for an Android app of mine. Are you aware of an issue here?
android.content.ActivityNotFoundException:
at android.app.Instrumentation.checkStartActivityResult (Instrumentation.java:1622)
at android.app.Instrumentation.execStartActivity (Instrumentation.java:1417)
at android.app.Activity.startActivityForResult (Activity.java:3370)
at androidx.fragment.app.FragmentActivity.startActivityForResult (FragmentActivity.java:675)
at android.app.Activity.startActivityForResult (Activity.java:3331)
at androidx.fragment.app.FragmentActivity.startActivityForResult (FragmentActivity.java:662)
at android.app.Activity.startActivity (Activity.java:3566)
at android.app.Activity.startActivity (Activity.java:3534)
at android.content.ContextWrapper.startActivity (ContextWrapper.java:284)
at android.text.style.URLSpan.onClick (URLSpan.java:62)
at me.saket.bettermovementmethod.BetterLinkMovementMethod.dispatchUrlClick (BetterLinkMovementMethod.java:392)
at me.saket.bettermovementmethod.BetterLinkMovementMethod.onTouchEvent (BetterLinkMovementMethod.java:248)
at android.widget.TextView.onTouchEvent (TextView.java:7536)
at android.view.View.dispatchTouchEvent (View.java:7246)
at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2174)
at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:1917)
at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2174)
at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:1917)
at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2174)
at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:1917)
at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2174)
at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:1917)
at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2174)
at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:1917)
at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2174)
at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:1917)
at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2174)
at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:1917)
at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2174)
at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:1917)
at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:2174)
at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:1917)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent (PhoneWindow.java:1953)
at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent (PhoneWindow.java:1405)
at android.app.Activity.dispatchTouchEvent (Activity.java:2410)
at androidx.appcompat.view.WindowCallbackWrapper.dispatchTouchEvent (WindowCallbackWrapper.java:69)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent (PhoneWindow.java:1901)
at android.view.View.dispatchPointerEvent (View.java:7426)
at android.view.ViewRootImpl.deliverPointerEvent (ViewRootImpl.java:3220)
at android.view.ViewRootImpl.deliverInputEvent (ViewRootImpl.java:3165)
at android.view.ViewRootImpl.doProcessInputEvents (ViewRootImpl.java:4292)
at android.view.ViewRootImpl.enqueueInputEvent (ViewRootImpl.java:4271)
at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent (ViewRootImpl.java:4363)
at android.view.InputEventReceiver.dispatchInputEvent (InputEventReceiver.java:179)
at android.os.MessageQueue.nativePollOnce (MessageQueue.java)
at android.os.MessageQueue.next (MessageQueue.java:125)
at android.os.Looper.loop (Looper.java:124)
at android.app.ActivityThread.main (ActivityThread.java:5041)
at java.lang.reflect.Method.invokeNative (Method.java)
at java.lang.reflect.Method.invoke (Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:793)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:560)
at dalvik.system.NativeStart.main (NativeStart.java)
After adding edit text below textview in given sample,onfocus of edittext cursor is not visible
Issues like #9 are embarrassing and should have gotten caught before release.
I am have tried using both of the methods but it doesn't linkify at all also it doesn't let me long click on my background.
// by this
txv.setMovementMethod(BetterLinkMovementMethod.newInstance());
Linkify.addLinks(txv, Linkify.ALL);
// and by this
BetterLinkMovementMethod.linkify(Linkify.ALL, txvMsg);
I'm trying to open auto links with ChromeCustomTabs, so i intented to use this library to get the url upon click but the library is neither highlighting the links nor is it making them clickable.
Here`s the initial method i tried and nothing happened:
tv.movementMethod = BetterLinkMovementMethod.newInstance().apply {
setOnLinkClickListener { textView, url ->
CustomTabsIntent.Builder().build().launchUrl(activity, Uri.parse(url))
true
}
}
Then i decided to us this and it still isn't working.
BetterLinkMovementMethod.linkify(Linkify.ALL, tv)
.setOnLinkClickListener { textView, url ->
CustomTabsIntent.Builder().build().launchUrl(activity, Uri.parse(url))
true
}
When i set autoLink in XML, it highlights the links but just does what standard linkify does, opens the links in the default browser.
I am using this lib with Markwon. markwon is parsing html urls and the i am applying Linkify Spans over it. it is working but when i scroll recyclerview up and click on any link nothing happens and recyclerview scrolls up by 1 page
`BetterLinkMovementMethod.linkify(Linkify.ALL,myTextView)
.setOnLinkLongClickListener(this::onLinkLongClickListener)
myTextView.setOnLongClickListener(v -> {
//Some very important code that creates "ITEM"
return false;
}
...
boolean onLinkLongClickListener(...) {
// Adds something new to the already created "ITEM"
}`
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.