WM有约II(四):你明天有空吗?
WM有约II(四):你明天有空吗?
Written by Allen Lee
你明天有空吗?
在上一集结束时,我们发现了一个新的需求——查询Outlook Mobile的计划安排,由于目前的用户界面已经不能满足这个需求了,于是我们需要对其进行一些调整:
图 1
我们使用TabControl分别安置设置当前状态和查询朋友状态两个功能,其中,查询朋友状态的功能集中在Ping Friends选项卡上,如上图所示。当用户选中Now并单击Ping按钮时,应用程序将会发送查询当前状态的短信息;当用户选中Time below,在DateTimePicker上输入一个时间,并单击Ping按钮时,应用程序将会发送查询这个时间的安排的短信息。
我希望只有当用户选中Time below时才能在DateTimePicker上输入时间,所以我把DateTimePicker的Enable属性设为false,并在Time below的CheckedChanged事件触发时进行相关的设置:
代码 1
接着,我们需要修改Ping按钮的代码,添加查询指定时间的安排的代码:
代码 2
现在,我们来试一下发送查询信息的功能,首先,我们分别发送两条查询当前状态和查询今天下午2点半的安排的短信息:
图 2
图 3
接着,我们来看看Cellular Emulator这边的情况,很好,短信息正常发出:
图 4
我明天……
还记得我们是如何定制查询状态的短信息吗?如果你读过《WM有约II(二):持续改进》,应该会记得我们是通过配置文件来添加自定义的查询状态的短信息,现在我们要截获如下所示的短信息:
{Trombone:PingSchedule(1/29/2009 2:30 PM)}
那么我们应该在配置文件里添加如下所示的截获设置:
代码 3
如何处理截获的短信息?一个简单直接的办法是修改OnMessageRecieved方法的代码(参见《WM有约II(三):整合Outlook Mobile的约会信息》的代码8),但这样混合两种不同的逻辑,将来如果需要扩展其它逻辑就会很麻烦,所以我打算尝试一个新的设计,把截获的短信息交给InterceptionProcessor来处理,所有InterceptionProcessor都实现了IInterceptionProcessor接口:
代码 4
接下来,我们要创建两个InterceptionProcessor:PingStatusProcessor和PingScheduleProcessor,它们的Name属性分别返回PingStatus和PingSchedule。PingStatusProcessor的Process方法将会移植OnMessageRecieved方法的代码,那么我们如何在PingStatusProcessor里访问用户界面上当前选中的状态文本呢?办法当然有很多,这里我为StatusTextManager添加一个SelectedStatusText属性,并对它应用Singleton模式,然后在主窗体的ComboBox的SelectedIndexChanged事件触发时把当前选中的状态文本赋给StatusTextManager.SelectedStatusText属性:
代码 5
有了这些准备,我们就可以着手实现PingStatusProcessor.Process方法了:
代码 6
在实现PingScheduleProcessor.Process方法之前,我们需要一个方法协助从截获的短信息中提取时间数据:
代码 7
PingScheduleProcessor.Process方法的代码和代码6类似,不过我们不再需要访问用户界面上当前选中的状态文本,但是我们需要在用户禁用Outlook Mobile的约会信息时回复相应的短信息而不是无声无息地"吞"掉截获的短信息:
代码 8
InterceptionProcessor如何和MessageInterceptor关联?我们可以通过配置文件做到:
代码 9
InterceptionProcessor的初始化是在InterceptionManager的构造函数里完成的:
代码 10
而读取并根据配置信息创建MessageInterceptor的代码需要做相应的修改:
代码 11
因为对象初始化器不支持对事件进行初始化,所以我把配置文件的InterceptionProcessor数据读到一个匿名类的Processor属性里,稍后再做处理:
代码 12
而之前的OnMessageRecieved方法和InterceptionManager. AttachInterceptionHandler方法则可以"退休"了。
好了,是时候测试一下应用程序了,分别通过Cellular Emulator发送以下短信息:
- {Trombone:PingStatus}
- {Trombone:PingSchedule(1/30/2009 10:30 AM)}
结果都顺利截获并成功回复Outlook Mobile的约会信息:
图 5
接着,禁用Outlook Mobile的约会信息:
图 6
然后再用Cellular Emulator发送那两条短信息(此时主窗体的状态文本是"I'm watching TV."),结果也和期望的一样:
图 7
时间限制选项
原本PingSchedule是用来查询将来的安排的,但现在无论用户界面还是处理逻辑都没有限制别人查询过去的历史,于是你可能在想,如果用户不希望别人查询过去的历史,或者用户希望由他/她来决定是否允许别人查询过去的历史呢?
为此,我们需要修改一下设置窗体:
图 8
并在配置文件里添加如下所示的配置项:
<option name="allowHistoryPing" value="false" />
然后在OptionManager和设置窗体里添加对应的代码,由于这些代码和其它配置项的代码(参见《WM有约II(三):整合Outlook Mobile的约会信息》的代码4-7)大同小异,就不在此一一细说了。
为了使配置生效,代码8也需要做相应的修改:
代码 13
至此,你可能会问,如果用户使用PingSchedule查询当前时间的安排呢?你会期望得到一个怎样的结果?回复一条消息,告诉别人应该换用PingStatus,还是自动把查询重定向到PingStatus?我相信没有人关心我们背后如何实现,更不希望你通过短信息告诉他们这些细节,所以我们应该选择后一个策略。那么我们应该在接收端还是发送端进行重定向呢?回顾到目前为止的代码,如果要在接收端实现重定向,定必造成不少冲击,因为目前的设计不支持从一个InterceptionProcessor的内部切换到另一个InterceptionProcessor,所以我们还是考虑从发送端着手吧。如果在发送端实现重定向,我们只需要在用户单击Ping按钮时做一个小小的判断就可以了:
代码 14
因为应用程序的事件只精确到分钟,所以我们只需判断DateTimePicker的事件和当前时间的差是否在1分钟内,如果是,则把查询重定向到PingStatus。
好了,又到测试应用程序的时候了,选中Time below,指定当前时间,然后单击Ping按钮:
图 9
查看Cellular Emulator,我们可以看到查询已被重定向到PingStatus了:
图 10
你还想要什么?
既然可以查询某个时刻的安排了,是否也可以考虑实现查询某个时段的安排呢?比如说,你想知道某人明天下午3点到4点有没有安排。理论上,要实现这样的功能是没有问题的,然而,真有这个必要吗?就算让你知道某人明天下午3点到4点没有安排,也不能保证你可以约到这个人;反过来,即使某人明天下午3点到4点已有安排,也可能因为你的一个电话取消原先的安排。技术可以帮助你也可以束缚你,没有搞清问题的本质就想当然地提出需求很可能会为自己埋下麻烦的种子。
那么,下一集我们玩些什么?嗯,有没有考虑过忽略来自陌生手机号码的查询短信息,或者维护一个白名单,只有名单里的手机号码才能查询将来的安排和过去的历史?