I got this new crash exception after the newest app update. It seems to not point anywhere. Would anyone be able to tell what the issue is? It looks like a possible email-forma
By the way no email client can occur if no email account is set up on the device so technically this can occur on real devices as well. Here is the code for disabling the AutoLink if not available as suggested by novettam:
protected boolean checkIntent(Intent intent)
{
PackageManager packageManager = getPackageManager();
List<ResolveInfo> apps = packageManager.queryIntentActivities(intent, 0);
return apps.size() > 0 && !( apps.get(0).activityInfo.name.equals(activity.getClass().getName()) && apps.size() == 1) ;
}
protected Intent createDummyEmailIntent()
{
final Intent emailIntent = new Intent(Intent.ACTION_SENDTO, Uri.fromParts(
"mailto", "abc@gmail.com", null));
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "EXTRA_SUBJECT");
return emailIntent;
}
protected Intent createDummyWebIntent()
{
final Intent webIntent = new Intent(Intent.ACTION_VIEW).setData(Uri.parse("http://www.google.co.uk"));
return webIntent;
}
protected Intent createDummyPhoneIntent(){
String uri = "tel:" + "0131 666 7777".trim() ;
final Intent phoneIntent = new Intent(Intent.ACTION_DIAL);
phoneIntent.setData(Uri.parse(uri));
return phoneIntent;
}
//Checking
if ( !checkIntent(intent) ) {
textview.setAutoLinkMask(0);
}
//be sure that you call setText after this
Copied from my other answer
I have already had the same issue, and I solved it by creating a new class that checks for null
before actually launching the Intent
.
All you have to do is to replace all URLSpan
spans, before setting the TextView
's text (which means you cannot use setAutoLinkMask()
).
This has to be done, because URLSpan
's onClick()
method does not perform any kind of null
checks.
How to preceed:
TextView txt = ...
txt.setLinksClickable(true);
txt.setText(SafeURLSpan.parseSafeHtml(<<YOUR STRING GOES HERE>>));
txt.setMovementMethod(LinkMovementMethod.getInstance());
Kinds of strings that could be used in <<YOUR STRING GOES HERE>>
:
"Click here: <a href=\"http://google.com\">My links</a>"
"Mail me: <a href=\"mailto:john@doe.com\">My email</a>"
... and so on...
Here is the source for SafeURLSPan
class (I use it in my app FPlay, and it has been tested on Android 10+):
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.provider.Browser;
import android.text.Html;
import android.text.Spannable;
import android.text.style.URLSpan;
import android.view.View;
public final class SafeURLSpan extends URLSpan {
public SafeURLSpan(String url) {
super(url);
}
@Override
public void onClick(View widget) {
try {
final Uri uri = Uri.parse(getURL());
final Context context = widget.getContext();
final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
if (context != null && intent != null) {
intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
context.startActivity(intent);
}
} catch (Throwable ex) {
}
}
public static CharSequence parseSafeHtml(CharSequence html) {
return replaceURLSpans(Html.fromHtml(html.toString()));
}
public static CharSequence replaceURLSpans(CharSequence text) {
if (text instanceof Spannable) {
final Spannable s = (Spannable)text;
final URLSpan[] spans = s.getSpans(0, s.length(), URLSpan.class);
if (spans != null && spans.length > 0) {
for (int i = spans.length - 1; i >= 0; i--) {
final URLSpan span = spans[i];
final int start = s.getSpanStart(span);
final int end = s.getSpanEnd(span);
final int flags = s.getSpanFlags(span);
s.removeSpan(span);
s.setSpan(new SafeURLSpan(span.getURL()), start, end, flags);
}
}
}
return text;
}
}
I ran into this problem as well, the thing is that when you define:
android:autoLink="email"
android:text="some@email.com"
or any other type of autolink in a TextView, android handles everything for you.
In the emulator email is not setup by default, so there is no app to handle the intent, and this makes your app crash if you click an autolinked email address. If you open the mail app and follow the instructions to set it up, then when you click the email address it will work.
I don't know what you can do to work around this issue. I guess it might not be safe to assume the user has mail setup or at least one app capable of handling that intent. I also guess that the app shouldn't be crashing... I think this is an issue with android as in this case you have no way of handling the exception.
If you look at the source of URLSpan (If autolink is set, TextView uses android.text.util.Linkify.addLinks(..) which creates instances of URLSpan to create links): http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.1.2_r1/android/text/style/URLSpan.java#URLSpan
public void onClick(View widget) {
Uri uri = Uri.parse(getURL());
Context context = widget.getContext();
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
context.startActivity(intent);
}
They could either handle the exception or use PackageManager to determine if the Intent would succeed before calling startActivity() (like they recommend: http://developer.android.com/training/basics/intents/sending.html#Verify) or use a chooser.
Although the Android platform guarantees that certain intents will resolve to one of the built-in apps (such as the Phone, Email, or Calendar app), you should always include a verification step before invoking an intent.
Caution: If you invoke an intent and there is no app available on the device that can handle the intent, your app will crash.
So my question would be: is there any guarantee that an email intent will always resolve on a real device?
ok... on the same section they also mention:
Note: You should perform this check when your activity first starts in case you need to disable the feature that uses the intent before the user attempts to use it. If you know of a specific app that can handle the intent, you can also provide a link for the user to download the app (see how to link to your product on Google Play).
One can always check if the intent resolves to any activity at the start of the application, set a flag and every time the layout containing such TextView is inflated we find it and disable auto link for email, etc. (and this is when I ponder killing an unicorn over coding that)