Android WebView“电话:”链接显示未找到网页


68

我试图让我的android webview应用程序打开tel:手机链接。每次我打开电话链接时,它都会很好地工作并打开电话。但是,一旦完成通话并返回到应用程序,该页面就会显示“找不到网页tel:0000000000”。然后,我必须再次单击“后退”按钮才能进入单击电话号码的页面。

有没有办法让它打开TEL链接而不尝试在Webview中查找页面并在电话中打开它?

这是我在WebView中使用的代码,以覆盖其对TEL和Mailto链接的处理:

        public boolean shouldOverrideUrlLoading(WebView view, String url) {
        if (url.startsWith("mailto:") || url.startsWith("tel:")) { 
                Intent intent = new Intent(Intent.ACTION_VIEW,
                        Uri.parse(url)); 
                startActivity(intent); 
                } 
        view.loadUrl(url);
        return true;
        }

任何帮助,将不胜感激。我在过去的2个小时里一直在搜索古德,但未能给出任何答案。


1
尝试使用ACTION_DIAL的tel:链接吗?
EboMike 2010年

等等,文档实际上说ACTION_VIEW很好:developer.android.com/reference/android/content/Intent.html没关系,然后...
EboMike 2010年

愚蠢的问题:您设置WebViewClient正确吗?其他一切有用吗?
EboMike 2010年

是的...除tel:链接外,其他所有功能均正常运行。即使是那些工作,也只是当您不接听电话并返回到应用程序时,它位于显示未找到的页面上。
杰夫·托马斯

@EboMike:我第一次认为您是对的:developer.android.com/guide/appendix/g-app-intents.html-我不相信“请注意VIEW操作如何执行被认为是正确的操作。对于特定URI最合理的事情”。
CommonsWare 2010年

Answers:


115

好的,所以我解决了我认为的问题。我只需要按如下所示分隔URL覆盖:

public boolean shouldOverrideUrlLoading(WebView view, String url) {
    if (url.startsWith("tel:")) { 
        Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(url)); 
        startActivity(intent);
        view.reload();
        return true;
    }

    view.loadUrl(url);
    return true;
}

现在,我的常规链接和电话链接一样有效。我也可以在其中添加geo:链接,如果需要的话,也不会给我带来以前在手机上打开地图所遇到的问题。


2
咄。我刚刚意识到您在tel:链接上调用view.LoadUrl(url)。您不妨添加一个return trueafter startActivity()
EboMike 2010年

6
将<uses-permission android:name =“ android.permission.CALL_PHONE” />添加到清单。
jasonflaherty 2012年

9
@jasonflaherty实际上CALL_PHONE权限是针对ACTION_CALL而不是ACTION_DIAL。因此,即使未经许可,上述代码也可以正常工作。
Elyess Abouda

4
您不需要view.loadUrl(url); 返回true将导致WebView覆盖URL并对其进行处理。
Matt Gaunt

5
请不要按原样复制上面的代码。从回调返回false,而不是调用view.loadUrl。调用loadUrl会引入一个细微的错误,即如果页面中有任何带有自定义方案URL的iframe(例如<iframe src =“ tel:123” />),它将在应用程序的主框架中导航到该URL,很可能会破坏应用程序为副作用。
marcin.kosiba

56

而不是call loadUrl(url),仅对不应该覆盖的URL返回false:

public boolean shouldOverrideUrlLoading(WebView view, String url) {
    if( URLUtil.isNetworkUrl(url) ) {
        return false;
    }

    // Otherwise allow the OS to handle it
    Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
    startActivity( intent ); 
    return true;
}

我发现VIEWing tel:在我们尝试过的所有手机上都能正常工作。由于DIAL动作,因此无需特殊情况。

我注意到YouTube视频之类的内容在WebViews中不起作用,因此您可能也希望检测到它们。

通过查询PackageManager中用于处理您的URI(不是嵌入式浏览器)的Activity,可以针对各种URI推广整个过程。这可能是过大的,并且会与其他已安装的浏览器混淆。


2
您可以使用URLUtil.isNetworkUrl(url)进行http / https(及其他)检查
xnagyg

2
很棒的建议,@ xnagyg。我已经更新了代码段。
2016年

2
这比添加单个URL方案更为实用。
安德烈Morujão

18

根据文档并根据我的经验,Intent.ACTION_VIEW可以很好地解析tel:sms:smsto:mms:mmsto:链接。

这是5合1:

@Override
    public boolean shouldOverrideUrlLoading(WebView webview, String url)
    {
     if (url.startsWith("tel:") || url.startsWith("sms:") || url.startsWith("smsto:") || url.startsWith("mms:") || url.startsWith("mmsto:"))
       { 
         Intent intent = new Intent(Intent.ACTION_VIEW,Uri.parse(url)); 
         startActivity(intent); 
         return true;
       }
    return false;
   }

14

注意:-shouldOverrideUrlLoading不推荐使用Android Nouget之后

您需要与shouldOverrideUrlLoading一起使用以shouldOverrideUrlLoading获得更好的支持。另外,您可能想检查url是否具有mailto:tel:,它们在HTML5中分别用于触发邮件客户端和电话拨号。

完整的解决方案现在看起来像这样

    @SuppressWarnings("deprecation")
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        if (url.startsWith("mailto:")) {  
            //Handle mail Urls
            startActivity(new Intent(Intent.ACTION_SENDTO, Uri.parse(url)));
        } else if (url.startsWith("tel:")) {
            //Handle telephony Urls
            startActivity(new Intent(Intent.ACTION_DIAL, Uri.parse(url)));
        } else {
            view.loadUrl(url);
        }
        return true;
    }

    @TargetApi(Build.VERSION_CODES.N)
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
        final Uri uri = request.getUrl();
        if (uri.toString().startsWith("mailto:")) {
            //Handle mail Urls
            startActivity(new Intent(Intent.ACTION_SENDTO, uri));
        } else if (uri.toString().startsWith("tel:")) {
            //Handle telephony Urls
            startActivity(new Intent(Intent.ACTION_DIAL, uri));
        } else {
            //Handle Web Urls
            view.loadUrl(uri.toString());
        }
        return true;
    }

完美...处理牛轧糖和牛轧糖。谢谢
Sudheesh R

谢谢!使用@Suppress("DEPRECATION")代替@SuppressWarnings("deprecation")。您可以为两个事件创建一个方法。用于view.getContext()在之前插入startActivity()
CoolMind

4
public class MainActivity extends Activity {

private static final String HTML ="<!DOCTYPE html><html><body><a 
href='tel:867-5309'>Click here to call!</a></body></html>";
private static final String TEL_PREFIX = "tel:";

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    WebView wv = (WebView) findViewById(R.id.webview);
    wv.setWebViewClient(new CustomWebViewClient());
    wv.loadData(HTML, "text/html", "utf-8");
}

private class CustomWebViewClient extends WebViewClient {

    @Override
    public boolean shouldOverrideUrlLoading(WebView wv, String url) {
        if(url.startsWith(TEL_PREFIX)) {
            Intent intent = new Intent(Intent.ACTION_DIAL);
            intent.setData(Uri.parse(url));
            startActivity(intent);
            return true;
        }
        return false;
    }
}

}

这是我发现的解决方法。您必须使用此方法。

wv.setWebViewClient(new CustomWebViewClient());

3

如果没有分配给WebView的WebViewClient,则默认情况下,WebView将要求“活动管理器”为URL选择适当的处理程序。如果提供了WebViewClient,则您应该自己处理不同的URL,并在WebViewClient.shouldOverrideUrlLoading()中返回true,否则它将尝试将请求发送到URL并获得错误,然后触发onReceiveError()。

检查文档:WebViewClient.shouldOverrideUrlLoading

     @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        if (url.startsWith("tel:")) { 
            // ...TODO: launch a Dial app or send SMS or add to contact, etc...
            return true;
        }
        else if (url.startsWith("mailto:")) {
            // ...TODO: send email to someone or add to contact, etc...
            return true;
        }
        else {
            // ...TODO: Handle URL here
            boolean handled = yourHandleUrlMethod(url);
            return handled;
        }
    }

1
public boolean shouldOverrideUrlLoading(WebView view, String url)
       {Uri query_string=Uri.parse(url);
        String query_scheme=query_string.getScheme();
        String query_host=query_string.getHost();
        if ((query_scheme.equalsIgnoreCase("https") || query_scheme.equalsIgnoreCase("http"))
            && query_host!=null && query_host.equalsIgnoreCase(Uri.parse(URL_SERVER).getHost())
            && query_string.getQueryParameter("new_window")==null
           )
           {return false;//handle the load by webview
           }
        try
           {Intent intent=new Intent(Intent.ACTION_VIEW, query_string);
            String[] body=url.split("\\?body=");
            if (query_scheme.equalsIgnoreCase("sms") && body.length>1)
               {intent=new Intent(Intent.ACTION_VIEW, Uri.parse(body[0]));
                intent.putExtra("sms_body", URLDecoder.decode(body[1]));
               }
            view.getContext().startActivity(intent);//handle the load by os
           }
        catch (Exception e) {}
        return true;
       }

1
您能否解释一下您的解决方案?尤其是对URL_SERVER的测试(顺便说一句,它是缺少的)可能并非所有人都清楚。
Ivin

URL_SERVER是“ your.app.domain.com ”,并“返回false”;意味着您网站中的页面将在应用程序中打开,而不会在android浏览器中打开
diyism 2013年

1

一些常量在WebView类中是静态的。您应该使用Intent.ACTION_VIEW代替Intent.ACTION_DIALACTION_SENDTO

@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
    String url = request.getUrl().toString();
    if (url.startsWith(WebView.SCHEME_TEL)
            || url.startsWith("sms:")
            || url.startsWith(WebView.SCHEME_MAILTO)
            || url.startsWith(WebView.SCHEME_GEO)) {
        
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setData(Uri.parse(url));
        startActivity(intent); // view.context.startActivity(intent);
    }
    return true;
}

0
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    WebView wv = (WebView) findViewById(R.id.webview);
    wv.setWebViewClient(new CustomWebViewClient());
    wv.loadData(HTML, "text/html", "utf-8");
}

private class CustomWebViewClient extends WebViewClient {
    @SuppressWarnings("deprecated")
    @Override
    public boolean shouldOverrideUrlLoading(WebView wv, String url) {
        if(url.startsWith(TEL_PREFIX)) {
            Intent intent = new Intent(Intent.ACTION_DIAL);
            intent.setData(Uri.parse(url));
            startActivity(intent);
            return true;
        }
        return false;
    }

我尝试使用上面发布的此方法,但是当我添加“ shouldoverrideurlloading”时,出现错误,表明此方法已弃用。API是否有使用此代码的限制?..我试图在手机中安装此代码,但应用程序会自行杀死。我该如何解决?..请帮助
Naju Mat Isa

0

对于那些尝试使用@jeff Thomas回答但出现以下错误的用户:

cannot find symbol view.startActivity(intent);

您可以使用以下代码:

public boolean shouldOverrideUrlLoading(WebView view, String url) {
    if (url.startsWith("tel:")) { 
        Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(url)); 
        view.getContext().startActivity(intent);
        view.reload();
        return true;
    }

    view.loadUrl(url);
    return true;
}

我只是更改 startActivity(intent)view.getContext().startActivity(intent)并且多数民众赞成为我工作。


同意。您也可以包装startActivitytry-catch块。view.reload();不需要。
CoolMind

0

看着Hitesh Sahukam C答案,我得到了解决方案。

在API 21中,它引发了一个异常:“ android.util.AndroidRuntimeException:从Activity上下文外部调用startActivity()需要FLAG_ACTIVITY_NEW_TASK标志。这真的是您想要的吗?”。

正如diyism所注意到的,也可能找不到活动。因此,我处理了这些情况。

class MyWebViewClient : WebViewClient() {

    @Suppress("DEPRECATION")
    override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
        if (view != null && url != null) {
            return resolveUri(view.context, Uri.parse(url))
        }
        return super.shouldOverrideUrlLoading(view, url)
    }

    @TargetApi(Build.VERSION_CODES.N)
    override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
        val uri = request?.url
        if (view != null && uri != null) {
            return resolveUri(view.context, uri)
        }
        return super.shouldOverrideUrlLoading(view, request)
    }

    private fun resolveUri(context: Context, uri: Uri): Boolean {
        val url = uri.toString()
        URL_SCHEMES.forEach {
            if (url.startsWith(it)) {
                val intent = Intent(Intent.ACTION_VIEW).apply {
                    data = uri
                    addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                }
                try {
                    context.startActivity(intent)
                } catch (e: ActivityNotFoundException) {
                }
                return true
            }
        }
        return false
    }

    companion object {
        private val URL_SCHEMES = arrayOf(WebView.SCHEME_TEL,
            WebView.SCHEME_MAILTO, WebView.SCHEME_GEO, "sms:", "smsto:", "mms:", "mmsto:")
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.