即时通讯软件(IM)因其即时、方便等特性而受到大多数人的青睐并已成为人们使用最频繁的通讯工具之一。在企业应用软件中嵌入即时通讯功能也已成为众多企业的普遍需求。本文将通过具体实例介绍如何实现 Lotus Sametime 的一些常用功能,并将这些功能集成到 Notes 文档的右键菜单中。通过本文的介绍,读者能够了解如何实现 Sametime 的常用功能,并可根据实际需要将 Sametime 提供的各种功能集成到自己的应用软件中。
IBM Lotus Sametime 是业内领先的企业级即时通讯软件和平台。它不仅具备强大的即时通讯,到场提醒和在线会议等功能,而且还提供了丰富的 API 供开发人员对其进行定制和扩展。本文将通过代码片断展示如何利用 Sametime Connect 工具包提供的各种服务和方法实现 Sametime 的常用功能,如搜索联系人、实时交谈、显示联系人名片、添加联系人、跟踪联系人状态变化等,并将这些功能集成到 Lotus Notes 文档的右键菜单中,从而使用户可以方便的与 Notes 文档中出现的任何人名、Email 地址或 Notes ID 进行实时协作。
![]() ![]() |
![]()
|
本文选用 Lotus Notes 8.5 客户机作为我们的开发平台。在 Lotus Notes 8.5 中,Lotus Sametime Connect 8.0.1 客户端已经被缺省集成到 Lotus Notes 客户机的侧栏上。
![]() ![]() |
![]()
|
在介绍具体实现之前,让我们先来了解一下 Sametime 提供的各种工具包。Sametime 工具包主要分为两种:客户机工具包和服务器工具包。
客户机工具包适用于开发基于客户机或浏览器的应用程序,表 1 列出了主要的客户机工具包。
表 1. Lotus Sametime 客户机工具包
客户机工具包 |
Sametime Connect 工具包 |
Sametime Links 工具包 |
Sametime Java 工具包 |
Sametime Helper 工具包 |
Sametime Connect Web API 工具包 |
其中 Sametime Connect、Sametime Helper 和 Sametime Connect Web API 工具包要求运行应用程序的机器上装有 Lotus Sametime Connect 客户机。利用客户机工具包提供的组件和服务,开发人员可以对 Sametime 客户机进行定制和集成,也可以开发自己的即时通讯软件。
服务器工具包适用于与 Lotus Sametime 服务器配套运行的应用程序。注意,这些应用程序无需在 Lotus Sametime 服务器中运行。表 2 列出了主要的服务器工具包。
表 2. Lotus Sametime 服务器工具包
服务器工具包 |
Community Server 工具包 |
Directory and Database Access 工具包 |
Sametime Monitoring and Statistics 工具包 |
Online Meeting 工具包 |
Meeting Room Client JavaScript Extensibility API 工具包 |
Sametime Gateway 工具包 |
服务器工具包提供了扩展 Sametime 服务器的 API。开发人员可以把自己的应用程序以类似于插件的方式插入 Sametime 服务器,例如 Sametime "机器人"程序。除了上述工具包,Sametime 还提供了 Client Telephony APIs 工具包和 Telephony Conferencing Service Provider Interface(TCSPI)工具包用来集成电话会议功能。
表 3 列出了各个工具包具有的特性以及支持的目标环境,开发人员可以根据实际需要选择合适的工具包。
表 3. 各个工具包的特性及目标运行环境
工具包 | 特性 | 目标环境 |
Sametime Connect 工具包 | 构建 Eclipse 插件扩展 Sametime Connect 客户机或与其集成。 | 台式机 |
Sametime Links 工具包 | 利用 Javascript 和 HTML 将 Sametime 的特性添加到 Web 页面。 | Web 浏览器 |
Sametime Java 工具包 | 将 Sametime 的特性集成到 Java 应用程序。 | 台式机 , 服务器 |
Sametime Helper 工具包 | 将 Sametime 的特性集成到微软的 Windows 应用程序中。 | 台式机(Microsoft Windows only) |
Sametime Connect Web API 工具包 | 从 Web 页面触发 Sametime Connect 客户机的功能(状态感知,实时交谈等)。 | 台式机 |
Sametime Gateway 工具包 | 构建插件和事件消费者以扩展 Sametime 本地社区与外部社区(如 AOL Instant Messenger、Yahoo! 和 Google Talk)之间的协作。 | 服务器 |
Community Server 工具包 | 构建 Java 组件添加或扩展 Sametime 服务器的服务 | 服务器 |
Directory and Database Access 工具包 | 为 Sametime 服务器构建 C++ 或 Java 组件,用于提供目录集成,聊天日志或病毒扫描服务。 | 服务器 |
Sametime Monitoring and Statistics 工具包 | 通过 HTTP 协议访问以 XML 格式显示的 Sametime 服务器统计数据 | 台式机 , 服务器 |
Meeting 工具包 | 通过 HTTP 安排和管理在线会议,利用 MRC 集成第三方工具。 | 台式机 , 服务器 |
TCSPI 工具包 | 向 Sametime Connect,Sametime Web conferencing 和 Lotus Notes 提供 click-to-call 电话服务。 | 服务器 |
Lotus Sametime 从 7.5.1 版本开始将它的客户机构建于 Eclipse 平台之上,同时在 Toolkit 中增加了 Sametime Connect 工具包,以帮助开发人员通过构建 Sametime 插件定制或扩展 Sametime 客户机。表 4 列出了 Connect 工具包提供的各种服务及它们所在的插件。
表 4. Sametime Connect 工具包提供的各种服务及它们所在的插件
服务名称 | 功能 | 所在的插件 |
AnnouncementService | 发送公告 / 通知 (announcements/ notifications) | com.ibm.collaboration.realtime.notifications |
BuddyListService | 提供了管理“联系人列表”的各种方法 | com.ibm.collaboration.realtime.imhub |
BusinessCardService | 提供了获取名片 (business card) 的相关方法 | com.ibm.rcp.realtime.livenames |
ChatControllerService | 提供了访问 chat features 和 chat window 相关的服务 | com.ibm.collaboration.realtime.chatwindow |
CommunityService | 提供了访问和管理社区的各种方法 | com.ibm.collaboration.realtime.community |
DirectoryService | 提供了获取目录 (directory) 信息的各种方法 | com.ibm.collaboration.realtime.directory |
GroupService | 提供了管理组 (group) 的各种方法 | com.ibm.collaboration.realtime.imhub |
PeopleService | 提供了管理联系人的各种方法 | com.ibm.collaboration.realtime.people |
LiveNameService | 提供了管理 Live name 的各种方法 | com.ibm.rcp.realtime.livenames |
获取 Connect 工具包中的服务
Connect 工具包提供了 ServiceHub 类(位于 com.ibm.collaboration.realtime.core 插件中)来注册和获取服务。开发人员可以利用 ServiceHub 中的静态方法 getService 获取服务,如下所示:
public static java.lang.Object getService(java.lang.String svcType) throws ServiceException |
其中 svcType 参数指定要获取的服务的名称。服务的名称以静态常量的方式定义在各个服务中,常量的名字统一为 SERVICE_TYPE。例如,通过以下代码获取 CommunityService:
CommunityService communitySvc = (CommunityService) ServiceHub .getService(CommunityService.SERVICE_TYPE); |
![]() ![]() |
![]()
|
接下来通过具体实例展示如何利用 Connect 工具包提供的组件和服务实现 Sametime 的常用功能。表 5 列出了各个功能对应的代码清单。
表 5. 代码清单
Sametime 功能 | 代码清单 |
搜索联系人 | 清单 1、清单 2 |
实时交谈 | 清单 3 |
显示联系人名片 | 清单 4 |
添加联系人 | 清单 5 |
跟踪联系人状态变化 | 清单 6 |
在对选中的人执行具体操作之前,先要解析联系人,即在 Sametime 服务器使用的 LDAP 或 Domino Directory 中查找选中的人名、Email 地址或 Notes ID 对应的用户目录。
清单 1 是搜索联系人的具体实现。
清单 1. 搜索联系人
1 Community defaultCommunity = IMService.getInstance().getDefaultCommunity(); 2 3 RtcSession session = defaultCommunity.getRtcSession(); 4 5 if (session != null) { 6 DirectoryServiceFactory dirSvcFactory = (DirectoryServiceFactory) defaultCommunity 7 .getService(DirectoryServiceFactory.SERVICE_TYPE); 8 DirectoryService service = (DirectoryService) dirSvcFactory 9 .getDirectoryService(session); 10 11 Lookup contactLookup = service.createUserLookup(); 12 contactLookup.addLookupListener(new ContactLookupListener(contactLookup, 13 defaultCommunity, monitor)); 14 contactLookup.setMaxResults(50); 15 contactLookup.lookup(name); 16 } |
搜索联系人需要使用 DirectoryService。DirectoryService 提供了搜索目录 (Directory) 信息的各种方法,如搜索联系人,搜索组,搜索组中的成员等等。清单 1 的第 1-9 行是获取 DirectoryService 的实现,其中第 1 行调用 IMService 的 getDefaultCommunity 方法获得缺省的 Community。IMService 是我们定义的单例类,它在内部封装了获取各个服务的代码,如下所示:
private IMService() { try { peopleSvc = (PeopleService) ServiceHub.getService(PeopleService.SERVICE_TYPE); liveNameSvc = (LiveNameService) ServiceHub .getService(LiveNameService.SERVICE_TYPE); bizCardSvc = (BusinessCardService) ServiceHub .getService(BusinessCardService.SERVICE_TYPE); CommunityService communitySvc = (CommunityService) ServiceHub .getService(CommunityService.SERVICE_TYPE); if (communitySvc != null) { defaultCommunity = communitySvc.getDefaultCommunity(); } } catch (ServiceException e) { logger.log(Level.SEVERE, "Error occurs when construct IMService", e); } } |
清单 1 的第 6 行从缺省的 community 里获取 DirectoryServiceFactory 实例,并用这个实例获得与这个 Community 相关的 DirectoryService。第 11 行调用 DirectoryService 的 createUserLookup 方法创建搜索联系人的 Lookup。Sametime 是一个网络通讯工具,它的绝大多数操作都需要经过网络传输。为了避免客户机长时间的等待,Sametime 采用观察者模式处理客户机的请求,即在客户机向服务器发送请求之前,先创建一个观察者并把它加入到适当的组件中。客户机发出请求后,可以继续执行其他操作。待请求的操作执行完毕,服务器会调用观察者的相关方法通知客户机请求的结果。所以在利用 Lookup 搜索指定联系人之前,先通过 addLookupListener 方法注册 lookup 操作的观察者 lookup listener,如第 12 行所示。第 14 行设置最多可以返回多少匹配的用户引用。最后调用 lookup 方法搜索联系人,如第 15 行所示。搜索的结果会通过 LookupEvent 对象发送给 LookupListener。清单 2 是 LookupListener 的实现。通过 LookupEvent 的 getType 方法我们能够得知搜索的结果。如果搜索成功,getType 方法会返回 LookupEvent. RESOLVE_SUCCESS;如果失败,会返回 LookupEvent.RESOLVE_FAILURE;如果发生冲突,会返回 LookupEvent. RESOLVE_CONFLICT。第 15 行通过 LookupEvent 的 getContactInfos 方法获得所有匹配的用户信息。第 16 行调用 resolvePerson 方法对返回的用户信息进行封装,并将封装后的结果通过 hanlder.execute 方法返回给调用者(Chat/ShowBizCard/AddContactAction)。
清单 2. LookupListener
1 private class ContactLookupListener implements LookupListener { 2 private Lookup lookup = null; 3 private Community community = null; 4 private IProgressMonitor monitor = null; 5 6 public ContactLookupListener(Lookup lookup, Community community, 7 IProgressMonitor monitor) { 8 this.lookup = lookup; 9 this.community = community; 10 this.monitor = monitor; 11 } 12 13 public void handleLookupEvent(LookupEvent event) { 14 if (event.getType() == LookupEvent.RESOLVE_SUCCESS) { 15 ContactInfo[] contactInfo = event.getContactInfos(); 16 resolvePerson(contactInfo, community, monitor); 17 } else { 18 logger.log(Level.FINEST, "Resolve failed!"); //$NON-NLS-1$ 19 closeMonitor(monitor); 20 handler.handleError(); 21 } 22 lookup.removeLookupListener(this); 23 } 24 } 25 26 private void resolvePerson(ContactInfo[] contacts, Community community, 27 IProgressMonitor monitor) { 28 ArrayList result = new ArrayList<PersonWrapper>(); 29 30 PeopleService peopleSvc = IMService.getInstance().getPeopleService(); 31 Person person = null; 32 PersonWrapper personWrapper = null; 33 for (int i = 0; i < contacts.length; i++) { 34 35 if (monitor != null && monitor.isCanceled()) { 36 return; 37 } 38 39 if (contacts[i].getId() != null) { 40 person = peopleSvc.getPerson(contacts[i].getId(), community.getId(), 41 false); 42 personWrapper = new PersonWrapper(person); 43 personWrapper.setId(contacts[i].getId()); 44 personWrapper.setDisplayName(contacts[i].getDisplayName()); 45 personWrapper.setName(contacts[i].getName()); 46 result.add(personWrapper); 47 } 48 } 49 closeMonitor(monitor); 50 handler.execute(result); 51 } |
实时交谈
清单 3 是 ChatAction 的实现。ChatAction 中会先调用上述搜索联系人的代码搜索指定的联系人。当存在多个符合条件的联系人时,第 15-18 行会弹出选择联系人对话框要求用户选择想要交谈的联系人。确定联系人后,第 26 行会从 IMService 中获得 PeopleService,然后利用 PeopleService 的 createConversation(Person person) 方法创建与指定联系人的会话。当联系人不能交谈时,这个方法会自动弹出提示窗口提醒用户无法与当前联系人交谈 , 是否发送邮件,如图 2 所示。
清单 3. 实时交谈
1 public class ChatAction extends AbstractContextAction { 2 3 public void init(IAction action) { 4 errorTitle = Messages.ChatAction_ErrorTitle; 5 } 6 7 public void execute(final ArrayList<PersonWrapper> contacts) { 8 UIJob chatUIJob = new UIJob("Chat UIJob") { //$NON-NLS-1$ 9 public IStatus runInUIThread(IProgressMonitor monitor) { 10 PersonWrapper person = null; 11 if (contacts != null && contacts.size() != 0) { 12 if (contacts.size() == 1) { 13 person = contacts.get(0); 14 } else { 15 SelectPersonDlg dlg = new SelectPersonDlg(null, contacts); 16 if (dlg.open() == Dialog.OK) { 17 person = dlg.getSelectedPerson(); 18 } 19 } 20 } else { 21 MessageDialog.openError(null, errorTitle, 22 Messages.ChatAction_ResolveErrorMsg); 23 } 24 25 if (person != null) { 26 PeopleService peopleService = IMService.getInstance() 27 .getPeopleService(); 28 peopleService.createConversation(person.getPerson()); 29 } 30 return Status.OK_STATUS; 31 } 32 }; 33 chatUIJob.setPriority(Job.INTERACTIVE); 34 chatUIJob.schedule(); 35 } 36} |
图 1. 实时聊天窗口
![图](https://i-blog.csdnimg.cn/blog_migrate/82ed43560cd24d6afd35286045275eb8.png)
图 2. 联系人不能聊天时的提示窗口
![图](https://i-blog.csdnimg.cn/blog_migrate/dca371cdba013fe4b10b718d1297ea13.png)
显示联系人名片(business card)
BusinessCardService 提供了创建名片的相关方法,如下所示:
BusinessCard getBusinessCard(org.eclipse.swt.widgets.Composite parent, int style) |
在指定的 composite 上创建名片。此方法适用于在某个固定位置显示名片。
HoverBusinessCard getHoverBusinessCard(org.eclipse.swt.widgets.Shell parent) |
在指定的 shell 上创建名片。此方法适用于创建悬浮名片(hover business card),即当鼠标悬浮 (hover) 在某处时弹出一个名片。
清单 4 是显示联系人名片的实现。第 26,27 行从 IMService 获取 BusinessCardService,并用 BusinessCardService 创建悬浮名片。第 28 行通过 HoverBusinessCard 的 setTimeBeforeDisplayed 方法设置显示名片的时间延迟,我们设置为 0,即在解析完联系人后立刻显示名片。如果是通过鼠标悬浮的方式显示名片,应设置一定的时间延迟以取得较好的用户体验。第 29,30 行设置名片所代表的联系人信息。第 32 行调用 show 方法在指定的位置显示名片,本例在光标的位置显示名片。
清单 4. 显示联系人 Business Card
1 public class ShowBizCardAction extends AbstractContextAction { 2 3 public void init(IAction action) { 4 errorTitle = Messages.ShowBizCardAction_ErrorTitle; 5 } 6 7 public void execute(final ArrayList<PersonWrapper> contacts) { 8 UIJob showBizCardUIJob = new UIJob("Chat UIJob") { //$NON-NLS-1$ 9 public IStatus runInUIThread(IProgressMonitor monitor) { 10 PersonWrapper personWrapper = null; 11 if (contacts != null && contacts.size() != 0) { 12 if (contacts.size() == 1) { 13 personWrapper = contacts.get(0); 14 } else { 15 SelectPersonDlg dlg = new SelectPersonDlg(null, contacts); 16 if (dlg.open() == Dialog.OK) { 17 personWrapper = dlg.getSelectedPerson(); 18 } 19 } 20 } else { 21 MessageDialog.openError(null, errorTitle, 22 Messages.ShowBizCardAction_ResolveErrorMsg); 23 } 24 25 if (personWrapper != null) { 26 HoverBusinessCard card = IMService.getInstance().getBizCardService() 27 .getHoverBusinessCard(null); 28 card.setTimeBeforeDisplayed(0); 29 card.setLiveName(personWrapper.getPerson()); 30 card.setLookupName(personWrapper.getId()); 31 Point mousePos = Display.getCurrent().getCursorLocation(); 32 card.show(mousePos); 33 } 34 return Status.OK_STATUS; 35 } 36 }; 37 showBizCardUIJob.setPriority(Job.INTERACTIVE); 38 showBizCardUIJob.schedule(); 39 } 40} |
图 3. 显示联系人名片
![图](https://i-blog.csdnimg.cn/blog_migrate/83b62725fc4a5025936c7b3eb391b35b.png)
添加联系人
Connect 工具包在内部封装了添加联系人的操作,调用者只需发送一个 AddContactToBuddyListMessage 消息即可将指定的联系人添加到 Connect 客户机的联系人列表中。清单 5 是将联系人添加到联系人列表的实现。其中第 26 行创建 AddContactToBuddyListMessage 消息,第 28 行设置要添加的联系人的 ID,然后调用 post 方法将消息发送给 Sametime Connect 客户机。Connect 客户机收到这个消息后,会自动打开 Add Sametime Contact 对话框要求用户选择将联系人添加到哪个组里。如图 4 所示。
清单 5. 将联系人添加到联系人列表
1 public class AddContactAction extends AbstractContextAction { 2 3 public void init(IAction action) { 4 errorTitle = Messages.AddContactAction_ErrorTitle; 5 } 6 7 public void execute(final ArrayList<PersonWrapper> contacts) { 8 UIJob addContactUIJob = new UIJob("Add Person to Sametime Contact List UIJob") { 9 public IStatus runInUIThread(IProgressMonitor monitor) { 10 PersonWrapper person = null; 11 if (contacts != null && contacts.size() != 0) { 12 if (contacts.size() == 1) { 13 person = contacts.get(0); 14 } else { 15 SelectPersonDlg dlg = new SelectPersonDlg(null, contacts); 16 if (dlg.open() == Dialog.OK) { 17 person = dlg.getSelectedPerson(); 18 } 19 } 20 } else { 21 MessageDialog.openError(null, errorTitle, 22 Messages.AddContactAction_ResolveErrorMsg); 23 } 24 25 if (person != null) { 26 AddContactToBuddyListMessage addContactMsg = 27 new AddContactToBuddyListMessage(); 28 addContactMsg.setPersonId(person.getPerson().getId()); 29 addContactMsg.post(); 30 } 31 return Status.OK_STATUS; 32 } 33 }; 34 addContactUIJob.setPriority(Job.INTERACTIVE); 35 addContactUIJob.schedule(); 36 } 37} |
图 4. 添加联系人对话框
![图](https://i-blog.csdnimg.cn/blog_migrate/1356574b9148279ff19001914dba5304.png)
跟踪联系人状态变化
当搜索到多个符合条件的联系人时,会弹出选择联系人对话框。这个对话框不仅会列出所有符合条件的联系人,而且会在联系人的旁边显示联系人的状态并在联系人的状态发生变化时更新状态图标,如图 5 所示。
图 5. 联系人状态感知 (Livename awareness)
![图](https://i-blog.csdnimg.cn/blog_migrate/dc9a97cd42bd86ed6992a191756ad5f0.png)
Connect 工具包提供了 LiveNameListener 监听器监听联系人的状态变化。这个接口定义了两个方法:
void resolveFailed(java.lang.String lookupName, java.lang.String communityId) |
当指定的 lookupName 不能被解析成 LiveName 对象时,这个方法会被调用。
void statusChanged(java.lang.String lookupName, LiveName livename) |
当 lookupName 代表的联系人的状态发生改变时,statusChanged 方法会被调用。注意,这个方法被调用的前提是参数 lookupName 指定的联系人已经被 LiveNameResolver 的 resolve 方法成功解析成 LiveName 对象。
清单 6 是跟踪联系人状态变化的实现。其中第 14 至 20 行覆写了 TableViewer 的 doUpdateItem 方法,将对话框中出现的联系人与它们所在的 TableItem 作为键值对存储在 HashMap 对象 liveNameMap 里。这样在需要更新某个联系人的状态时,可以快速找到它所对应的 TableItem。第 33 行从 LiveNameService 获取 LiveNameResolver,并将 34 行创建的 LiveNameListener 注册到这个 resolver 上。第 44-58 行调用 LiveNameResolver 的 resolve 解析 SelectPersonDlg 对话框里的所有用户。注意,LiveNameListener 只能监听那些被成功解析的用户。在某个联系人的状态发生变化时,LiveNameListener 的 statusChanged 方法会被调用,第 114 行通过传入的 lookupName 查询 liveNameMap 找到这个联系人所在的 TableItem,第 117-119 行利用 LiveName 中的状态信息更新 tableItem,然后调用 TableViewer 的 update 方法更新联系人的状态。
清单 6. 跟踪联系人状态变化
1 public class SelectPersonDlg extends Dialog { 2 3 private List<PersonWrapper> personList = null; 4 5 private LiveNameResolver liveNameResolver = null; 6 private LiveNameListener liveNameListener = null; 7 private HashMap<String, TableItem> liveNameMap = null; 8 9 protected Control createDialogArea(Composite parent) { 10 //ignore unrelated code 11 ...... 12 13 viewer = new TableViewer(table) { 14 protected void doUpdateItem(Widget widget, Object element, boolean fullMap) { 15 super.doUpdateItem(widget, element, fullMap); 16 if (widget instanceof TableItem && element instanceof PersonWrapper) { 17 liveNameMap.put(((PersonWrapper) element).getPerson().getContactId(), 18 (TableItem) widget); 19 } 20 } 21 }; 22 viewer.setLabelProvider(new SelectPersonLabelProvider()); 23 viewer.setContentProvider(new SelectPersonContentProvider()); 24 25 hookLiveName(); 26 27 viewer.setInput(personList); 28 29 return parent; 30 } 31 32 private void hookLiveName() { 33 liveNameResolver = IMService.getInstance().getLiveNameSvc() .getLiveNameResolver(); 34 liveNameListener = new SelectPersonLiveNameListener(); 35 liveNameResolver.addLiveNameListener(liveNameListener); 36 table.addDisposeListener(new DisposeListener() { 37 38 public void widgetDisposed(DisposeEvent arg0) { 39 liveNameMap.clear(); 40 liveNameResolver.removeLiveNameListener(liveNameListener); 41 } 42 }); 43 44 Job resolveJob = new Job("Select Person Dialog Resolve Name Job") { 45 46 protected IStatus run(IProgressMonitor arg0) { 47 if (liveNameResolver != null) { 48 String[] lookupNames = new String[personList.size()]; 49 for (int i = 0; i < personList.size(); i++) { 50 lookupNames[i] = personList.get(i).getPerson().getContactId(); 51 } 52 liveNameResolver.resolve(lookupNames); 53 } 54 return Status.OK_STATUS; 55 } 56 }; 57 resolveJob.setSystem(true); 58 resolveJob.schedule(); 59 } 60 61 private class SelectPersonLabelProvider extends LabelProvider implements 62 ITableLabelProvider { 63 public Image getColumnImage(Object obj, int colIndex) { 64 Image retImg = null; 65 if (colIndex == STATUS_COL_IDX && obj instanceof PersonWrapper) { 66 int status = ((PersonWrapper) obj).getStatus(); 67 if (status != Person.STATUS_OFFLINE 68 && status != Person.STATUS_INVALID) { 69 retImg = ((PersonWrapper) obj).getStatusImage(); 70 } 71 } 72 return retImg; 73 } 74 75 public String getColumnText(Object obj, int colIndex) { 76 String retText = null; 77 if (obj instanceof PersonWrapper) { 78 switch (colIndex) { 79 case DISPLAYNAME_COL_IDX: 80 retText = getDisplayName((PersonWrapper) obj); 81 break; 82 case INTERNET_ADDR: 83 retText = ((PersonWrapper) obj).getId(); 84 break; 85 default: 86 break; 87 } 88 } 89 return retText == null ? "" : retText; //$NON-NLS-1$ 90 } 91 92 private String getDisplayName(PersonWrapper personWrapper) { 93 String displayName = null; 94 95 if (personWrapper.getDisplayName() != null) { 96 displayName = personWrapper.getDisplayName(); 97 } else if (personWrapper.getName() != null) { 98 displayName = personWrapper.getName(); 99 } else { 100 displayName = personWrapper.getId(); 101 } 102 return displayName; 103 } 104 } 105 106 private class SelectPersonLiveNameListener implements LiveNameListener { 107 108 public void resolveFailed(String arg0, String arg1) { 109 // Do nothing... 110 } 111 112 public void statusChanged(String lookupName, LiveName liveName) { 113 if (lookupName != null && liveName != null) { 114 TableItem item = liveNameMap.get(lookupName); 115 if (item != null) { 116 final PersonWrapper element = (PersonWrapper) item.getData(); 117 element.setStatus(liveName.getStatus()); 118 element.setStatusImage(liveName.getStatusImage()); 119 element.setStatusMessage(liveName.getStatusMessage()); 120 PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { 121 public void run() { 122 viewer.update(element, null); 123 } 124 }); 125 } 126 } 127 } 128 } 129} |
![]() ![]() |
![]()
|
接下来将实时交谈、显示联系人名片和添加联系人三个 action 添加到 Notes 文档的上下文菜单中。在 plug-in.xml 中添加清单 7 所示的代码可以将 Chat action 添加到 Notes 文档选中文字的右键菜单中。采用相同的方式可以将显示联系人名片和添加联系人 action 添加到选中人名的右键菜单中。注意,这三个 action 是以 objectContribution 的方式 contribute 到 popupMenus 扩展点,其中 objectClass 是 org.eclipse.jface.text.ITextSelection。添加后,选中 Notes 文档中出现的任何人名、email 地址或 Notes ID,点击右键,就可看到如图 6 或图 7 所示的集成效果。
清单 7. 将 Chat action 添加到 Notes 文档的右键菜单中
1 <extension point="org.eclipse.ui.popupMenus"> 2 <objectContribution 3 id="com.ibm.lotus.quickcollaboration.text" 4 objectClass="org.eclipse.jface.text.ITextSelection"> 5 <action 6 class="com.ibm.lotus.quickcollaboration.action.ChatAction" 7 id="com.ibm.lotus.quickcollaboration.action.ChatAction" 8 menubarPath="additions" 9 label="%str.chat.label" 10 enabledFor="1" 11 /> 12 </objectContribution> 13 </extension> |
图 6. 集成效果— Notes 邮件
![图](https://i-blog.csdnimg.cn/blog_migrate/29232654c2c16f46018f9176f16ec06a.png)
图 7. 集成效果— Notes DB 文档(Discussion DB)
![图](https://i-blog.csdnimg.cn/blog_migrate/3d823358089aa7da6dc30bfe519ac4ee.png)
![]() ![]() |
![]()
|
通过本文的介绍,读者应该对 Lotus Sametime 提供的各种工具包有了初步的了解,并能利用 Connect 工具包实现 Sametime 的常用功能。关于 Sametime 工具包的更多介绍,请参考 Sametime SDK。
转载于:https://blog.51cto.com/mcsky/278853