提供一个实现思路:
在传给webview之前,把数据中所有可能的电话号码/网址/邮箱地址添加上html标记,类似<a href></a>这种。
diff --git a/src/com/android/mail/browse/WebViewContextMenu.java b/src/com/android/mail/browse/WebViewContextMenu.java
index b81a3be..87a2ed2 100644
--- a/src/com/android/mail/browse/WebViewContextMenu.java
+++ b/src/com/android/mail/browse/WebViewContextMenu.java
@@ -42,6 +42,7 @@ import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.Charset;
+import android.util.Log;
/**
* <p>Handles display and behavior of the context menu for known actionable content in WebViews.
@@ -245,6 +246,7 @@ public class WebViewContextMenu implements OnCreateContextMenuListener,
// Show the correct menu group
String extra = result.getExtra();
+ Log.d("zhuwenwu", "onCreateContextMenu --> extra = " + extra);
menu.setGroupVisible(R.id.PHONE_MENU, type == WebView.HitTestResult.PHONE_TYPE);
menu.setGroupVisible(R.id.EMAIL_MENU, type == WebView.HitTestResult.EMAIL_TYPE);
menu.setGroupVisible(R.id.GEO_MENU, type == WebView.HitTestResult.GEO_TYPE);
@@ -277,6 +279,8 @@ public class WebViewContextMenu implements OnCreateContextMenuListener,
}
menu.setHeaderTitle(decodedPhoneExtra);
+Log.d("zhuwenwu", "281 decodedPhoneExtra: " +decodedPhoneExtra);
+
// Dial
final MenuItem dialMenuItem =
menu.findItem(getMenuResIdForMenuType(MenuType.DIAL_MENU));
@@ -318,6 +322,7 @@ public class WebViewContextMenu implements OnCreateContextMenuListener,
Analytics.getInstance().sendEvent(
CATEGORY_WEB_CONTEXT_MENU, ACTION_LONG_PRESS, "email", 0);
menu.setHeaderTitle(extra);
+Log.d("zhuwenwu", "324 extra: " +extra);
final Intent mailtoIntent =
new Intent(Intent.ACTION_VIEW, Uri.parse(WebView.SCHEME_MAILTO + extra));
menu.findItem(getMenuResIdForMenuType(MenuType.EMAIL_CONTACT_MENU))
@@ -329,6 +334,7 @@ public class WebViewContextMenu implements OnCreateContextMenuListener,
Analytics.getInstance().sendEvent(
CATEGORY_WEB_CONTEXT_MENU, ACTION_LONG_PRESS, "geo", 0);
menu.setHeaderTitle(extra);
+Log.d("zhuwenwu", "336 extra: " +extra);
String geoExtra = "";
try {
geoExtra = URLEncoder.encode(extra, Charset.defaultCharset().name());
@@ -373,8 +379,9 @@ public class WebViewContextMenu implements OnCreateContextMenuListener,
// SRC_ANCHOR_TYPE or the url would be specified in the extra. We don't need to
// call requestFocusNodeHref(). If we wanted to handle UNKNOWN HitTestResults, we
// would. With this knowledge, we can just set the title
+ Log.d("zhuwenwu", "setupAnchorMenu --> extra = " + extra);
menu.setHeaderTitle(extra);
-
+Log.d("zhuwenwu", "382 extra: " +extra);
menu.findItem(getMenuResIdForMenuType(MenuType.COPY_LINK_MENU)).
setOnMenuItemClickListener(new Copy(extra, "copy_link"));
diff --git a/src/com/android/mail/ui/HtmlConversationTemplates.java b/src/com/android/mail/ui/HtmlConversationTemplates.java
index 124cb83..d95110e 100644
--- a/src/com/android/mail/ui/HtmlConversationTemplates.java
+++ b/src/com/android/mail/ui/HtmlConversationTemplates.java
@@ -30,6 +30,12 @@ import com.google.common.annotations.VisibleForTesting;
import java.util.Locale;
import java.util.regex.Pattern;
+import android.util.Patterns;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import android.util.Log;
+
+
/**
* Renders data into very simple string-substitution HTML templates for conversation view.
*/
@@ -135,7 +141,12 @@ public class HtmlConversationTemplates extends AbstractHtmlTemplates {
final String showImagesClass = safeForImages ? "mail-show-images" : "";
String body = message.getBodyAsHtml();
-
+ // Modify bug 1876 recognise phonenum and mark it by qidongran start
+ body = body.replaceAll("@", "@");
+ body=addWebMark(body);
+ body=addTelMark(body);
+ body=addEmailMark(body);
+ // Modify bug 1876 recognise phonenum and mark it by qidongran end
/* Work around a WebView bug (5522414) in setBlockNetworkImage that causes img onload event
* handlers to fire before an image is loaded.
* WebView will report bad dimensions when revealing inline images with absolute URLs, but
@@ -214,4 +225,288 @@ public class HtmlConversationTemplates extends AbstractHtmlTemplates {
return emit();
}
+ // Modify bug 1876 by qidongran start
+ private String replacedString = "";
+ private String PHONE_HIGHLIGHT_ELEMENT = "tel:";
+ private String EMAIL_HIGHLIGHT_ELEMENT = "mailto:";
+
+ private String addWebMark(String text) {
+ replacedString = text;
+ String MATCH_WEB_URL = "www\\.[^(\\s|\"|<|')]*|[a-zA-z]+://[^(\\s|\"|<|')]*";
+ Pattern pattern = Pattern.compile(MATCH_WEB_URL);
+ Matcher matcher = pattern.matcher(replacedString);
+ try {
+ Log.d(TAG, "zhuwenwu1 addWebMark --> text:" + text);
+ replacedString = replaceWeb(matcher);
+Log.d(TAG, "zhuwenwu1 addWebMark --> replacedString:" + replacedString);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return replacedString;
+ }
+
+ private String replaceWeb(Matcher matcher) {
+ String keyString = "";
+ String result = replacedString;
+ StringBuffer sb = new StringBuffer("");
+ while (matcher.find()) {
+ keyString = matcher.group();
+ Log.d(TAG, "zhuwenwu matcher.find():" + keyString);
+ // the character in front og keystring is "." or "/" or "@"... ignore
+ if (matcher.start() - 1 >= 0) {
+ char keyChar = result.charAt(matcher.start() - 1);
+ if (keyChar == 45 || keyChar == 47 || keyChar == 61
+ || keyChar == 63 || keyChar == 95
+ || (48 <= keyChar && keyChar <= 57)
+ || (35 <= keyChar && keyChar <= 38)) {
+ matcher.appendReplacement(sb, "$0");
+ continue;
+ }
+ }
+ int end = matcher.start();
+ int start = end - 8;
+ if (start < 0) {
+ start = end - 7;
+ }
+ if (start >= 0) {
+ String urlStr = result.substring(start, end);
+ if (urlStr.contains("http") || urlStr.contains("Http")
+ || urlStr.contains("HTTP")
+ || urlStr.contains("ftp") || urlStr.contains("Ftp")
+ || urlStr.contains("FTP")
+ || urlStr.contains("rtsp")
+ || urlStr.contains("Rtsp")
+ || urlStr.contains("RTSP")) {
+ matcher.appendReplacement(sb, "$0");
+ continue;
+ }
+ }
+ // the character behind keystring is "."|or "@" or "A-z" ignore
+ if (matcher.end() != result.length()) {
+ char keyChar = result.charAt(matcher.end());
+ if ((64 <= keyChar && keyChar <= 122) || keyChar == 46
+ || (48 <= keyChar && keyChar <= 57)) {
+ matcher.appendReplacement(sb, "$0");
+ continue;
+ }
+ }
+
+ int endPhone = matcher.start();
+ int startPhone = endPhone - 7;
+ if (startPhone > 0) {
+ String emailStr = result.substring(startPhone, endPhone);
+ // contain "href" ignore
+ if (emailStr.contains("href")) {
+ matcher.appendReplacement(sb, "$0");
+ continue;
+ } else {
+ startPhone = endPhone + keyString.length() + 7;
+ if (startPhone > result.length()) {
+ startPhone = result.length();
+ }
+ emailStr = result.substring(endPhone, startPhone);
+ // contain "</a>" ignore
+ if (emailStr.contains("</a>")) {
+ matcher.appendReplacement(sb, "$0");
+ continue;
+ }
+ }
+ }
+
+ // contain keystring ignore
+ int endStr = matcher.start() - 2;
+ int startStr = endStr - keyString.length();
+ if (startStr > 0) {
+ String webStr = result.substring(startStr, endStr);
+ if (keyString.contains(webStr)) {
+ matcher.appendReplacement(sb, "$0");
+ continue;
+ }
+ }
+ if(keyString.startsWith("www")){
+ keyString = "http://" + keyString;
+ }
+ String replacement = String.format("<a href=\"%s\">%s</a>", keyString, keyString);
+ Log.d(TAG, "zhuwenwu replacement:" + replacement);
+ matcher.appendReplacement(sb, replacement);
+ }
+ matcher.appendTail(sb);
+ return sb.toString();
+ }
+
+ private String addEmailMark(String text) {
+ replacedString = text;
+ String replaceElement = "";
+ String MATCH_EMAIL_URL = "[\\w!#$%&'*+/=?^_`{|}~-]+(?:\\.[\\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\\w](?:[\\w-]*[\\w])?\\.)+[\\w](?:[\\w-]*[\\w])?";
+ Pattern pattern = Pattern.compile(MATCH_EMAIL_URL);
+ Matcher matcher = pattern.matcher(replacedString);
+ replaceElement = EMAIL_HIGHLIGHT_ELEMENT;
+ try {
+ Log.d(TAG, "zhuwenwu addEmailMark --> text:" + text);
+ replacedString = replaceKeyString(matcher, replaceElement);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ return replacedString;
+ }
+
+ private String addTelMark(String text) {
+ replacedString = text;
+ String replaceElement = "";
+ Pattern pattern = Pattern
+ .compile("(((13[0-9])|(147)|(145)|(15[^4,\\D])|(173)|(17[6-8])|(18[0-9]))\\d{8})"
+ + "|"
+ + "(((\\d{4}|\\d{3})-(\\d{7,8})-(\\d{4}|\\d{3}|\\d{2}|\\d{1}))|((\\d{4}|\\d{3})-(\\d{7,8}))|((\\d{7,8})-(\\d{4}|\\d{3}|\\d{2}|\\d{1}))|(\\d{7,8}))");
+ Matcher matcher = pattern.matcher(replacedString);
+ replaceElement = PHONE_HIGHLIGHT_ELEMENT;
+ try {
+ Log.d(TAG, "zhuwenwu addTelMark --> text:" + text);
+ replacedString = replaceKeyString(matcher, replaceElement);
+ } catch (Exception e) {
+ LogUtils.v(LogUtils.TAG, e, "Did not add tel mark");
+ }
+
+ return replacedString;
+ }
+
+ /**
+ * replace the key String
+ *
+ * @return replacedString text has been replaced
+ */
+ private String replaceKeyString(Matcher matcher, String replaceElement) {
+ String keyString = "";
+ String result = replacedString;
+ StringBuffer sb = new StringBuffer("");
+ while (matcher.find()) {
+ keyString = matcher.group();
+ Log.d(TAG, "zhuwenwu matcher.find():" + keyString);
+ if (PHONE_HIGHLIGHT_ELEMENT.equals(replaceElement)) {
+ // the character in front og keystring is "." or "/" or "@"... ignore
+ if (matcher.start() - 1 >= 0) {
+ char keyChar = result.charAt(matcher.start() - 1);
+ if (keyChar == 45 || keyChar == 47 || keyChar == 61
+ || keyChar == 63 || keyChar == 95
+ || (48 <= keyChar && keyChar <= 57)
+ || (35 <= keyChar && keyChar <= 38)) {
+ matcher.appendReplacement(sb, "$0");
+ continue;
+ }
+ }
+ int end = matcher.start();
+ int start = end - 8;
+ if (start < 0) {
+ start = end - 7;
+ }
+ if (start >= 0) {
+ String urlStr = result.substring(start, end);
+ if (urlStr.contains("http") || urlStr.contains("Http")
+ || urlStr.contains("HTTP")
+ || urlStr.contains("ftp") || urlStr.contains("Ftp")
+ || urlStr.contains("FTP")
+ || urlStr.contains("rtsp")
+ || urlStr.contains("Rtsp")
+ || urlStr.contains("RTSP")) {
+ matcher.appendReplacement(sb, "$0");
+ continue;
+ }
+ }
+ // the character behind keystring is "."|or "@" or "A-z" ignore
+ if (matcher.end() != result.length()) {
+ char keyChar = result.charAt(matcher.end());
+ if ((64 <= keyChar && keyChar <= 122) || keyChar == 46
+ || (48 <= keyChar && keyChar <= 57)) {
+ matcher.appendReplacement(sb, "$0");
+ continue;
+ }
+ }
+
+ int endPhone = matcher.start();
+ int startPhone = endPhone - 4;
+ String emailStr = result.substring(startPhone, endPhone);
+ // contain "tel" ignore
+ if (emailStr.contains("tel")) {
+ matcher.appendReplacement(sb, "$0");
+ continue;
+ }
+ // contain keystring ignore
+ int endStr = matcher.start() - 2;
+ int startStr = endStr - keyString.length();
+ if (startStr > 0) {
+ String webStr = result.substring(startStr, endStr);
+ if (keyString.contains(webStr)) {
+ matcher.appendReplacement(sb, "$0");
+ continue;
+ }
+ }
+ } else if (EMAIL_HIGHLIGHT_ELEMENT.equals(replaceElement)) {
+ // the character in front og keystring is "." or "/" or "@"... ignore
+ if (matcher.start() - 1 >= 0) {
+ char keyChar = result.charAt(matcher.start() - 1);
+ if (keyChar == 45 || keyChar == 47 || keyChar == 61
+ || keyChar == 63 || keyChar == 95
+ || (48 <= keyChar && keyChar <= 57)
+ || (35 <= keyChar && keyChar <= 38)) {
+ matcher.appendReplacement(sb, "$0");
+ continue;
+ }
+ }
+ int end = matcher.start();
+ int start = end - 8;
+ if (start < 0) {
+ start = end - 7;
+ }
+ if (start >= 0) {
+ String urlStr = result.substring(start, end);
+ if (urlStr.contains("http") || urlStr.contains("Http")
+ || urlStr.contains("HTTP")
+ || urlStr.contains("ftp") || urlStr.contains("Ftp")
+ || urlStr.contains("FTP")
+ || urlStr.contains("rtsp")
+ || urlStr.contains("Rtsp")
+ || urlStr.contains("RTSP")) {
+ matcher.appendReplacement(sb, "$0");
+ continue;
+ }
+ }
+ // the character behind keystring is "."|or "@" or "A-z" ignore
+ if (matcher.end() != result.length()) {
+ char keyChar = result.charAt(matcher.end());
+ if ((64 <= keyChar && keyChar <= 122) || keyChar == 46
+ || (48 <= keyChar && keyChar <= 57)) {
+ matcher.appendReplacement(sb, "$0");
+ continue;
+ }
+ }
+
+ int endPhone = matcher.start();
+ int startPhone = endPhone - 7;
+ String emailStr = result.substring(startPhone, endPhone);
+ // contain "mailto" ignore
+ if (emailStr.contains("mailto")) {
+ matcher.appendReplacement(sb, "$0");
+ continue;
+ }
+ // contain keystring ignore
+ int endStr = matcher.start() - 2;
+ int startStr = endStr - keyString.length();
+ if (startStr > 0) {
+ String webStr = result.substring(startStr, endStr);
+ if (keyString.contains(webStr)) {
+ matcher.appendReplacement(sb, "$0");
+ continue;
+ }
+ }
+ }
+ String replacement = String.format("<a href=\"%s\">%s</a>", replaceElement + keyString,
+ keyString);
+ Log.d(TAG, "zhuwenwu replacement:" + replacement);
+ matcher.appendReplacement(sb, replacement);
+ // LogUtils.i("qidongran", "sb:" + sb);
+ }
+ matcher.appendTail(sb);
+ return sb.toString();
+ }
+ // Modify bug 1876 by qidongran end
}
diff --git a/src/com/android/mail/utils/HtmlSanitizer.java b/src/com/android/mail/utils/HtmlSanitizer.java
index ca831df..7a46db6 100644
--- a/src/com/android/mail/utils/HtmlSanitizer.java
+++ b/src/com/android/mail/utils/HtmlSanitizer.java
@@ -125,7 +125,7 @@ public final class HtmlSanitizer {
* Disallow the "cid:" url on links. Do allow "mailto:" urls to support sending mail.
*/
private static final AttributePolicy A_HREF_PROTOCOLS =
- new FilterUrlByProtocolAttributePolicy(ImmutableList.of("mailto", "http", "https"));
+ new FilterUrlByProtocolAttributePolicy(ImmutableList.of("mailto", "http", "https", "tel", "sms"));
/**
* Disallow the "mailto:" url on images so that "Show pictures" can't be used to start composing
@@ -189,7 +189,7 @@ public final class HtmlSanitizer {
*/
private static final PolicyFactory POLICY_DEFINITION = new HtmlPolicyBuilder()
.allowAttributes("dir").matching(true, "ltr", "rtl").globally()
- .allowUrlProtocols("cid", "http", "https", "mailto")
+ .allowUrlProtocols("cid", "http", "https", "mailto", "tel", "sms") /* @}*/
.allowStyling(CssSchema.union(CssSchema.DEFAULT, ADDITIONAL_CSS))
.disallowTextIn("applet", "frameset", "object", "script", "style", "title")
.allowElements("a")
以上为修改。
/package/apps/UnifiedEmail/src/com/android/mail/providers/Message.java
bodyhtml = cursor.getString(UIProvider.MESSAGE_BODY_HTML_COLUME);
"Email正文webview加载方式
使用android.webkit.WebView控件
在xml布局文件中定义
<WebView
android:id=”@+id/webkit01”
android:layout:width=”fill_parent”
android:layout:height=”fill_parent”
android:layout:weight=”1” />
在程序中使用WebView
mWebView = (WebView) findViewById(R.id.webview01);
mWebView.loadUrl(“http://www.google.com”);
通过WebSettings来设置WebView的属性和状态
WebSettings webSettings = mWebview.getSettings();
当WebView销毁后,再使用WebSettings会抛出IllegalStateException异常。
WebSettings常用方法
1. setAllowFileAccess, 启用/禁止WebKit访问文件数据
2. setBlockNetworkImage, 是否显示网络图像
3. setBuiltInZoomControls, 是否支持缩放
4. setCacheMode, 设置缓存模式
5. setDefaultFontSize, 设置默认字体大小
6. setDefaultTextEncodingName, 设置默认的解码方式
7. setFixedFontFamily, 设置固定使用的字体
8. setJavaScriptEnabled, 是否支持JavaScript
9. setLayoutAlgorithm, 设置布局方式
10. setLightTouchEnabled,
11. setSupportZoom, 是否支持变焦
使用WebViewClient
WebViewClient用来处理各种通知、请求等事件,WebView调用setWebViewClient()来指定一个WebViewClient对象。
WebViewClient常用方法
1. doUpdateVisitedHistory, 更新历史记录
2. onFormResubmission, 重新请求网页数据
3. onLoadResource, 加载资源
4. onPageFinished, 网页加载完毕
5. onPageStarted, 网页开始加载
6. onReceivedError, 报告错误信息
7. onScaleChanged, 发生Scale改变
8. shouldOverrideUrlLoading, 控制新的连接在当前WebView中打开
使用WebChromeClient
WebChromeClient用来处理JavaScript对话框、网站图标、网站title、加载进度等。
WebChromeClient常用方法
1. onCloseWindow,
2. onCreateWindow,
3. onJsAlert,
4. onJsConfirm,
5. onJsPrompt,
6. onProgressChanged,
7. onReceivedIcon,
8. onReceivedTitle,
9. onRequestFocus
示例:实现简单浏览网页的功能
// 点击返回键返回到前一个页面
if((keyCode==KeyEvent.KEYCODE_BACK) && (mWebView.canGoBack()))
mWebView.goBack();
// 学习如何处理JavaScript常用对话框
WetSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setAllowFileAccess(true);
webSettings.setBuiltInZoomControls(true);
// 设置WebViewClient
mWebView.setWebViewClient(new WebViewClient(){
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
// onPageFinished
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
}
// onPageStarted
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
}
});
// 设置WebChromeClient
mWebView.setWebChromeClient(new WebChromeClient(){
// 处理JavaScript中的alert
public boolean onJsAlert(WebView view, String url, String msg, fianl JsResult result) {
// 弹出对话框 builder.show()
// 确定
result.confirm();
return true;
}
// 处理JavaScript中的confirm
public boolean onJsConfirm(WebView view, String url, String msg, fianl JsResult result) {
// 弹出对话框 builder.show()
// 确定
result.confirm();
// 否定
result.cancel();
return true;
}
// 处理JavaScript中的prompt
public boolean onJsPrompt(WebView view, String url, String msg, String defaultValue, fianl JsPromptResult result) {
// 弹出对话框 builder.show()
// 确定
result.confirm(value);
// 否定/取消
result.cancel();
return true;
}
// 处理网页加载进度条
public void onProgressChanged(WebView view, int newProgress) {
getWindow().setFeatureInt(Window.FEATURE_PROGRESS, newProgress*100);
super.onProgressChanged(view, newProgress);
}
// 得到网页的标题,设置app的标题title。
public void onReceivedTitle(WebView view, String title) {
setTitle(title);
super.onReceivedTitle(view, title);
}
});
// 连接按钮点击事件处理程序
String url = mUrlBox.getText().toString();
if(URLUtil.isNetworkUrl(url)){
mWebView.loadUrl(url);
}
// 点击返回按键
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()) {
mWebView.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
// 示例代码:弹出对话框
final View dialogView = mInflater.inflate(R.layout.xxx, null);
Builder builder = new Builder(mContext);
builder.setTitle(“xxx”);
builder.setView(dialogView);
// 肯定
builder.setPositiveButton(android.R.string.ok, new AlertDialog.OnclickListener(){
public void onClick(DialogInterface dialog, int which) {
}
});
// 否定
builder.setNegativeButton(android.R.string.cancel, new AlertDialog.OnclickListener(){
public void onClick(DialogInterface dialog, int which) {
}
});
// 取消
builder.setOnCancelListener(new AlertDialog.OnCancelListener(){
public void onClick(DialogInterface dialog, int which) {
}
});
// 不允许取消
builder.setCancelable(false);
builder.create();
builder.show();
return true;