WM有约II(五):区别对待不同的手机号码
WM有约II(五):区别对待不同的手机号码
Written by Allen Lee
不要删除我的短信息!
试想一下,某天你的朋友给你发了下面这条短信息:
在干嘛呢?早上收到你的东西了,今晚有空出来聊聊吗?
而此时应用程序正在运行,将会发生什么事呢?没错,你的朋友将会收到自动回复,但是,你却永远看不到这条短信息,因为它已经被删除了。能够想象吗,在某些情况下,错过上面这条短信息可能会引发一些不必要的误会……
为此,我们需要区分默认的查询短信息和自定义的查询短信息,前者在处理后应该自动删除,而后者则应该保留。因为默认的查询短信息都是以"{Trombone:"开头的,所以我们只需一个MessageInterceptor就可以截获所有默认的查询短信息了:
代码 1
在继续之前,我要就某些类型的命名变更说明一下,首先是IInterceptionProcessor接口重命名为ISmsProcessor,它的Process方法的sms参数类型改为SmsMessage;接着是InterceptionManager类重命名为InterceptorManager,里面的m_InterceptionProcessors私有成员重命名为m_SmsProcessors。
在代码1里,我们需要从截获的短信息中提取出短信息处理器的名字,获取对应的短信息处理器对象,然后把短信息交由它处理,其中,提取名字的工作由ExtractSmsProcessorName方法负责:
代码 2
至于自定义的查询短信息,我们仍然通过配置文件来存放MessageInterceptor的配置信息(旧的配置信息可以扔了):
代码 3
这些MessageInterceptor的创建和之前的(参见《WM有约II(二):持续改进》的代码2)一样,除了InterceptionAction属性的值需要改为InterceptionAction.Notify,这样,自定义的查询短信息就不会被删除了。
好了,现在用Cellular Emulator发送一个短信息看看:
图 1
嗯,自动回复功能正常,再来看看短信息有没有保留下来:
图 2
很好!这样我们就不会错过任何重要的信息了。
这是谁的手机号码?
到目前为止,无论谁发送查询短信息,应用程序都会自动回复,这样不好,我希望应用程序忽略陌生的手机号码,即如果发送查询短信息的人不在我的联系人里,就不要回复了。那么,如何判断某个人是否在我的联系人里呢?我们可以通过SmsMessage.From属性获取一个Recipient对象,它存储了发送方的信息,包括发送方的姓名(Recipient.Name属性)和地址(Recipient.Address属性)。Recipient.Address属性的值可能是一个手机号码(例如+8613713149394),也可能是一个由姓名和手机号码组合而成的字符串(例如"Stephen Chou" <+8613713149394>),这取决于发送方是否在联系人里,所以你不能简单地把它的值和Contact.MobileTelephoneNumber属性的值进行比较。Recipient.Name属性的值可能是一个手机号码,也可能是一个姓名,当发送方不在联系人里时,它的值就是发送方的手机号码,而当发送方已在联系人里时,它的值则和Contact.FileAs属性的值相等,所以我们可以考虑把Recipient.Name属性的值和Contact.FileAs属性的值进行比较。据此,我们可以通过如下代码判断发送方是否在联系人里:
代码 4
当应用程序截获一条查询短信息时,它首先要判断发送方是否有权执行查询操作,如果没有,忽略请求,否则,获取查询结果,然后回复发送方,整个过程如下图所示:
图 3
不同的查询操作只有中间那步是不同的,于是,我们不妨考虑在ISmsProcessor接口和PingStatusProcessor类、PingScheduleProcessor类之间添加一个SmsProcessorBase抽象类:
图 4
SmsProcessorBase.Process方法负责定义逻辑框架以及实现IsAuthorized和SendResponse两个方法:
代码 5
剩下的就是处理PingStatusProcessor类、PingScheduleProcessor类和SmsProcessorBase抽象类之间的继承关系了……
不知不觉又到了测试的时候了,首先准备若干联系人:
图 5
接着,通过Cellular Emulator使用如下手机号码发送查询短信息:
- 15933449394
- 13122113344
只有第一个手机号码得到自动回复,第二个已被忽略了:
图 6
谁查询过我的状态/安排?
到目前为止,查询短信息的截获和状态/安排的自动回复都是应用程序悄悄完成的,我们无从得知背后究竟发生了什么事情。人有时候很矛盾,一方面希望事情最好能够自动完成,另一方面又害怕自动化会把控制权夺走。自动化可以解放我们的注意力,但同时也会产生认知空白,从而导致情绪上的焦虑。于是,我们不难想象,故事的发展会产生一个新的需求——告诉我谁查询过我的状态/安排以及应用程序如何处理每个查询请求。不用我说你也知道,这个需求会导致用户界面的改变(下面那个灰色方框是DataGrid控件):
图 7
这些历史纪录将会存储在InterceptionHistory.xml里,这个文件的内容结构如下所示:
代码 6
其中,每个interception元素将会对应一个Interception对象,它的定义如下:
代码 7
历史纪录的管理是由InterceptionHistory类来负责的,因为它的实现方式基本上是照搬StatusTextManager的(包括应用Singleton模式和使用BindingList<T>集合),所以我就不在这里重复整个实现细节了。
那么,我们应该在什么时候添加历史纪录?想想看,什么时候我们能够得到一条纪录所需的全部数据?没错,答案就在SmsProcessorBase.Process方法里:
代码 8
最后,我们只需要把InterceptionHistory.Interceptions属性(类型为BindingList<Interception>)关联到用户界面那个灰色方框就行了:
代码 9
现在,我们再做一次刚才那个测试,不过这此我们把目光投向应用程序的主界面上:
图 8
嗯,很好,不过有个小小的问题,就是Time那列只显示日期,没有时间,我不知道如何控制DataGrid按照特定格式显示DateTime,有人知道怎样做吗?
你还想要什么?
你上次不是说还有个什么白名单吗?怎么我没看到?噢,是的,我没有忘记,因为那个需求将会引出一连串其它需求,比如说,执行某个查询请求需要什么权限,如何指定,发送方拥有什么权限,这些权限又是如何界定的等等。另外,陌生人似乎也比较无奈,因为他/她什么也不能做,是否应该考虑给他/她一个"注册"的机会呢?