事情的起因是这样的,因为需要在Email的显示界面中为电话号码添加高亮显示,从而提高用户体验。我想用过google自带邮箱的朋友都知道,当我们的收到邮件的内容中含有网址,邮箱地址时,会在显示界面中以超链接的方式显示,此时当我们点击该网址或者邮箱地址时,便会弹出浏览器或者撰写邮件。目前的情况是,当我们的邮件内容中含有可识别的电话号码时,我们可以点击并跳转到拨号界面,但却不能高亮显示,因此我们的目标就是将电话号码修改为高亮显示,如下图1:
图1:修改后的效果
那么我们还是列出我们的目的吧,以免忘记(我就容易忘记:D)。
目标:将接收邮件内容中的电话号码高亮显示。
因为自己也是第一次接触邮件这块,因此可能走了很多弯路或者说的并不清除,请别见笑,如果后文中有错误还恳请各位朋友指正。毕竟,没有人一生下来就是老鸟!!!
好了废话不多说,开始吧!
1.找到问题的根源
需要修改的Email读取邮件的界面,当我看到该界面的时候第一反应,这货应该是一个TextView,用于撰写Email的界面应该是一个EditText。毕竟TextView用于显示这些内容足够了啊,进而联想到xml布局文件中的android:autoLink属性,只要为该TextView控件加上该属性则在TextView中的网址,邮件地址,电话号码等自动会变为超链接(不信的可以自己试试)。有了这个思路那么我们首先应该找到显示邮件内容的布局文件(这里提一下,1.如果像我等小白的话可能就会老老实实的去找packages/apps/Email/res/layout,然后再去看哪个名字像是显示内容的,比如:Mmessage_content.xml这种就很像,我一开始就是这么找的T_T。2.如果以前看过源码的朋友估计会通过查看该APP的应用程序入口,然后根据如果跳转到邮件显示Activity最后再搜索setContentView方法就可以知道其布局文件。3.其实其实,SDK/tools文件夹下真的有好多有用的东西——hierarchyviewer就是其中之一,我们只要运行该工具同时连接上我们的设备,也可以是模拟器,该工具会自动列出当前界面的布局状态,如图2所示)。
图2 hierarchyviewer显示
我用的是Ubuntu10.04 64bit 在windows下都差不多的,hierarchyviewer存放在SDK下的tools目录中,通过hierarchyviewer我可轻松的分析该界面的布局状态。鼠标左键选中以上布局文件时,右边界面中会有提示。因此在这里我们可以很轻松的将我们前面的假设推翻,显示界面根本就不是什么TextView而是WebView(小白就是小白啊T_T)。怎么会是WebView呢,稍稍做过web开发的人都知道的吧,我们要在界面中显示图片,超链接,邮件等等,这些东西如果要放到TextView中去做???很明显应该选择WebView嘛,同时我们从服务其获得的信息就是html字符串(后文会提到)。因此前面的问题就变成了在WebView中对电话号码进行高亮显示。
2.查看源代码
Linux之父Linus曾说过,要了解程序原理的最好方法就是——Read the fucking source code.好吧,通过查看程序入口,然后跳转到显示界面这些步骤省略,最终我们找到/packages/apps/Email/src/com/android/email/activity/MessageView.java,该类主要完成消息内容的显示。那么如何开始查看呢?因为我们主要关心的WebView的设置,因此我们可以直接搜索WebView来查看,结果全文中只有一个WebView:
private WebView mMessageContentView;
因此我们可以继续搜索mMessageContentView这个对象,我们可以在onCreate方法中找到:
mMessageContentView.setClickable(true);
mMessageContentView.setLongClickable(false); // Conflicts with ScrollView, unfortunately
mMessageContentView.setVerticalScrollBarEnabled(false);
mMessageContentView.getSettings().setBlockNetworkLoads(true);
mMessageContentView.getSettings().setSupportZoom(false);
mMessageContentView.setWebViewClient(new CustomWebViewClient());
这里对该WebView对象进行了一些设置。
(1).setClickable(true)是设置WebView可点击;
(2).setLongClickable(false)是该WebView不支持长点击事件,这是因为会和ScrollView冲突,因此将其设置位false,可以在ScrollView中处理长点击事件;
(3).setVerticalScrollBarEnabled(false)接着设置垂直方向滚动禁止,也是和ScrollView冲突;
(4).setBlcokNetworkLoads(true)设置是否阻止网络加载,如果邮件中含有图片则会打开该属性;
(5).setSupportZoom(false)这是设置当前WebView不支持缩放;
(6).setWebViewClient使WebView接收到各种通知和请求;
接下来我们继续查找mMessageContentView可以发现mMessageContentView.loadDataWithBaseURL(...)方法,通过命名我们可以知道该方法用于加载URL同时,其中的参数很是奇怪,有text,mHtmlTextWebView等,这些东西是什么呢,果断加入Log.i("TAG","text:"+text);将这些信息通过log.i输出来,加入这些代码之后单独编译Email并将修改之后的Email.apk push到/system/app路径下实践,根据打印出来的log我们可以初步将目光锁定到:
/**
* Reload the body from the provider cursor. This must only be called from the UI thread.
*
* @param bodyText text part
* @param bodyHtml html part
*
* TODO deal with html vs text and many other issues
*/
private void reloadUiFromBody(String bodyText, String bodyHtml) {
String text = null;
mHtmlTextRaw = null;
boolean hasImages = false;
if (bodyHtml == null) {
text = bodyText;
/*
* Convert the plain text to HTML
*/
StringBuffer sb = new StringBuffer("<html><body>");
if (text != null) {
// Escape any inadvertent HTML in the text message
text = EmailHtmlUtil.escapeCharacterToDisplay(text);
// Find any embedded URL's and linkify
Matcher m = Patterns.WEB_URL.matcher(text);
while (m.find()) {
int start = m.start();
/*
* WEB_URL_PATTERN may match domain part of email address. To detect
* this false match, the character just before the matched string
* should not be '@'.
*/
if (start == 0 || text.charAt(start - 1) != '@') {
String url = m.group();
Matcher proto = WEB_URL_PROTOCOL.matcher(url);
String link;
if (proto.find()) {
// This is work around to force URL protocol part be lower case,
// because WebView could follow only lower case protocol link.
link = proto.group().toLowerCase() + url.substring(proto.end());
} else {
// Patterns.WEB_URL matches URL without protocol part,
// so added default protocol to link.
link = "http://" + url;
}
String href = String.format("<a href=\"%s\">%s</a>", link, url);
m.appendReplacement(sb, href);
}
else {
m.appendReplacement(sb, "$0");
}
}
m.appendTail(sb);
}
sb.append("</body></html>");
text = sb.toString();
} else {
text = bodyHtml;
mHtmlTextRaw = bodyHtml;
hasImages = IMG_TAG_START_REGEX.matcher(text).find();