webapp聊天室|h5仿微信版聊天室(多人互动)

h5微互动聊天室|h5多人群聊天室  ,html5开发的多人互动聊天室——手机端仿微信界面聊天室,采用750px + flex全新布局,以及使用rem响应式配合fontsize.js,弹窗则是使用自己开发的wcPop.js插件;编辑器部分由原先的单一表情新增为动图表情,实现了消息、表情发送 | 大图、视频效果预览 | 仿微信红包、打赏等功能。

002360截图20180703094436583.jpg

004360截图20180703094558646.jpg

005360截图20180703094648536.jpg

006360截图20180703094742961.jpg

007360截图20180703094909872.jpg

008360截图20180703095118526.jpg

010360截图20180703095355515.jpg

011360截图20180703095500179.jpg

012360截图20180703095616681.jpg

013360截图20180703095702680.jpg

small-360截图20180707110220369.jpg

 
继续阅读 »
h5微互动聊天室|h5多人群聊天室  ,html5开发的多人互动聊天室——手机端仿微信界面聊天室,采用750px + flex全新布局,以及使用rem响应式配合fontsize.js,弹窗则是使用自己开发的wcPop.js插件;编辑器部分由原先的单一表情新增为动图表情,实现了消息、表情发送 | 大图、视频效果预览 | 仿微信红包、打赏等功能。

002360截图20180703094436583.jpg

004360截图20180703094558646.jpg

005360截图20180703094648536.jpg

006360截图20180703094742961.jpg

007360截图20180703094909872.jpg

008360截图20180703095118526.jpg

010360截图20180703095355515.jpg

011360截图20180703095500179.jpg

012360截图20180703095616681.jpg

013360截图20180703095702680.jpg

small-360截图20180707110220369.jpg

  收起阅读 »

仿微信客户端web版|h5仿wechat聊天

最近开发了一个仿微信客户端web版聊天案例,和微信聊天界面很类似,功能也还不错。

003360截图20180526105830754.jpg

005360截图20180526110017896.jpg

006360截图20180526110433256.jpg

006360截图20180526110523643.jpg

007360截图20180526110617998.jpg

008360截图20180526110706912.jpg

009360截图20180526111003984.jpg

010360截图20180526111118020.jpg


微信图片_20180526112637.jpg

 
继续阅读 »
最近开发了一个仿微信客户端web版聊天案例,和微信聊天界面很类似,功能也还不错。

003360截图20180526105830754.jpg

005360截图20180526110017896.jpg

006360截图20180526110433256.jpg

006360截图20180526110523643.jpg

007360截图20180526110617998.jpg

008360截图20180526110706912.jpg

009360截图20180526111003984.jpg

010360截图20180526111118020.jpg


微信图片_20180526112637.jpg

  收起阅读 »

环信客户互动云v5.37已发布,新增客户中心日志

新增客户中心日志:在客户中心日志页面下,可查看到客户中心的操作日志,目前记录范围为黑名单操作日志。
进入“管理员模式 > 客户中心 > 客户中心日志”页面,可以查看到黑名单操作日志,点击【筛选排序】按钮可进行自定义筛选,点击操作日志记录可显示客户资料、互动详情及黑名单操作详情。

 

[b]
500312548_wx.jpg
[/b]
 客服模式

历史会话和质检页面iframe标签页支持拖动移动左右位置

历史会话和质检记录详情,会话聊窗右侧iframe标签页支持拖动移动左右位置。

进入“客服模式 > 历史会话”页面,打开历史会话记录详情,选中聊窗右侧iframe标签页,待鼠标变成小手状时,左右拖动可移动位置。

“客服模式 > 质量检查 > 质检记录”页面,以上优化适用。

注:管理员模式下,历史会话/质量检查页面记录详情页,同样支持iframe标签页拖动移动左右位置。

历史会话和质检页面会话标签可显示所有层级

历史会话和质检页面记录详情,会话标签可显示所有层级。

进入“客服模式 > 历史会话”页面,打开历史会话记录详情,可看到聊窗会话标签显示效果优化并可显示所有层级。

“客服模式 > 质量检查 > 质检记录”页面,“客服模式 > 搜索”页面,以上优化适用。

注:管理员模式下,历史会话/质检/搜索页面,以上优化适用。

历史会话满意度标签页替换为满意度图标显示

历史会话详情,原满意度标签页取消,右上角增加满意度图标,显示满意度评价信息。

进入“客服模式 > 历史会话”页面,打开历史会话记录详情,可看到聊窗右侧满意度标签页取消显示,右上角增加满意度图标,鼠标点击可弹窗显示满意度评价详情。

“客服模式 > 质量检查 > 质检记录”页面,以上优化适用。

注:管理员模式下,历史会话/质检页面,以上优化适用。

管理员模式

新增【客户中心日志】功能


在客户中心日志页面下,可查看到客户中心的操作日志,目前记录范围为黑名单操作日志。

进入“管理员模式 > 客户中心 > 客户中心日志”页面,可以查看到黑名单操作日志,点击【筛选排序】按钮可进行自定义筛选,点击操作日志记录可显示客户资料、互动详情及黑名单操作详情。

新增自定义业务规则模拟测试功能

自定义业务规则新增模拟测试功能,可通过测试,判断规则是否可用。

进入“管理员模式 > 设置 > 自定义业务规则”页面,可点击 图标对指定规则进行模拟测试。点击 ,弹出规则测试详细参数窗口,设定参数后,可点击测试按钮,进行测试。若测试成功,则说明规则可用。

注:自定义业务规则功能为标准版增值服务、旗舰版基础功能。如需开通,请提供租户ID并联系环信商务经理。

权限管理中客服模式-历史会话增加数据权限

自定义角色权限,客服模式-历史会话增加数据权限。

进入“管理员模式 > 设置 > 权限管理”页面,编辑自定义角色权限(或添加自定义角色),找到客服模式下历史会话权限,可查看到右边新增数据权限,下拉列表可选择“客服/技能组/租户”3个选项,可根据需要进行自定义设置。

【优化】客户中心导航菜单优化

管理员模式下,【客户中心】导航菜单优化。

进入“管理员模式 > 客户中心”页面,可看到客户中心下二级菜单项优化。其中包括:

原“客户中心”一级菜单更改为【客户中心】下“客户信息”二级菜单。
原“设置-客户标签”“设置-客户中心设置”二级菜单 更改为【客户中心】下二级菜单。
新增【客户中心日志】功能,详情可参考新增【客户中心日志】功能章节。

【优化】访客端消息关键字匹配后行为设置

关键字匹配功能下,新增访客消息端行为配置选项。

进入“管理员模式 > 设置 > 关键字匹配”页面,点击关键字过滤(或添加关键字匹配规则),在弹出窗口中,消息来源选择“访客消息”,满足以下条件选择“关键字匹配”,可查看到执行下拉列表新增2项“在坐席端提示消息 / 在坐席和访客端提示消息”;

弹出窗口中,消息来源选择“访客消息”,满足以下条件选择“正则表达式”,可查看到执行下拉列表新增1项“发送事件到告警记录面板”。

注:关键字匹配功能为标准版增值服务、旗舰版基础功能。如需开通,请提供租户ID并联系环信商务经理。

【优化】客户标签数量限制增大至100

优化会话窗口客户标签显示问题,数量限制40增大至100。新建及导入客户标签时,支持最大数量为100。

【优化】机器人自定义菜单操作提示统一交互

优化机器人设置页面,自定义菜单标签页操作的交互提示,与其他交互保持一致。

进入“管理员模式 > 智能机器人 > 机器人设置”页面,打开自定义菜单页签,添加/编辑/删除菜单项时,回车或点击输入框以外的其他区域可保存,并在右上角给出保存反馈提示。

【优化】公共常用语新增及导入字符数限制统一为1000

优化了公共常用语新增及导入字符数限制问题,统一为1000字符。1个字母或1个汉字均算作1个字符。

修复历史会话/待接入页面数据权限问题

“管理员模式 > 设置 > 权限管理”,修复如下问题:
  • 管理员角色-客服模式-待接入,默认数据权限为“技能组”。
  • 坐席角色-客服模式-历史会话,默认数据权限为“客服”。
  • 坐席角色-客服模式-待接入,默认数据权限为“技能组”。


并修复了相关前端数据显示问题。
 
环信客户互动云更新日志:更新日志
环信客户互动云登陆地址:登陆地址
继续阅读 »
新增客户中心日志:在客户中心日志页面下,可查看到客户中心的操作日志,目前记录范围为黑名单操作日志。
进入“管理员模式 > 客户中心 > 客户中心日志”页面,可以查看到黑名单操作日志,点击【筛选排序】按钮可进行自定义筛选,点击操作日志记录可显示客户资料、互动详情及黑名单操作详情。

 

[b]
500312548_wx.jpg
[/b]
 客服模式

历史会话和质检页面iframe标签页支持拖动移动左右位置

历史会话和质检记录详情,会话聊窗右侧iframe标签页支持拖动移动左右位置。

进入“客服模式 > 历史会话”页面,打开历史会话记录详情,选中聊窗右侧iframe标签页,待鼠标变成小手状时,左右拖动可移动位置。

“客服模式 > 质量检查 > 质检记录”页面,以上优化适用。

注:管理员模式下,历史会话/质量检查页面记录详情页,同样支持iframe标签页拖动移动左右位置。

历史会话和质检页面会话标签可显示所有层级

历史会话和质检页面记录详情,会话标签可显示所有层级。

进入“客服模式 > 历史会话”页面,打开历史会话记录详情,可看到聊窗会话标签显示效果优化并可显示所有层级。

“客服模式 > 质量检查 > 质检记录”页面,“客服模式 > 搜索”页面,以上优化适用。

注:管理员模式下,历史会话/质检/搜索页面,以上优化适用。

历史会话满意度标签页替换为满意度图标显示

历史会话详情,原满意度标签页取消,右上角增加满意度图标,显示满意度评价信息。

进入“客服模式 > 历史会话”页面,打开历史会话记录详情,可看到聊窗右侧满意度标签页取消显示,右上角增加满意度图标,鼠标点击可弹窗显示满意度评价详情。

“客服模式 > 质量检查 > 质检记录”页面,以上优化适用。

注:管理员模式下,历史会话/质检页面,以上优化适用。

管理员模式

新增【客户中心日志】功能


在客户中心日志页面下,可查看到客户中心的操作日志,目前记录范围为黑名单操作日志。

进入“管理员模式 > 客户中心 > 客户中心日志”页面,可以查看到黑名单操作日志,点击【筛选排序】按钮可进行自定义筛选,点击操作日志记录可显示客户资料、互动详情及黑名单操作详情。

新增自定义业务规则模拟测试功能

自定义业务规则新增模拟测试功能,可通过测试,判断规则是否可用。

进入“管理员模式 > 设置 > 自定义业务规则”页面,可点击 图标对指定规则进行模拟测试。点击 ,弹出规则测试详细参数窗口,设定参数后,可点击测试按钮,进行测试。若测试成功,则说明规则可用。

注:自定义业务规则功能为标准版增值服务、旗舰版基础功能。如需开通,请提供租户ID并联系环信商务经理。

权限管理中客服模式-历史会话增加数据权限

自定义角色权限,客服模式-历史会话增加数据权限。

进入“管理员模式 > 设置 > 权限管理”页面,编辑自定义角色权限(或添加自定义角色),找到客服模式下历史会话权限,可查看到右边新增数据权限,下拉列表可选择“客服/技能组/租户”3个选项,可根据需要进行自定义设置。

【优化】客户中心导航菜单优化

管理员模式下,【客户中心】导航菜单优化。

进入“管理员模式 > 客户中心”页面,可看到客户中心下二级菜单项优化。其中包括:

原“客户中心”一级菜单更改为【客户中心】下“客户信息”二级菜单。
原“设置-客户标签”“设置-客户中心设置”二级菜单 更改为【客户中心】下二级菜单。
新增【客户中心日志】功能,详情可参考新增【客户中心日志】功能章节。

【优化】访客端消息关键字匹配后行为设置

关键字匹配功能下,新增访客消息端行为配置选项。

进入“管理员模式 > 设置 > 关键字匹配”页面,点击关键字过滤(或添加关键字匹配规则),在弹出窗口中,消息来源选择“访客消息”,满足以下条件选择“关键字匹配”,可查看到执行下拉列表新增2项“在坐席端提示消息 / 在坐席和访客端提示消息”;

弹出窗口中,消息来源选择“访客消息”,满足以下条件选择“正则表达式”,可查看到执行下拉列表新增1项“发送事件到告警记录面板”。

注:关键字匹配功能为标准版增值服务、旗舰版基础功能。如需开通,请提供租户ID并联系环信商务经理。

【优化】客户标签数量限制增大至100

优化会话窗口客户标签显示问题,数量限制40增大至100。新建及导入客户标签时,支持最大数量为100。

【优化】机器人自定义菜单操作提示统一交互

优化机器人设置页面,自定义菜单标签页操作的交互提示,与其他交互保持一致。

进入“管理员模式 > 智能机器人 > 机器人设置”页面,打开自定义菜单页签,添加/编辑/删除菜单项时,回车或点击输入框以外的其他区域可保存,并在右上角给出保存反馈提示。

【优化】公共常用语新增及导入字符数限制统一为1000

优化了公共常用语新增及导入字符数限制问题,统一为1000字符。1个字母或1个汉字均算作1个字符。

修复历史会话/待接入页面数据权限问题

“管理员模式 > 设置 > 权限管理”,修复如下问题:
  • 管理员角色-客服模式-待接入,默认数据权限为“技能组”。
  • 坐席角色-客服模式-历史会话,默认数据权限为“客服”。
  • 坐席角色-客服模式-待接入,默认数据权限为“技能组”。


并修复了相关前端数据显示问题。
 
环信客户互动云更新日志:更新日志
环信客户互动云登陆地址:登陆地址 收起阅读 »

h5仿微信聊天|h5微聊系统


002360截图20170628163414741.jpg


003360截图20170628171647847.jpg


004360截图20170628171705343.jpg


005360截图20170628171829856.jpg


006360截图20170701135250829.jpg


007360截图20170628172154239.jpg


008360截图20180225115155731.jpg


009360截图20180225115737554.jpg


010360截图20180225114747100.jpg


011360截图20180225120528050.jpg


欢迎大家一起来交流学习  QQ:(282310962) WX:(xy190310)
 
继续阅读 »

002360截图20170628163414741.jpg


003360截图20170628171647847.jpg


004360截图20170628171705343.jpg


005360截图20170628171829856.jpg


006360截图20170701135250829.jpg


007360截图20170628172154239.jpg


008360截图20180225115155731.jpg


009360截图20180225115737554.jpg


010360截图20180225114747100.jpg


011360截图20180225120528050.jpg


欢迎大家一起来交流学习  QQ:(282310962) WX:(xy190310)
  收起阅读 »

环信即时通讯云新版本发布-不在群聊天也能参加多人实时音视频会议!

环信即时通讯云V3.4.2已发布,新增消息邀请参加会议接口,被邀请方不在群聊天也能加入多人音视频会议。

  • Android SDK 更新日志


版本 V3.4.2 2018-06-19

新功能:

多人会议增加 joinConferenceWithTicket(); 接口
通过消息邀请参加会议;
Android 8 适配

修复:

修复移动端与chrome 端视频通话时不显示视频画面的bug;
解决极端情况下 Android 通话挂断崩溃的问题

优化:

音视频会议实现新的UI;
优化设备同时有FCM和华为推送时的逻辑,使用EMOption#setUseFCM()接口

EMOption#setUseFCM() 接口可以设置是否启用谷歌推送,无需再通过后台配置。默认为true,即如果用户手机上有google play service 且配置了FCM number则优先使用FCM推送,如果仅有国内用户,建议关闭该选项,详细可参考 demo 实现

请注意:为提供高质量且一致的音视频体验,从3.4.1版本开始,1v1 通话不再与3.1.5及以前版本兼容,请及时升级。
 
  • iOS SDK 更新日志


版本 V3.4.2 2018-06-15

新功能:

通过消息邀请参加其他人参加多人实时音视频会议

修复:

1v1视频通话,iOS端接收视频后,偶尔会不显示视频画面

请注意:为提供高质量且一致的音视频体验,从3.4.1版本开始,1v1 通话不再与3.1.5及以前版本兼容,请及时升级。
 
SDK更新日志:Android SDK更新日志 ios SDK更新日志
最新SDK下载:下载地址
继续阅读 »
环信即时通讯云V3.4.2已发布,新增消息邀请参加会议接口,被邀请方不在群聊天也能加入多人音视频会议。

  • Android SDK 更新日志


版本 V3.4.2 2018-06-19

新功能:

多人会议增加 joinConferenceWithTicket(); 接口
通过消息邀请参加会议;
Android 8 适配

修复:

修复移动端与chrome 端视频通话时不显示视频画面的bug;
解决极端情况下 Android 通话挂断崩溃的问题

优化:

音视频会议实现新的UI;
优化设备同时有FCM和华为推送时的逻辑,使用EMOption#setUseFCM()接口

EMOption#setUseFCM() 接口可以设置是否启用谷歌推送,无需再通过后台配置。默认为true,即如果用户手机上有google play service 且配置了FCM number则优先使用FCM推送,如果仅有国内用户,建议关闭该选项,详细可参考 demo 实现

请注意:为提供高质量且一致的音视频体验,从3.4.1版本开始,1v1 通话不再与3.1.5及以前版本兼容,请及时升级。
 
  • iOS SDK 更新日志


版本 V3.4.2 2018-06-15

新功能:

通过消息邀请参加其他人参加多人实时音视频会议

修复:

1v1视频通话,iOS端接收视频后,偶尔会不显示视频画面

请注意:为提供高质量且一致的音视频体验,从3.4.1版本开始,1v1 通话不再与3.1.5及以前版本兼容,请及时升级。
 
SDK更新日志:Android SDK更新日志 ios SDK更新日志
最新SDK下载:下载地址 收起阅读 »

【新手上路--分享一个小确幸 “解决更换Demo下的AppKey不起作用的问题”】

关于更换AppKey无效的问题  
下面是我更换AppKey的流程步骤:
  1. 利用随机注册的账号登录后,在Demo的【设置】页面进行更改为已注册好生成应用下的AppKey(已在环信管理后台进行注册 该应用,且利用后台的【IM注册用户】创建了用户user  )
  2. 点击"退出登录",杀死改APP进程,重新登录,输入user 及密码 ,提示登录失败,用户名或密码有误 !

  
   很纳闷,明明已经更换了AppKey啊,而且我输入的用户及密码都是对应注册应用下的用户啊,于是我再次利用一开始注册好的账号进行登录,竟然成功!说明我刚才更改为自己的AppKey根本就没起作用!!!    找到问题源之后,我开始留意到[自定义AppKey]的“switch 开关”,我输入完之后,将switch关 了,没错,我竟然傻乎乎的填完自定义AppKey后将switch开关给关了!顿时为自己的智商担忧啊,于是赶紧试了一下填完之后已经保留switch开关已开的状态,然后按照上述的步骤,登录成功了! 
继续阅读 »
关于更换AppKey无效的问题  
下面是我更换AppKey的流程步骤:
  1. 利用随机注册的账号登录后,在Demo的【设置】页面进行更改为已注册好生成应用下的AppKey(已在环信管理后台进行注册 该应用,且利用后台的【IM注册用户】创建了用户user  )
  2. 点击"退出登录",杀死改APP进程,重新登录,输入user 及密码 ,提示登录失败,用户名或密码有误 !

  
   很纳闷,明明已经更换了AppKey啊,而且我输入的用户及密码都是对应注册应用下的用户啊,于是我再次利用一开始注册好的账号进行登录,竟然成功!说明我刚才更改为自己的AppKey根本就没起作用!!!    找到问题源之后,我开始留意到[自定义AppKey]的“switch 开关”,我输入完之后,将switch关 了,没错,我竟然傻乎乎的填完自定义AppKey后将switch开关给关了!顿时为自己的智商担忧啊,于是赶紧试了一下填完之后已经保留switch开关已开的状态,然后按照上述的步骤,登录成功了!  收起阅读 »

环信即时通讯云V3.4.1已发布,更快,更稳定的实时音视频

​环信即时通讯云V3.4.1已发布:优化WiFi切4G时与服务器重连速度,优化1V1视频通话,新增音视频弱网检测提示功能。

500932935_wx.jpg

 iOS SDK 更新日志

版本 V3.4.1 2018-05-16


优化:

优化WiFi切4G时与服务器重连速度
优化实时1对1通话

新功能:

新增音视频弱网检测回调
新增加群时填写验证消息
新增聊天室断线时被踢出聊天室回调

请注意:为提供高质量且一致的音视频体验,从3.4.1版本开始,1v1 通话不再与3.1.5及以前版本兼容,请及时升级。
 
Android SDK 更新日志

版本 V3.4.1 2018-05-11


SDK:

修复安卓中会话消息未读数显示不准确的问题;
修复华为推送自动登录情况下有时不可用的问题;
修复app第一次安装运行,初始化过程有可能报nullpointer的问题;
尝试解决sendDeviceToServer()方法crash的问题;
实现音视频弱网检测提示功能;
实现加群时填写验证消息功能;
提供聊天室断线时被踢出聊天室的提醒;
优化1v1 通话;

EaseUI:

更新消息发送逻辑,用于解决发送消息出现重复,顺序颠倒等问题.

请注意:为提供高质量且一致的音视频体验,从3.4.1版本开始,1v1 通话不再与3.1.5及以前版本兼容,请及时升级。
 
版本历史:Android SDK更新日志  ios SDK更新日志
下载地址:SDK下载​
继续阅读 »
​环信即时通讯云V3.4.1已发布:优化WiFi切4G时与服务器重连速度,优化1V1视频通话,新增音视频弱网检测提示功能。

500932935_wx.jpg

 iOS SDK 更新日志

版本 V3.4.1 2018-05-16


优化:

优化WiFi切4G时与服务器重连速度
优化实时1对1通话

新功能:

新增音视频弱网检测回调
新增加群时填写验证消息
新增聊天室断线时被踢出聊天室回调

请注意:为提供高质量且一致的音视频体验,从3.4.1版本开始,1v1 通话不再与3.1.5及以前版本兼容,请及时升级。
 
Android SDK 更新日志

版本 V3.4.1 2018-05-11


SDK:

修复安卓中会话消息未读数显示不准确的问题;
修复华为推送自动登录情况下有时不可用的问题;
修复app第一次安装运行,初始化过程有可能报nullpointer的问题;
尝试解决sendDeviceToServer()方法crash的问题;
实现音视频弱网检测提示功能;
实现加群时填写验证消息功能;
提供聊天室断线时被踢出聊天室的提醒;
优化1v1 通话;

EaseUI:

更新消息发送逻辑,用于解决发送消息出现重复,顺序颠倒等问题.

请注意:为提供高质量且一致的音视频体验,从3.4.1版本开始,1v1 通话不再与3.1.5及以前版本兼容,请及时升级。
 
版本历史:Android SDK更新日志  ios SDK更新日志
下载地址:SDK下载​ 收起阅读 »

环信客户互动云v5.36已发布:新增历史会话查询方式

   环信客户互动云v5.36已发布,支持根据会话创建时间查询历史会话,方便客户相关数据部门统计历史会话信息。 
 
500604486_banner.jpg



 客服模式

历史会话支持【按会话创建时间】查询

支持根据会话创建时间查询历史会话,方便客户相关数据部门统计历史会话信息。

在历史会话列表页,点击右上角【筛选排序】按钮,弹窗内选择时间段为“会话创建时间”,点击右下角【筛选查询】按钮,即可根据会话创建时间进行筛选。筛选完成后,可在列表页查看相关结果。

历史会话详情资料页工单列表支持点击跳转

历史会话工单列表支持点击跳转,便于快速查询相关工单信息。

在历史会话详情资料页,可查看到当前历史会话相关工单列表,点击工单,可快速打开工单页面,查看工单详情。

注:融合工单功能为标准版/旗舰版增值服务。如需开通,请在“管理员模式 > 工单”页面提交申请,环信商务经理会主动联系您。

会话标签为空时支持保存

当前会话/进行中会话:

当“会话结束时强制添加会话标签”开关关闭时,会话标签支持全部删除,会话标签为空时可以保存;

当“会话结束时强制添加会话标签”开关开启时,点击会话标签,会话标签支持全部删除,为空时可以保存;点击结束会话按钮时,弹出添加会话标签窗口,此时必须至少添加一个会话标签,才可保存并结束会话;

历史会话:

会话标签支持全部删除,为空时可以保存;

会话聊窗与右侧iframe布局支持拖动分配大小

会话模式下(包括会话、历史会话),聊天窗口支持最小宽度340px,iframe窗口支持最小宽度195px,聊天窗口与iframe窗口支持拖动分配大小。

注:管理员模式下,“当前会话 / 历史会话 / 质量检查”会话详情页面支持聊窗与iframe窗口拖动分配大小。

【优化】历史会话质检指标单独标签页显示

“客服模式 > 历史会话“ 页面,会话聊窗右侧工具栏中原质检页签下“查看系统质检指标筛选”相关属性单独为“指标”页签进行显示。 “客服模式 > 质量检查 > 质检记录”页面,以上优化适用。

注:管理员模式下,历史会话与质检页面,以上优化适用。

新增客服结束语设置

新增客服结束语设置,可设置客服手动结束会话时的结束语。

进入“客服模式 > 客服信息”页面,可看到客服问候语下新增结束语设置,可设置客服结束语并可选择是否开启。客服结束语开启,客服手动结束会话时,会向访客自动发送客服结束语,客服无需在结束会话前,进行手动发送,以提高工作效率。

客服手动结束会话时,详细发送规则如下:
  • 客服结束语开启,系统结束语开启,发送客服结束语。
  • 客服结束语关闭,系统结束语开启,发送系统结束语。
  • 客服结束语开启,系统结束语关闭,发送客服结束语。
  • 客服结束语关闭,系统结束语关闭,不发送结束语。


注:管理员模式下,设置管理员客服结束语,管理员手动结束会话时,以上规则适用;其中客服结束语为管理员客服结束语。

管理员模式

【优化】当前会话列表增加渠道及关联显示


在当前会话列表页面,可以查看到会话渠道及关联信息。

进入“管理员模式 > 当前会话”页面,可以在当前会话列表,查看到会话渠道及关联信息。

技能组溢出增加“客服全部隐身/忙碌/离开”规则设置

技能组溢出增加“客服全部隐身/忙碌/离开”规则设置。

进入“管理员模式 > 设置 > 会话分配规则”页面,打开排队规则TAB页,点击右上角【添加规则】按钮,点击“满足以下条件”下拉选项,选择“客服全部隐身 / 客服全部忙碌 / 客服全部离开”状态,即可设置相应状态下的技能组溢出规则。

注:技能组溢出(排队规则)功能为标准版/旗舰版增值服务。如需开通,请提供租户ID并联系环信商务经理。

黑名单列表默认加载全部数据

黑名单列表,默认情况下加载全部数据。

进入“管理员模式 > 客户中心”页面,打开黑名单标签,默认可查看到所有黑名单信息。

企业欢迎语设置文案更新

进入“管理员模式 > 设置 > 系统开关”页面,可看到“企业欢迎语”设置下文案已更新。

更新内容见以下截图:

2_4.jpg


【优化】时间计划-节假日设置日期选择范围更新

节假日设置页面,开始日期及结束日期可选择任意日期。

进入“管理员模式 > 设置 > 时间计划”页面,打开节假日设置页签,可以设置节假日,节假日范围可选择任意日期(结束日期晚于开始日期)。

修复技能组查询问题

支持>1000技能组,技能组数量>1000时,可正常查询并显示。

修复当前会话显示二次修改访客资料问题

二次修改访客资料开启:

集成访客资料中描述信息修改后,打开当前会话,可查看到资料页中访客描述信息为最新访客资料。

修复最大接待数显示问题

管理员修改“管理员”或“自定义角色”最大接待数,相应角色当前会话可实时显示最大接待数。
 
环信客户互动云更新 文档地址
环信客户互动云 登陆地址
继续阅读 »
   环信客户互动云v5.36已发布,支持根据会话创建时间查询历史会话,方便客户相关数据部门统计历史会话信息。 
 
500604486_banner.jpg



 客服模式

历史会话支持【按会话创建时间】查询

支持根据会话创建时间查询历史会话,方便客户相关数据部门统计历史会话信息。

在历史会话列表页,点击右上角【筛选排序】按钮,弹窗内选择时间段为“会话创建时间”,点击右下角【筛选查询】按钮,即可根据会话创建时间进行筛选。筛选完成后,可在列表页查看相关结果。

历史会话详情资料页工单列表支持点击跳转

历史会话工单列表支持点击跳转,便于快速查询相关工单信息。

在历史会话详情资料页,可查看到当前历史会话相关工单列表,点击工单,可快速打开工单页面,查看工单详情。

注:融合工单功能为标准版/旗舰版增值服务。如需开通,请在“管理员模式 > 工单”页面提交申请,环信商务经理会主动联系您。

会话标签为空时支持保存

当前会话/进行中会话:

当“会话结束时强制添加会话标签”开关关闭时,会话标签支持全部删除,会话标签为空时可以保存;

当“会话结束时强制添加会话标签”开关开启时,点击会话标签,会话标签支持全部删除,为空时可以保存;点击结束会话按钮时,弹出添加会话标签窗口,此时必须至少添加一个会话标签,才可保存并结束会话;

历史会话:

会话标签支持全部删除,为空时可以保存;

会话聊窗与右侧iframe布局支持拖动分配大小

会话模式下(包括会话、历史会话),聊天窗口支持最小宽度340px,iframe窗口支持最小宽度195px,聊天窗口与iframe窗口支持拖动分配大小。

注:管理员模式下,“当前会话 / 历史会话 / 质量检查”会话详情页面支持聊窗与iframe窗口拖动分配大小。

【优化】历史会话质检指标单独标签页显示

“客服模式 > 历史会话“ 页面,会话聊窗右侧工具栏中原质检页签下“查看系统质检指标筛选”相关属性单独为“指标”页签进行显示。 “客服模式 > 质量检查 > 质检记录”页面,以上优化适用。

注:管理员模式下,历史会话与质检页面,以上优化适用。

新增客服结束语设置

新增客服结束语设置,可设置客服手动结束会话时的结束语。

进入“客服模式 > 客服信息”页面,可看到客服问候语下新增结束语设置,可设置客服结束语并可选择是否开启。客服结束语开启,客服手动结束会话时,会向访客自动发送客服结束语,客服无需在结束会话前,进行手动发送,以提高工作效率。

客服手动结束会话时,详细发送规则如下:
  • 客服结束语开启,系统结束语开启,发送客服结束语。
  • 客服结束语关闭,系统结束语开启,发送系统结束语。
  • 客服结束语开启,系统结束语关闭,发送客服结束语。
  • 客服结束语关闭,系统结束语关闭,不发送结束语。


注:管理员模式下,设置管理员客服结束语,管理员手动结束会话时,以上规则适用;其中客服结束语为管理员客服结束语。

管理员模式

【优化】当前会话列表增加渠道及关联显示


在当前会话列表页面,可以查看到会话渠道及关联信息。

进入“管理员模式 > 当前会话”页面,可以在当前会话列表,查看到会话渠道及关联信息。

技能组溢出增加“客服全部隐身/忙碌/离开”规则设置

技能组溢出增加“客服全部隐身/忙碌/离开”规则设置。

进入“管理员模式 > 设置 > 会话分配规则”页面,打开排队规则TAB页,点击右上角【添加规则】按钮,点击“满足以下条件”下拉选项,选择“客服全部隐身 / 客服全部忙碌 / 客服全部离开”状态,即可设置相应状态下的技能组溢出规则。

注:技能组溢出(排队规则)功能为标准版/旗舰版增值服务。如需开通,请提供租户ID并联系环信商务经理。

黑名单列表默认加载全部数据

黑名单列表,默认情况下加载全部数据。

进入“管理员模式 > 客户中心”页面,打开黑名单标签,默认可查看到所有黑名单信息。

企业欢迎语设置文案更新

进入“管理员模式 > 设置 > 系统开关”页面,可看到“企业欢迎语”设置下文案已更新。

更新内容见以下截图:

2_4.jpg


【优化】时间计划-节假日设置日期选择范围更新

节假日设置页面,开始日期及结束日期可选择任意日期。

进入“管理员模式 > 设置 > 时间计划”页面,打开节假日设置页签,可以设置节假日,节假日范围可选择任意日期(结束日期晚于开始日期)。

修复技能组查询问题

支持>1000技能组,技能组数量>1000时,可正常查询并显示。

修复当前会话显示二次修改访客资料问题

二次修改访客资料开启:

集成访客资料中描述信息修改后,打开当前会话,可查看到资料页中访客描述信息为最新访客资料。

修复最大接待数显示问题

管理员修改“管理员”或“自定义角色”最大接待数,相应角色当前会话可实时显示最大接待数。
 
环信客户互动云更新 文档地址
环信客户互动云 登陆地址 收起阅读 »

看了这篇文章你也能自己解决90%的开发问题-环信公开课第26期:客户端日志分析

    不管是使用何种编程语言,日志输出几乎无处不在,日志能帮我们追踪问题、 监控状态以及安全审计。作为一名软件开发人员,就必须要学会通过日志分析处理问题。在过去的环信公开课第26期,环信工程师沈冲讲解了环信开发过程中的日志分析处理。

 
公开课回放视频:https://v.qq.com/x/page/i0679s0ueso.html
 
 
 1、环信日志介绍
* 如何开启日志打印:
    * 初始化SDK时设置:options.enableConsoleLog = YES;
* 日志保存位置:
    * iOS:
        * 2.x:沙盒/Library/EaseMobLog
        * 3.x:沙盒/Documents/HyphenateSDK/easemobLog/
    * Android:
        * 2.x:/sdcard/Android/data/(您的包名)/(您的appkey)/log/日期/xxx.html
        * 3.x:/sdcard/Android/data/(包名)/(appkey)/core_log/easemob.log
* 如何导出日志:
    * iOS:
        * development:到沙盒中取出日志文件
        * distribution:将手机连上Xcode,bundleid不更换,run后导出沙盒文件,取出日志文件
    * Android:
        * 到本机存储卡中取出日志
    * 通过SDK接口导出:
        * iOS:[[EMClient sharedClient] getLogFilesPathWithCompletion:^(NSString *aPath, EMError *aError) {}];
        * Android:List<EMDeviceInfo> com.hyphenate.chat.EMClient.getLoggedInDevicesFromServer

2、日志分析
* SDK初始化:EMChatClientImpl::init()
* 登录、自动登录、退出登录
    * EMSessionManager::login():
AutoLogin
begin logout ..
* 断网与重连
    * onNetworkChanged():
EMSessionManager::reconnect()
* 被踢、被禁用
    * 被踢:operation : 2 
禁用:operation : 1
* 单聊和群聊的发送消息、已读回执
    * asyncSendMessage
单聊:chattype : CHAT
群聊:chattype : GROUPCHAT
已读回执:chattype : READ_ACK
消息ID:server_id : 475456018394908684
* 接收消息
    * command : NOTICE
消息ID:id : 475456157712910340
* 删除消息:EMDatabase::removeMessage: 475460335331969036
* 删除会话:EMChatManager::removeConversation: 31750912802818 isRemoveMessages: 1
* 群组相关:
    * 创建群组: mucCreate:: retCode: 200
    * 有用户加入群组: operation : INVITE_ACCEPT, operation : PRESENCE
    * 查看群详情、获取群成员列表、获取群公告、群组免打扰
    * 
收到群邀请、接受群邀请:
收到群邀请:operation : INVITE,
接受群邀请:mucInviteDisposeOperation:: retCode: 200
* 聊天室相关:
    * 加入、离开聊天室: 
        * 加入聊天室:EMChatroomManager::joinChatroom
        * 退出聊天室: mucQuit:: retCode: 200
    * 有用户加入聊天室: operation : PRESENCE
    * 有用户离开聊天室: operation : ABSENCE
看完视频你学会了吗?环信公开课每周三下午三点准时开讲https://ke.qq.com/course/293539?tuin=3441d418
 
继续阅读 »
    不管是使用何种编程语言,日志输出几乎无处不在,日志能帮我们追踪问题、 监控状态以及安全审计。作为一名软件开发人员,就必须要学会通过日志分析处理问题。在过去的环信公开课第26期,环信工程师沈冲讲解了环信开发过程中的日志分析处理。

 
公开课回放视频:https://v.qq.com/x/page/i0679s0ueso.html
 
 
 1、环信日志介绍
* 如何开启日志打印:
    * 初始化SDK时设置:options.enableConsoleLog = YES;
* 日志保存位置:
    * iOS:
        * 2.x:沙盒/Library/EaseMobLog
        * 3.x:沙盒/Documents/HyphenateSDK/easemobLog/
    * Android:
        * 2.x:/sdcard/Android/data/(您的包名)/(您的appkey)/log/日期/xxx.html
        * 3.x:/sdcard/Android/data/(包名)/(appkey)/core_log/easemob.log
* 如何导出日志:
    * iOS:
        * development:到沙盒中取出日志文件
        * distribution:将手机连上Xcode,bundleid不更换,run后导出沙盒文件,取出日志文件
    * Android:
        * 到本机存储卡中取出日志
    * 通过SDK接口导出:
        * iOS:[[EMClient sharedClient] getLogFilesPathWithCompletion:^(NSString *aPath, EMError *aError) {}];
        * Android:List<EMDeviceInfo> com.hyphenate.chat.EMClient.getLoggedInDevicesFromServer

2、日志分析
* SDK初始化:EMChatClientImpl::init()
* 登录、自动登录、退出登录
    * EMSessionManager::login():
AutoLogin
begin logout ..
* 断网与重连
    * onNetworkChanged():
EMSessionManager::reconnect()
* 被踢、被禁用
    * 被踢:operation : 2 
禁用:operation : 1
* 单聊和群聊的发送消息、已读回执
    * asyncSendMessage
单聊:chattype : CHAT
群聊:chattype : GROUPCHAT
已读回执:chattype : READ_ACK
消息ID:server_id : 475456018394908684
* 接收消息
    * command : NOTICE
消息ID:id : 475456157712910340
* 删除消息:EMDatabase::removeMessage: 475460335331969036
* 删除会话:EMChatManager::removeConversation: 31750912802818 isRemoveMessages: 1
* 群组相关:
    * 创建群组: mucCreate:: retCode: 200
    * 有用户加入群组: operation : INVITE_ACCEPT, operation : PRESENCE
    * 查看群详情、获取群成员列表、获取群公告、群组免打扰
    * 
收到群邀请、接受群邀请:
收到群邀请:operation : INVITE,
接受群邀请:mucInviteDisposeOperation:: retCode: 200
* 聊天室相关:
    * 加入、离开聊天室: 
        * 加入聊天室:EMChatroomManager::joinChatroom
        * 退出聊天室: mucQuit:: retCode: 200
    * 有用户加入聊天室: operation : PRESENCE
    * 有用户离开聊天室: operation : ABSENCE
看完视频你学会了吗?环信公开课每周三下午三点准时开讲https://ke.qq.com/course/293539?tuin=3441d418
  收起阅读 »

不是群主的好友,能否申请加入该群呢?

iOS
不是群主的好友,能否申请加入该群呢?
不是群主的好友,能否申请加入该群呢?

【有奖调查】环信T恤文案征集,你来我们就送!

微信图片_20180601155239.jpg

 
环信T恤背面文案征集,欢迎小伙伴留言,7个中文字以内。
 
送送送T恤!
 
文案一经采用,送T恤!并且留言楼层是8的尾号(包含8),恭喜你中奖了,环信T恤包邮到家!
 
中奖规则:
 
同一账号重复留言只记做一次,小伙伴们构思好文案再留言,珍惜抽奖机会。

活动时间2018年6月1日-6月10日,最终解释权归环信所有。
 
中奖名单公布:
  1. 楼层6:Eternally(额外奖励,提建议被采纳)
  2. 楼层8:°﹏D.X.F.VIP 
  3. 楼层18:空谷幽兰
  4. 楼层28:skoxe
  5. 楼层38.咚咚

继续阅读 »
微信图片_20180601155239.jpg

 
环信T恤背面文案征集,欢迎小伙伴留言,7个中文字以内。
 
送送送T恤!
 
文案一经采用,送T恤!并且留言楼层是8的尾号(包含8),恭喜你中奖了,环信T恤包邮到家!
 
中奖规则:
 
同一账号重复留言只记做一次,小伙伴们构思好文案再留言,珍惜抽奖机会。

活动时间2018年6月1日-6月10日,最终解释权归环信所有。
 
中奖名单公布:
  1. 楼层6:Eternally(额外奖励,提建议被采纳)
  2. 楼层8:°﹏D.X.F.VIP 
  3. 楼层18:空谷幽兰
  4. 楼层28:skoxe
  5. 楼层38.咚咚

收起阅读 »

Android收发消息原理,如何集成华为、小米等厂商推送?-环信公开课第25期

Android开发的你是否也有这样疑问,应用在Android手机退到后台太久会收不到消息?第三方推送怎么集成?谷歌推送国内能用吗?

5月30日环信研发工程师张松在环信第25期公开课上分享关于Android收发消息的原理,以及推送通知的实现,详细内容如下。
 

 为什么应用在Android手机上退到后台太久会出现收不到消息的情况?
    当app在后台运行时环信SDK通过一个后台服务保持一条连接环信服务器的长连接,但Android为了解决系统待机性能差的问题,随着Android版本的升级逐渐禁止了app级别的后台服务的运行。所以在一些版本比较高的Android系统上会有接收不到消息的情况。
 
如何解决Android后台收不到消息的问题?
   为了提高消息的到达率,环信SDK增加了对第三方推送服务的支持,包括小米推送、华为推送、Google FCM推送。 (服务端也增加了相应功能)
 
1.Google FCM推送:Firebase Cloud Messaging,用来替代Google的GCM推送服务。
 
FCM推送在国内是无法正常使用的,需要设备上有Google play service和能连接Google服务器的网络。所以该推送服务主要针对海外用户,如果你的app有海外用户,建议你增加对该推送服务的支持。
 
推送消息发送流程:
 

古国.png

环信Server:
1.环信后台配置推送证书
  让环信服务器拥有向你的app发送推送消息的功能
  根据配置的证书名称服务器可以判断设备使用了哪种推送
2.目标设备的推送token
  Android设备通过集成第三方推送SDK可以得到该推送token.
  推送token跟Android设备是一一对应的关系,一个token对应一个Android设备.
  不同推送通道获取到的推送token不同.
3.推送token属于哪个推送通道
  决定了用哪个通道来发送推送消息.
  根据Android设备上传的证书名称确定.
 
Android设备:

1.判断app所在Android设备上支持哪种推送通道
  • 小米推送: 支持小米系统
  • 华为推送: 支持华为系统
  • FCM:需要Google play service和能连接Google服务器的网络

2.集成第三方推送SDK获取推送token
3.将推送token和推送通道名称上传至环信服务器.

 
SDK集成第三方推送
小米推送:
小米推送目前集成在了SDK内部,后续也会放在Demo中实现,让开发者可以自己升级小米推送SDK
1.在app AndroidManifest.xml中添加配置:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.VIBRATE"/>
<permission
android:name="com.hyphenate.chatuidemo.permission.MIPUSH_RECEIVE" android:protectionLevel="signature" />
<!--这里com.hyphenate.chatuidemo改成app的包名-->
<uses-permission android:name="com.hyphenate.chatuidemo.permission.MIPUSH_RECEIVE" />
<!--这里com.hyphenate.chatuidemo改成app的包名-->

<service
android:name="com.xiaomi.mipush.sdk.PushMessageHandler"
android:enabled="true"
android:exported="true"
tools:ignore="ExportedService" />
<service
android:name="com.xiaomi.mipush.sdk.MessageHandleService"
android:enabled="true" />

<receiver
# EMMipushReceiver extends PushMessageReceiver
android:name="com.hyphenate.chat.EMMipushReceiver"
android:exported="true"
tools:ignore="ExportedReceiver">
<intent-filter>
<action android:name="com.xiaomi.mipush.RECEIVE_MESSAGE" />
</intent-filter>
<intent-filter>
<action android:name="com.xiaomi.mipush.MESSAGE_ARRIVED" />
</intent-filter>
<intent-filter>
<action android:name="com.xiaomi.mipush.ERROR" />
</intent-filter>
</receiver>

EMOptions#setMipushConfig(appKey:"2882303761517426801", appSecret:"5381742660801");


SDK处理流程:

根据小米推送SDK提供的方法MiPushClient#shouldUseMIUIPush(Context context)判断当前设备是否支持小米推送
若当前设备支持推送,则调用MiPushClient#registerPush(Context context, appKey, appSecret)获取小米通道推送
 
token
将appKey和token上传至环信服务器,该部分实现位于EMMipushReceiver 中,如果开发者需要自定义小米PushMessageReceiver,请继承自EMMipushReceiver .
 
华为推送
华为推送已放置到demo层实现,方便开发者自己更新华为推送SDK
华为推送SDK做为hmspush 单独module存在
注意修改hmspush module中AndroidManifext.xml文件的appid,两个provider对应authorities
Demo中配置了一个BroadcastReceiver和一个HMSPushHelper
 
FCM推送
增加了compile 'com.google.android.gms:play-services-base:11.4.0',检查当前设备是否支持fcm推送.
设置fcm numberoptions.setFCMNumber("826964054884");
 
 整体工作流程:

2.png

   Android设备手动或自动登录时,判断当前设备支持哪种推送通道,将该通道下获取到的token和证明名称上传至环信服务器,当服务端检测到当前设备不在线,需要向当前设备发送推送消息的时候,根据设备上传的证书名称判断出当前设备使用的哪种推送通道接收推送消息,使用相应推送通道向该设备上传的推送token上发送消息,当前设备即可收到该推送消息.
 
建议:

1.如果你的app有海外用户,建议你在环信SDK上增加对FCM推送的支持
2.建议同时支持小米推送和华为推送,这样在小米和华为设备上都可以接收到推送消息.

 
课程完整回放视频http://v.qq.com/x/page/m0671pnnzjk.html 

 
 
 
 
继续阅读 »
Android开发的你是否也有这样疑问,应用在Android手机退到后台太久会收不到消息?第三方推送怎么集成?谷歌推送国内能用吗?

5月30日环信研发工程师张松在环信第25期公开课上分享关于Android收发消息的原理,以及推送通知的实现,详细内容如下。
 

 为什么应用在Android手机上退到后台太久会出现收不到消息的情况?
    当app在后台运行时环信SDK通过一个后台服务保持一条连接环信服务器的长连接,但Android为了解决系统待机性能差的问题,随着Android版本的升级逐渐禁止了app级别的后台服务的运行。所以在一些版本比较高的Android系统上会有接收不到消息的情况。
 
如何解决Android后台收不到消息的问题?
   为了提高消息的到达率,环信SDK增加了对第三方推送服务的支持,包括小米推送、华为推送、Google FCM推送。 (服务端也增加了相应功能)
 
1.Google FCM推送:Firebase Cloud Messaging,用来替代Google的GCM推送服务。
 
FCM推送在国内是无法正常使用的,需要设备上有Google play service和能连接Google服务器的网络。所以该推送服务主要针对海外用户,如果你的app有海外用户,建议你增加对该推送服务的支持。
 
推送消息发送流程:
 

古国.png

环信Server:
1.环信后台配置推送证书
  让环信服务器拥有向你的app发送推送消息的功能
  根据配置的证书名称服务器可以判断设备使用了哪种推送
2.目标设备的推送token
  Android设备通过集成第三方推送SDK可以得到该推送token.
  推送token跟Android设备是一一对应的关系,一个token对应一个Android设备.
  不同推送通道获取到的推送token不同.
3.推送token属于哪个推送通道
  决定了用哪个通道来发送推送消息.
  根据Android设备上传的证书名称确定.
 
Android设备:

1.判断app所在Android设备上支持哪种推送通道
  • 小米推送: 支持小米系统
  • 华为推送: 支持华为系统
  • FCM:需要Google play service和能连接Google服务器的网络

2.集成第三方推送SDK获取推送token
3.将推送token和推送通道名称上传至环信服务器.

 
SDK集成第三方推送
小米推送:
小米推送目前集成在了SDK内部,后续也会放在Demo中实现,让开发者可以自己升级小米推送SDK
1.在app AndroidManifest.xml中添加配置:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.VIBRATE"/>
<permission
android:name="com.hyphenate.chatuidemo.permission.MIPUSH_RECEIVE" android:protectionLevel="signature" />
<!--这里com.hyphenate.chatuidemo改成app的包名-->
<uses-permission android:name="com.hyphenate.chatuidemo.permission.MIPUSH_RECEIVE" />
<!--这里com.hyphenate.chatuidemo改成app的包名-->

<service
android:name="com.xiaomi.mipush.sdk.PushMessageHandler"
android:enabled="true"
android:exported="true"
tools:ignore="ExportedService" />
<service
android:name="com.xiaomi.mipush.sdk.MessageHandleService"
android:enabled="true" />

<receiver
# EMMipushReceiver extends PushMessageReceiver
android:name="com.hyphenate.chat.EMMipushReceiver"
android:exported="true"
tools:ignore="ExportedReceiver">
<intent-filter>
<action android:name="com.xiaomi.mipush.RECEIVE_MESSAGE" />
</intent-filter>
<intent-filter>
<action android:name="com.xiaomi.mipush.MESSAGE_ARRIVED" />
</intent-filter>
<intent-filter>
<action android:name="com.xiaomi.mipush.ERROR" />
</intent-filter>
</receiver>

EMOptions#setMipushConfig(appKey:"2882303761517426801", appSecret:"5381742660801");


SDK处理流程:

根据小米推送SDK提供的方法MiPushClient#shouldUseMIUIPush(Context context)判断当前设备是否支持小米推送
若当前设备支持推送,则调用MiPushClient#registerPush(Context context, appKey, appSecret)获取小米通道推送
 
token
将appKey和token上传至环信服务器,该部分实现位于EMMipushReceiver 中,如果开发者需要自定义小米PushMessageReceiver,请继承自EMMipushReceiver .
 
华为推送
华为推送已放置到demo层实现,方便开发者自己更新华为推送SDK
华为推送SDK做为hmspush 单独module存在
注意修改hmspush module中AndroidManifext.xml文件的appid,两个provider对应authorities
Demo中配置了一个BroadcastReceiver和一个HMSPushHelper
 
FCM推送
增加了compile 'com.google.android.gms:play-services-base:11.4.0',检查当前设备是否支持fcm推送.
设置fcm numberoptions.setFCMNumber("826964054884");
 
 整体工作流程:

2.png

   Android设备手动或自动登录时,判断当前设备支持哪种推送通道,将该通道下获取到的token和证明名称上传至环信服务器,当服务端检测到当前设备不在线,需要向当前设备发送推送消息的时候,根据设备上传的证书名称判断出当前设备使用的哪种推送通道接收推送消息,使用相应推送通道向该设备上传的推送token上发送消息,当前设备即可收到该推送消息.
 
建议:

1.如果你的app有海外用户,建议你在环信SDK上增加对FCM推送的支持
2.建议同时支持小米推送和华为推送,这样在小米和华为设备上都可以接收到推送消息.

 
课程完整回放视频http://v.qq.com/x/page/m0671pnnzjk.html 

 
 
 
  收起阅读 »

每周三下午3点,环信公开课,开课啦!

InsertPic_(05-08(05-15-10-37-24).jpg

环信公开课第27期-即时通讯IM开发知识点讲解

活动时间】:2018年6月13日(周三) 15:00

活动介绍】 : 【线上直播】IM集成--复习篇

  本期将由环信工程师进行im集成现场辅导课,温故而知新,有问题,你提,我来答! 

报名地址】:https://ke.qq.com/course/293539?tuin=3441d418

直播QQ群】: 560329342 (欢迎进群提问,老师将针对学生提问,进行讲解)

  20余万开发者小伙伴在这里,还有最强王者带你飞,快来加入我们一起学习吧!
  如有疑问,请您及时联系我们!
     客服QQ:2379053675
继续阅读 »
InsertPic_(05-08(05-15-10-37-24).jpg

环信公开课第27期-即时通讯IM开发知识点讲解

活动时间】:2018年6月13日(周三) 15:00

活动介绍】 : 【线上直播】IM集成--复习篇

  本期将由环信工程师进行im集成现场辅导课,温故而知新,有问题,你提,我来答! 

报名地址】:https://ke.qq.com/course/293539?tuin=3441d418

直播QQ群】: 560329342 (欢迎进群提问,老师将针对学生提问,进行讲解)

  20余万开发者小伙伴在这里,还有最强王者带你飞,快来加入我们一起学习吧!
  如有疑问,请您及时联系我们!
     客服QQ:2379053675 收起阅读 »

环信公开课第24期视频回放-IOS本地通知、远程推送的实现

5月23日周三下午3点,环信公开课第24期如期举行。环信IOS工程师沈冲在公开课上讲解了ios本地通知+远程推送。
 
公开课大纲

 
1、本地通知;

online 不推离线推送 offline 进入离线队列,推离线推送;

什么时候能收到离线推送:
App在后台被系统kill(150s),或手动kill,进程被杀死(用户离线),推离线推送

实现本地通知:
单例注册监听:
遵守协议: EMChatManagerDelegate
注册代理监听:[[EMClient sharedClient].chatManager addDelegate:self delegateQueue:nil];
实现接收消息回调:- (void)messagesDidReceive:(NSArray *)aMessages;
本地通知实现示例:- (void)showNotificationWithMessage:(EMMessage *)message;

2、离线推送

development:开发环境(连xcode run) production:生成环境(ipa)

创建推送证书上传管理后台,(证书appid+工程bundleid+上传管理后台传入packagename三者一致);
初始化SDK设置证书名称: options.apnsCertName = @"hxdemo2Dev”;
注册推送权限: _registerRemoteNotification;
绑定devicetoken: [[EMClient sharedClient] bindDeviceToken:deviceToken];
登录成功后,将app杀死,给之前登录的ID发消息,测试APNs推送。

3、rest+客户端 调试;
Postman;
rest调用示例;
客户端log。

4、私有API被拒
com.apple.springboard.lockcomplete
更新SDK_>提审
24期公开课视频回放:点击观看
 
环信公开课每周三下午3点,环信公开课不见不散
 
公开课观看地址:点击观看
继续阅读 »
5月23日周三下午3点,环信公开课第24期如期举行。环信IOS工程师沈冲在公开课上讲解了ios本地通知+远程推送。
 
公开课大纲

 
1、本地通知;

online 不推离线推送 offline 进入离线队列,推离线推送;

什么时候能收到离线推送:
App在后台被系统kill(150s),或手动kill,进程被杀死(用户离线),推离线推送

实现本地通知:
单例注册监听:
遵守协议: EMChatManagerDelegate
注册代理监听:[[EMClient sharedClient].chatManager addDelegate:self delegateQueue:nil];
实现接收消息回调:- (void)messagesDidReceive:(NSArray *)aMessages;
本地通知实现示例:- (void)showNotificationWithMessage:(EMMessage *)message;

2、离线推送

development:开发环境(连xcode run) production:生成环境(ipa)

创建推送证书上传管理后台,(证书appid+工程bundleid+上传管理后台传入packagename三者一致);
初始化SDK设置证书名称: options.apnsCertName = @"hxdemo2Dev”;
注册推送权限: _registerRemoteNotification;
绑定devicetoken: [[EMClient sharedClient] bindDeviceToken:deviceToken];
登录成功后,将app杀死,给之前登录的ID发消息,测试APNs推送。

3、rest+客户端 调试;
Postman;
rest调用示例;
客户端log。

4、私有API被拒
com.apple.springboard.lockcomplete
更新SDK_>提审
24期公开课视频回放:点击观看
 
环信公开课每周三下午3点,环信公开课不见不散
 
公开课观看地址:点击观看 收起阅读 »

环信webim,收发消息,收发图片,收发表情,未读已读消息数量



QQ截图20180525151449.jpg


QQ图片20180525154227.png



大家好,这是根据我们公司的项目需求集成的webim demo,具体效果如上图。
此demo不依赖后台纯属前端demo,且同步聊天记录安卓和ios(只要用户不清楚浏览器缓存),数据全部储存在本地
那么接下来 咱们开始看看代码吧
第一步:
    1、注册环信即时通信云获得appkey,注册账号之后登录环信后台创建应用就可以得到appkey
第二步:
    2.引用本地文件
    <script type='text/javascript' src='webim.config.js'></script>  
    <script type='text/javascript' src='strophe-1.2.8.min.js'></script>  
    <script type='text/javascript' src='websdk-1.4.11.js'></script> 
这三个文件的顺序 不要修改 ,就这么引入
然后把appkey替换成自己的就可以。
第三步:
    1.注册
     var options = {  
             username: userid,  
             password: password,  
             nickname: nickname,  
             appKey: WebIM.config.appkey,  
             success: function () {//注册成功之后回调函数  
                                      
             },  
             error: function () {},  
             apiUrl: WebIM.config.apiURL  
      };  
     conn.registerUser(options);  
    2.登录
    var options = {  
        apiUrl: WebIM.config.apiURL,  
        user: user,  
        pwd: password,  
        appKey: WebIM.config.appkey  
    };  
    conn.open(options);  
    3.创建连接
    var conn = new WebIM.connection({  
    isMultiLoginSessions: WebIM.config.isMultiLoginSessions,  
    https: typeof WebIM.config.https === 'boolean' ? WebIM.config.https : location.protocol === 'https:',  
    url: WebIM.config.xmppURL,  
    heartBeatWait: WebIM.config.heartBeatWait,  
    autoReconnectNumMax: WebIM.config.autoReconnectNumMax,  
    autoReconnectInterval: WebIM.config.autoReconnectInterval,  
    apiUrl: WebIM.config.apiURL,  
    isAutoLogin: true  
});  
conn.listen({  
    onOpened: function ( message ) {},      //连接成功回调  
    onClosed: function ( message ) {},         //连接关闭回调  
    onTextMessage: function ( message ) {//收到文本消息  
        console.log(message);  
        console.log('收到'+message.from+'发送的消息:'+message.data);  
        setTimeout(function(){
              //这一步或许有人会问为什么要加setTimeout,在这里解释一下,是为了同步执行下去
              var str = message.ext.chatIcon;
              if(str.indexOf("http")>=0){
                        str = str.slice(32);
               }
              else{
                        str = message.ext.chatIcon;
               }
              //这一步是因为ios和安卓发送消息时,人物头像连接一个是http开头地址  一个是不带本域名的地址,所有要做判断
             var getUserItem = localStorage.getItem('user_id');//这个user_id 是ios和安卓的conversation_id 这个id是在我们人才详情页面,点击立即沟通时,存储到用户的id
             然后进入聊天界面,然后模拟点击显示与此用户的界面
       
            if(message.from==getUserItem){
                getNowFormatDate();//时间函数
                showChatMessage(timestamp3,str,message.data,message.id);//此函数为展示消息函数
                //这里执行这一步,是判断如果收到的消息是当前用户,就直接显示在本聊天界面
                detailMessage(timestamp3,message.data,message.from,'text','',message.id,message.from,str,'','','has_read','');//此函数为存储消息函数
                //为了显示消息,这里是对所有收到的消息都做了存储,下文会介绍消息函数各个参数。
            }
            else{//收到的消息不是当前用户
                detailMessage(timestamp3,message.data,message.from,'text','',message.id,message.from,str,'','','no_read','');//存储消息
                var w = localStorage[message.from];//获取本地所有存储用户的消息列表
                var getList = JSON.parse(localStorage[message.from]);//转换成json数组
                for(var i = 0; i<getList.length; i++){
                        var reserve = getList[i].message[0].reserve;//此处是为存储的消息设置的已读、未读状态(上文的has_read,no_read)
                         if(reserve=='no_read'){
                                 var listNumber = Number(getList.length)-Number(i);//这是获取的未读消息的数量
                                 getList[0].message[0].number=listNumber;//此处是把未读消息的数量存储到 该用户第一条消息的number里面       
                                $('.top-list li.'+message.from+'').find('span.notice-badge').show();
                                $('.top-list li.'+message.from+'').find('span.notice-badge').text(listNumber);
                                //此处是显示未读消息的数量,该li的class是收到消息用户的id
                                localStorage[message.from] = JSON.stringify(getList);
                                return false;//然后把消息的未读已读状态更改保存回本地         
                         }
                }
            }
        },0)
    },  
    onEmojiMessage: function ( message ) {//收到表情消息  
        console.log('收到'+message.from+'发送的Emoji'+':'+message.data);  
        //缓存数据  
        for(var i=0;i<message.data.length;i++){  
            var img = message.data[i];  
            var string;  
            if (img.type=='txt') {string = string+img.data;}  
            else{string = string+'<img class="emoji" '+'src="'+img.data +'">';}  
        }  
        string = string.replace('undefined','');  
        console.log(string);  
       //此处的方法同收到文本消息,不过需要赋予字符串emoji表情标签(转化字符串为img标签)
      //下面代码需要拿来出,放到你的$(function(){})里面,放到下面只是为了,解释给读者
      WebIM.Emoji = {  
            path: '../images/faces/',  
            map: {  
                '[):]': 'ee_1.png',  
                '[:D]': 'ee_2.png',  
                '[;)]': 'ee_3.png',  
                '[:-o]': 'ee_4.png',  
                '[:p]': 'ee_5.png',  
                '[(H)]': 'ee_6.png',  
                '[:@]': 'ee_7.png',  
                '[:s]': 'ee_8.png',  
                '[:$]': 'ee_9.png',  
                '[:(]': 'ee_10.png',  
                '[:\'(]': 'ee_11.png',  
                '[:|]': 'ee_12.png',  
                '[(a)]': 'ee_13.png',  
                '[8o|]': 'ee_14.png',  
                '[|]': 'ee_15.png',  
                '[+o(]': 'ee_16.png',  
                '[<o)]': 'ee_17.png',  
                '[|-)]': 'ee_18.png',  
                '[*-)]': 'ee_19.png',  
                '[:-#]': 'ee_20.png',  
                '[:-*]': 'ee_21.png',  
                '[^o)]': 'ee_22.png',  
                '[8-)]': 'ee_23.png',  
                '[(|)]': 'ee_24.png',  
                '[(u)]': 'ee_25.png',  
                '[(S)]': 'ee_26.png',  
                '[(*)]': 'ee_27.png',  
                '[(#)]': 'ee_28.png',  
                '[(R)]': 'ee_29.png',  
                '[({)]': 'ee_30.png',  
                '[(})]': 'ee_31.png',  
                '[(k)]': 'ee_32.png',  
                '[(F)]': 'ee_33.png',  
                '[(W)]': 'ee_34.png',  
                '[(D)]': 'ee_35.png'  
            }  
        }; 
    },  
    onPictureMessage: function ( message ) {//收到图片消息  
        console.log(message);  
        console.log('收到'+message.from+'发送的图片'+':'+message.url);  
        getNowFormatDate();//时间函数
        showChatMessage(timestamp3,str,message.data,message.id);//此函数为展示图片消息函数
    },  
    onCmdMessage: function ( message ) {},     //收到命令消息  
    onAudioMessage: function ( message ) {},   //收到音频消息  
    onLocationMessage: function ( message ) {},//收到位置消息  
    onFileMessage: function ( message ) {//收到文件消息  
        console.log(message);  
        console.log('收到'+message.from+'发送的文件'+':'+message.url);  
    },  
    onVideoMessage: function (message) {  },   //收到视频消息  
    onPresence: function ( message ) {},       //处理“广播”或“发布-订阅”消息,如联系人订阅请求、处理群组、聊天室被踢解散等消息  
    onRoster: function ( message ) {},         //处理好友申请  
    onInviteMessage: function ( message ) {},  //处理群组邀请  
    onOnline: function () {},                  //本机网络连接成功  
    onOffline: function () {},                 //本机网络掉线  
    onError: function ( message ) {},          //失败回调  
    onBlacklistUpdate: function (list) {},   //黑名单变动                    
    onReceivedMessage: function(message){},    //收到消息送达客户端回执  
    onDeliveredMessage: function(message){},   //收到消息送达服务器回执  
    onReadMessage: function(message){
        //此处为收到已读消息的回执
         setTimeout(function(){//为了保持同步操作
                  var getLength = $("#recordchat-main li.item-myself").length
                  //此处获取的长度是,当前聊天窗口的长度,且消息是本人发出的,因为现实已读未读只显示己方消息
                  for (var i = 0; i < getLength ; i++) {
                      var getLiName = $('#recordchat-main li.item-myself:eq('+i+')').attr('id');
                       //获取当前我发出消息的id,此id为user_id也就是上文的conversation_id
                      // console.log(getLiName)
                      var getMid = $('#recordchat-main li.item-myself:eq('+i+')').attr('mid');
                      //此处是获取当前我发出消息的mid,与消息回执的mid进行匹配
                      //console.log(getMid)
                      if(message.mid==getMid){
                        $('#recordchat-main li.item-myself:eq('+i+')').find('.no-read').text('已读');
                        //如果收到回执的消息mid等于当前窗口消息列表的mid,把当前的未读状态改变成已读状态
                      }
                      statusRead('已读',message.mid,getLiName,'');//此函数是把每一条的消息mid存储到本地且存储了状态
                  }
                  
          },0)
},        //收到消息已读回执  
    onCreateGroup: function(message){},        //创建群组成功回执(需调用createGroupNew)  
    onMutedMessage: function(message){}        //如果用户在A群组被禁言,在A群发消息会走这个回调并且消息不会传递给群其它成员  
});  

//此处是左侧联系人列表,此方法是应用的layui的流加载,本想着用户多了,会使用layim+环信集成社区聊天模板
        flow.load({
            elem: '.top-list' //流加载容器
            ,mb:10
            ,isAuto: false
            ,isLazyimg: true
            ,done: function(page, next){ //执行下一页的回调
                //数据插入
                $.ajax({
                    url:'',
                    type:'get',
                    data:{page:page,user_type:2},
                    error:function(data){
                        layer.msg("世上难得两全法,您看是不是您的网络问题,如果不是刷新一下试试哦~")
                    },
                    success:function(data){
                        // console.log(data)
                        var obj = eval('('+data+')');
                        var length = obj.result.length;
                        var pages = length/10;
                        var lis = [];
                        for(var i = 0; i < length; i++)
                        {
                            lis.push('<li class='+obj.result[i].conversation_id+' chatId="'+obj.result[i].conversation_id+'" chatIcon="'+obj.result[i].info.user_img+'" add_time="'+obj.result[i].add_time+'" uid="'+obj.result[i].chat_id+'" chatName = "'+obj.result[i].conversation_id+'" career_name = "'+obj.result[i].info.career_name+'" city = "'+obj.result[i].info.city+'" education = "'+obj.result[i].info.education+'" industry_id = "'+obj.result[i].info.industry_id+'" salary_range = "'+obj.result[i].info.salary_range+'" school_name="'+obj.result[i].info.school_name+'" school_status="'+obj.result[i].info.school_status+'" specialty="'+obj.result[i].info.specialty+'" user_age="'+obj.result[i].info.user_age+'" user_sex="'+obj.result[i].info.user_sex+'" user_name="'+obj.result[i].info.user_name+'"><a href="javascript:;" data-url=""><div class="figure"><img src="'+obj.result[i].info.user_img+'"></div><div class="text"><div class="title"><div class="text-clear"><span class="name">'+obj.result[i].info.user_name+'</span><span class="time">'+obj.result[i].add_time+'</span></div><p class="gray"> '+obj.result[i].info.school_name+' | '+obj.result[i].info.career_name+' | '+obj.result[i].info.education+'</p></div><span class="notice-badge" style="display: none;"></span></div></a></li>')
                        }
                        next(lis.join(''),page< pages); //总页数
                        var getUserId = localStorage.getItem("user_id");
                        //模拟点击 当用户直接从人才列表点击进来
                        for (var i = 0; i < $(".top-list li").length; i++) {
                            var getItem = $('.top-list li:eq('+i+')').attr('chatid');
                            // console.log(getItem)此处是conversation_id
                            if(getUserId==getItem){
                                $('.top-list li:eq('+i+')').trigger("click");
                                var height = $("#recordchat-main").height();
                                $("#chat-list").scrollTop(height);
                                //此处代码是项目中有需求,点击左侧联系人的时候,右侧聊天窗口显示最底部的消息
                            } 
                            if(localStorage[getItem]){
                            var chatSen = localStorage[getItem];
                            //console.log(chatSen)然后在加载左侧结束的同时,获取本地存储所有该用户的聊天记录
                            var chatSenGetItem = JSON.parse(localStorage[getItem]);
                            //console.log(chatSenGetItem)转换改聊天记录为json数组
                            var number = chatSenGetItem[0].message[0].number;
                            //console.log(number)此处为读取存储本地未读的消息
                            if(number!=''){
                            $('.top-list li:eq('+i+')').find("span.notice-badge").show();
                            $('.top-list li:eq('+i+')').find("span.notice-badge").text(number);
                            }
                            else{
                            $('.top-list li:eq('+i+')').find("span.notice-badge").hide();
                            $('.top-list li:eq('+i+')').find("span.notice-badge").text(number);
                            }
                            //未读消息的展示
                            }
                        } 
                    }
                });
            }
        })
//此处是左侧联系人列表点击的时候,由于没有后台支撑,所有我把所有的用户信息,全部存储到li里面,此处只是demo测试,正式版本肯定会有接口支持
        $(".top-list").on("click","li",function(){
            //此处点击切换数据代码已裁剪掉,只写本地存储,im相关   
           var chatId = $(this).attr("class") //上文提到过conversation_id为了li的class
            //获取聊天记录
            if(localStorage[chatId]){
            var localContent = JSON.parse(localStorage[chatId]);//点击获取该用户的所有聊天记录
            if(localContent[0].message[0].number!=''){//此number是未读消息的数量
            localContent[0].message[0].number='';//如果未读消息不为空的话,点击该用户,该用户的未读消息数量清空
            localStorage[chatId]=JSON.stringify(localContent);
            //console.log(localContent)
            }
                var localContent = JSON.parse(localStorage[chatId]);
                //console.log(localContent)
                for (var i = 0; i < localContent.length; i++) {
                    var data = localContent[i].message[0].data;
                    var chatId = localContent[i].message[0].chatId;
                    var from = localContent[i].message[0].from;
                    var time = localContent[i].message[0].time;
                    var type = localContent[i].message[0].type;
                    var filename = localContent[i].message[0].filename;
                    var id = localContent[i].message[0].id;
                    var mid = localContent[i].message[0].mid;
                    var chatIcon = localContent[i].message[0].chatIcon;
                    var reserve = localContent[i].message[0].reserve;
                    if(chatIcon==undefined){
                        chatIcon = '/head_img/12064_15087368603690.png';//如果数据库人物头像丢失,显示默认头像
                    }
                    if(localContent[i].message[0].reserve=='no_read'){
                       // localStorage[chatId].message[0].reserve = 'has_read'
                        localContent[i].message[0].reserve='has_read';
                        //console.log(localContent)
                        localContent[0].message[0].number='';
                        localStorage[chatId]=JSON.stringify(localContent);
                        //console.log(localStorage[chatId])  判断该消息的未读已读状态,点击该用户进行转换状态
                    }
                    var chat_status;
                    // console.log(localStorage[chatId])
                    if(localStorage[id]){
                        var localStatusContent = JSON.parse(localStorage[id]);
                        //console.log(localStatusContent)
                        chat_status = localStatusContent[0].status;
                    }
                    else{
                        //console.log(localStorage[id])
                        chat_status = '未读';
                    }
                    if(from=='me'){
                        if(type=='text'){
                            $("#recordchat-main").append('<li class="item-time"><span class="time">'+time+'</span></li><li class="item-myself" id='+id+' mid='+mid+'><div class="no-read">'+chat_status+'</div><div class="text">'+data+'</div></li>');
                        }
                        else if(type=='picture'){
                            $("#recordchat-main").append('<li class="item-time"><span class="time">'+time+'</span></li><li class="selfPrture item-myself" id='+id+' mid='+mid+'><div class="text"><img class="img_url" src='+data+' alt="内容图片" /></div></li>');
                        }
                    }else{
                        if(type=='text'){
                            $("#recordchat-main").append(
                             '<li class="item-time"><span class="time">'+time+'</span></li>'
                            +'<li class="item-friend" id='+mid+'>'
                            +'<div class="figure"><img src="http://beta.app.first-job-1.com'+chatIcon+'" alt="人物头像" /></div>'//<img src="http://beta.app.first-job-1.com'+message.ext.chatIcon+'">
                            +'<div class="text">'+data+'</div>'
                            +'</li>');
                        }
                        else if(type=='picture'){
                            $("#recordchat-main").append(
                             '<li class="item-time"><span class="time">'+time+'</span></li>'
                            +'<li class="item-friend selfPrture" id='+mid+'>'
                            +'<div class="figure"><img src="http://beta.app.first-job-1.com'+chatIcon+'" alt="人物头像" /></div>'
                            +'<div class="text"><img class="img_url" src='+data+' alt="内容图片" /></div>'
                            +'</li>');
                        }
                    }
                }
                //刷新时读取本地消息 展示在聊天窗口
                //此处为jq写法插入数据,正式版本是改成tpl模板渲染,想想用2句代码就可以展示消息 还是蛮激动的。
            }
            else{
                //console.log("暂无聊天记录")
            }
            $(this).find('span.notice-badge').hide();//隐藏数量小红点
        }) 
//发送文本消息函数
        var sendPrivateText = function(msg_content){
            var name = top.$(".figure>a>img").attr("src");//该企业用户头像
            var chatIcon = name.slice(31);//该企业用户头像地址裁剪
            var chatName = top.$(".figure>span").text();//该企业用户名称
            var chatId = $(".chatId").val();//chat-id
            var id = conn.getUniqueId();
            var msg = new WebIM.message('txt', id);
            msg.set({
                msg: msg_content,
                to: chatId,
                roomType: false,
                chatType: 'singleChat', 
                success: function(id, serverMsgId){
                    $(".chat-input").html("");
                    getNowFormatDate();
                    var emojiMessage = WebIM.utils.parseEmoji(msg_content); //表情解析工具
                   showChatMessage(timestamp3,str,message.data,message.id);
                    var recordchat = document.getElementById('chat-list');
                    recordchat.scrollTop = recordchat.scrollHeight;//发送消息时,滚动条出现在底部
                    //暂时插入数据,tpl绑定模板插入渲染
                    $("#btn-send").addClass("disabled");//发送按钮置灰
                    detailMessage(timestamp3,emojiMessage,"me",'text',id,serverMsgId,chatId,'','未读','','','');  
                },
                fail: function(e){
                    //console.log("fail")
                }
            });

            msg.body.chatType = 'singleChat';
            // msg.setGroup('singleChat');  
            msg.body.ext.chatName= chatName;//传递chatName->ios,java
            msg.body.ext.chatIcon= chatIcon;//传递chatIcon->ios,java
            conn.send(msg.body);
        };
//发送图片消息函数
        var sendPrivateFile = function () {
            var name = top.$(".figure>a>img").attr("src");//该企业用户头像
            var chatIcon = name.slice(31);//该企业用户头像地址裁剪
            var chatName = top.$(".figure>span").text();//该企业用户名称
            var chatId = $(".chatId").val();//chat-id
            var id = conn.getUniqueId();                   // 生成本地消息id  
            var msg = new WebIM.message('img', id);        // 创建图片消息  
            var input = document.getElementById('image');  // 选择图片的input   id必填  
            var file = WebIM.utils.getFileUrl(input);      // 将图片转化为二进制文件  
            var allowType = {'jpg': true,'gif': true,'png': true,'bmp': true};  
            var img_url;  
            if (file.filetype.toLowerCase() in allowType) {  
                var option = {  
                    apiUrl: WebIM.config.apiURL,  
                    file: file,  
                    to: chatId,                       // 接收消息对象  
                    roomType: false,  
                    chatType: 'singleChat',  
                    onFileUploadError: function () {      // 消息上传失败  
                        //console.log('图片发送失败!');  
                    },  
                    onFileUploadComplete: function (aa) {   // 消息上传成功  
                        //console.log('onFileUploadComplete');
                        img_url = aa.uri+"/"+aa.entities[0].uuid;  
                    },  
                    success: function (id, serverMsgId) {                // 消息发送成功 
                        //console.log(id)
                        getNowFormatDate() 
                        showChatMessage(timestamp3,str,message.data,message.id);
                        var recordchat = document.getElementById('chat-list');
                        recordchat.scrollTop = recordchat.scrollHeight;
                        detailMessage(timestamp3,img_url,"me",'picture',file.id,serverMsgId,chatId,'','','','',''); 
                        // showMessage();
                    },  
                    flashUpload: WebIM.flashUpload  
                };  
                msg.set(option); 
                msg.body.chatType = 'singleChat'; 
                msg.body.ext.chatName= chatName;//传递chatName->ios,java
                msg.body.ext.chatIcon= chatIcon;//传递chatIcon->ios,java
                conn.send(msg.body);  
            }  
        };
        //时间函数
        function getNowFormatDate(){
          var timestamp1 = new Date().getTime();//获取时间戳此方法准确
          var timestamp2 = new Date(timestamp1);
          timestamp3 = timestamp2.toLocaleDateString().replace(/\//g, "-") + " " + timestamp2.toTimeString().substr(0, 8);
        }
       //展示消息  根据个人项目需求,正式版本会使用tpl模板 
        function showChatMessage(timestamp3,str,data,id){
            $("#recordchat-main").append(
                             '<li class="item-time"><span class="time">'+timestamp3+'</span></li>'
                            +'<li class="item-friend" id='+id+'>'
                            +'<div class="figure"><img src="http://beta.app.first-job-1.com'+str+'"></div>'
                            +'<div class="text">'+data+'</div>'
                            +'</li>');
        }
     //绑定数据模板
        function detailMessage(timestamp3,data,from,type,id,mid,chatId,chatIcon,status,filename,reserve,number){  
            var localContent = new Array();
            if (localStorage[chatId]) {  
                localContent = JSON.parse(localStorage[chatId]);  
            }  
            localContent[localContent.length]= { 'message':[{ 
                'time':timestamp3,  
                'data':data,//数据  
                'from':from,//谁发的  
                'type':type,//文本类型 text,file,picture  
                'id':id,//消息id  
                'mid':mid,
                'chatId':chatId,
                'chatIcon':chatIcon,
                'status':status,//状态
                'filename':filename, //文件名字 
                'reserve':reserve,//已读未读
                'number':number,//未读数量
            }]};  
            localStorage[chatId] = JSON.stringify(localContent);//存储本地; 
            //console.log(JSON.parse(localStorage[chatId]))
            
        }  
       //根据每一条消息的id存储本地 存储未读已读状态
        function statusRead(status,mid,id,reserve){
            var localStatusContent = new Array();
            if(localStatusContent[id]){
                localStatusContent = JSON.parse(localStorage[id]); 

            }
            localStatusContent[localStatusContent.length]={
                'status':status,
                'mid':mid,
                'id':id,
                'reserve':reserve
            };
            localStorage[id] = JSON.stringify(localStatusContent);
            //console.log(localStorage[id])
        }

项目需求只有表情和图片,文件视频,音频其实都是属于文件的一种,若有帮助请赞赏一下吧。

微信图片_20180525164006.jpg


微信图片_20180525164014.png



 
继续阅读 »


QQ截图20180525151449.jpg


QQ图片20180525154227.png



大家好,这是根据我们公司的项目需求集成的webim demo,具体效果如上图。
此demo不依赖后台纯属前端demo,且同步聊天记录安卓和ios(只要用户不清楚浏览器缓存),数据全部储存在本地
那么接下来 咱们开始看看代码吧
第一步:
    1、注册环信即时通信云获得appkey,注册账号之后登录环信后台创建应用就可以得到appkey
第二步:
    2.引用本地文件
    <script type='text/javascript' src='webim.config.js'></script>  
    <script type='text/javascript' src='strophe-1.2.8.min.js'></script>  
    <script type='text/javascript' src='websdk-1.4.11.js'></script> 
这三个文件的顺序 不要修改 ,就这么引入
然后把appkey替换成自己的就可以。
第三步:
    1.注册
     var options = {  
             username: userid,  
             password: password,  
             nickname: nickname,  
             appKey: WebIM.config.appkey,  
             success: function () {//注册成功之后回调函数  
                                      
             },  
             error: function () {},  
             apiUrl: WebIM.config.apiURL  
      };  
     conn.registerUser(options);  
    2.登录
    var options = {  
        apiUrl: WebIM.config.apiURL,  
        user: user,  
        pwd: password,  
        appKey: WebIM.config.appkey  
    };  
    conn.open(options);  
    3.创建连接
    var conn = new WebIM.connection({  
    isMultiLoginSessions: WebIM.config.isMultiLoginSessions,  
    https: typeof WebIM.config.https === 'boolean' ? WebIM.config.https : location.protocol === 'https:',  
    url: WebIM.config.xmppURL,  
    heartBeatWait: WebIM.config.heartBeatWait,  
    autoReconnectNumMax: WebIM.config.autoReconnectNumMax,  
    autoReconnectInterval: WebIM.config.autoReconnectInterval,  
    apiUrl: WebIM.config.apiURL,  
    isAutoLogin: true  
});  
conn.listen({  
    onOpened: function ( message ) {},      //连接成功回调  
    onClosed: function ( message ) {},         //连接关闭回调  
    onTextMessage: function ( message ) {//收到文本消息  
        console.log(message);  
        console.log('收到'+message.from+'发送的消息:'+message.data);  
        setTimeout(function(){
              //这一步或许有人会问为什么要加setTimeout,在这里解释一下,是为了同步执行下去
              var str = message.ext.chatIcon;
              if(str.indexOf("http")>=0){
                        str = str.slice(32);
               }
              else{
                        str = message.ext.chatIcon;
               }
              //这一步是因为ios和安卓发送消息时,人物头像连接一个是http开头地址  一个是不带本域名的地址,所有要做判断
             var getUserItem = localStorage.getItem('user_id');//这个user_id 是ios和安卓的conversation_id 这个id是在我们人才详情页面,点击立即沟通时,存储到用户的id
             然后进入聊天界面,然后模拟点击显示与此用户的界面
       
            if(message.from==getUserItem){
                getNowFormatDate();//时间函数
                showChatMessage(timestamp3,str,message.data,message.id);//此函数为展示消息函数
                //这里执行这一步,是判断如果收到的消息是当前用户,就直接显示在本聊天界面
                detailMessage(timestamp3,message.data,message.from,'text','',message.id,message.from,str,'','','has_read','');//此函数为存储消息函数
                //为了显示消息,这里是对所有收到的消息都做了存储,下文会介绍消息函数各个参数。
            }
            else{//收到的消息不是当前用户
                detailMessage(timestamp3,message.data,message.from,'text','',message.id,message.from,str,'','','no_read','');//存储消息
                var w = localStorage[message.from];//获取本地所有存储用户的消息列表
                var getList = JSON.parse(localStorage[message.from]);//转换成json数组
                for(var i = 0; i<getList.length; i++){
                        var reserve = getList[i].message[0].reserve;//此处是为存储的消息设置的已读、未读状态(上文的has_read,no_read)
                         if(reserve=='no_read'){
                                 var listNumber = Number(getList.length)-Number(i);//这是获取的未读消息的数量
                                 getList[0].message[0].number=listNumber;//此处是把未读消息的数量存储到 该用户第一条消息的number里面       
                                $('.top-list li.'+message.from+'').find('span.notice-badge').show();
                                $('.top-list li.'+message.from+'').find('span.notice-badge').text(listNumber);
                                //此处是显示未读消息的数量,该li的class是收到消息用户的id
                                localStorage[message.from] = JSON.stringify(getList);
                                return false;//然后把消息的未读已读状态更改保存回本地         
                         }
                }
            }
        },0)
    },  
    onEmojiMessage: function ( message ) {//收到表情消息  
        console.log('收到'+message.from+'发送的Emoji'+':'+message.data);  
        //缓存数据  
        for(var i=0;i<message.data.length;i++){  
            var img = message.data[i];  
            var string;  
            if (img.type=='txt') {string = string+img.data;}  
            else{string = string+'<img class="emoji" '+'src="'+img.data +'">';}  
        }  
        string = string.replace('undefined','');  
        console.log(string);  
       //此处的方法同收到文本消息,不过需要赋予字符串emoji表情标签(转化字符串为img标签)
      //下面代码需要拿来出,放到你的$(function(){})里面,放到下面只是为了,解释给读者
      WebIM.Emoji = {  
            path: '../images/faces/',  
            map: {  
                '[):]': 'ee_1.png',  
                '[:D]': 'ee_2.png',  
                '[;)]': 'ee_3.png',  
                '[:-o]': 'ee_4.png',  
                '[:p]': 'ee_5.png',  
                '[(H)]': 'ee_6.png',  
                '[:@]': 'ee_7.png',  
                '[:s]': 'ee_8.png',  
                '[:$]': 'ee_9.png',  
                '[:(]': 'ee_10.png',  
                '[:\'(]': 'ee_11.png',  
                '[:|]': 'ee_12.png',  
                '[(a)]': 'ee_13.png',  
                '[8o|]': 'ee_14.png',  
                '[|]': 'ee_15.png',  
                '[+o(]': 'ee_16.png',  
                '[<o)]': 'ee_17.png',  
                '[|-)]': 'ee_18.png',  
                '[*-)]': 'ee_19.png',  
                '[:-#]': 'ee_20.png',  
                '[:-*]': 'ee_21.png',  
                '[^o)]': 'ee_22.png',  
                '[8-)]': 'ee_23.png',  
                '[(|)]': 'ee_24.png',  
                '[(u)]': 'ee_25.png',  
                '[(S)]': 'ee_26.png',  
                '[(*)]': 'ee_27.png',  
                '[(#)]': 'ee_28.png',  
                '[(R)]': 'ee_29.png',  
                '[({)]': 'ee_30.png',  
                '[(})]': 'ee_31.png',  
                '[(k)]': 'ee_32.png',  
                '[(F)]': 'ee_33.png',  
                '[(W)]': 'ee_34.png',  
                '[(D)]': 'ee_35.png'  
            }  
        }; 
    },  
    onPictureMessage: function ( message ) {//收到图片消息  
        console.log(message);  
        console.log('收到'+message.from+'发送的图片'+':'+message.url);  
        getNowFormatDate();//时间函数
        showChatMessage(timestamp3,str,message.data,message.id);//此函数为展示图片消息函数
    },  
    onCmdMessage: function ( message ) {},     //收到命令消息  
    onAudioMessage: function ( message ) {},   //收到音频消息  
    onLocationMessage: function ( message ) {},//收到位置消息  
    onFileMessage: function ( message ) {//收到文件消息  
        console.log(message);  
        console.log('收到'+message.from+'发送的文件'+':'+message.url);  
    },  
    onVideoMessage: function (message) {  },   //收到视频消息  
    onPresence: function ( message ) {},       //处理“广播”或“发布-订阅”消息,如联系人订阅请求、处理群组、聊天室被踢解散等消息  
    onRoster: function ( message ) {},         //处理好友申请  
    onInviteMessage: function ( message ) {},  //处理群组邀请  
    onOnline: function () {},                  //本机网络连接成功  
    onOffline: function () {},                 //本机网络掉线  
    onError: function ( message ) {},          //失败回调  
    onBlacklistUpdate: function (list) {},   //黑名单变动                    
    onReceivedMessage: function(message){},    //收到消息送达客户端回执  
    onDeliveredMessage: function(message){},   //收到消息送达服务器回执  
    onReadMessage: function(message){
        //此处为收到已读消息的回执
         setTimeout(function(){//为了保持同步操作
                  var getLength = $("#recordchat-main li.item-myself").length
                  //此处获取的长度是,当前聊天窗口的长度,且消息是本人发出的,因为现实已读未读只显示己方消息
                  for (var i = 0; i < getLength ; i++) {
                      var getLiName = $('#recordchat-main li.item-myself:eq('+i+')').attr('id');
                       //获取当前我发出消息的id,此id为user_id也就是上文的conversation_id
                      // console.log(getLiName)
                      var getMid = $('#recordchat-main li.item-myself:eq('+i+')').attr('mid');
                      //此处是获取当前我发出消息的mid,与消息回执的mid进行匹配
                      //console.log(getMid)
                      if(message.mid==getMid){
                        $('#recordchat-main li.item-myself:eq('+i+')').find('.no-read').text('已读');
                        //如果收到回执的消息mid等于当前窗口消息列表的mid,把当前的未读状态改变成已读状态
                      }
                      statusRead('已读',message.mid,getLiName,'');//此函数是把每一条的消息mid存储到本地且存储了状态
                  }
                  
          },0)
},        //收到消息已读回执  
    onCreateGroup: function(message){},        //创建群组成功回执(需调用createGroupNew)  
    onMutedMessage: function(message){}        //如果用户在A群组被禁言,在A群发消息会走这个回调并且消息不会传递给群其它成员  
});  

//此处是左侧联系人列表,此方法是应用的layui的流加载,本想着用户多了,会使用layim+环信集成社区聊天模板
        flow.load({
            elem: '.top-list' //流加载容器
            ,mb:10
            ,isAuto: false
            ,isLazyimg: true
            ,done: function(page, next){ //执行下一页的回调
                //数据插入
                $.ajax({
                    url:'',
                    type:'get',
                    data:{page:page,user_type:2},
                    error:function(data){
                        layer.msg("世上难得两全法,您看是不是您的网络问题,如果不是刷新一下试试哦~")
                    },
                    success:function(data){
                        // console.log(data)
                        var obj = eval('('+data+')');
                        var length = obj.result.length;
                        var pages = length/10;
                        var lis = [];
                        for(var i = 0; i < length; i++)
                        {
                            lis.push('<li class='+obj.result[i].conversation_id+' chatId="'+obj.result[i].conversation_id+'" chatIcon="'+obj.result[i].info.user_img+'" add_time="'+obj.result[i].add_time+'" uid="'+obj.result[i].chat_id+'" chatName = "'+obj.result[i].conversation_id+'" career_name = "'+obj.result[i].info.career_name+'" city = "'+obj.result[i].info.city+'" education = "'+obj.result[i].info.education+'" industry_id = "'+obj.result[i].info.industry_id+'" salary_range = "'+obj.result[i].info.salary_range+'" school_name="'+obj.result[i].info.school_name+'" school_status="'+obj.result[i].info.school_status+'" specialty="'+obj.result[i].info.specialty+'" user_age="'+obj.result[i].info.user_age+'" user_sex="'+obj.result[i].info.user_sex+'" user_name="'+obj.result[i].info.user_name+'"><a href="javascript:;" data-url=""><div class="figure"><img src="'+obj.result[i].info.user_img+'"></div><div class="text"><div class="title"><div class="text-clear"><span class="name">'+obj.result[i].info.user_name+'</span><span class="time">'+obj.result[i].add_time+'</span></div><p class="gray"> '+obj.result[i].info.school_name+' | '+obj.result[i].info.career_name+' | '+obj.result[i].info.education+'</p></div><span class="notice-badge" style="display: none;"></span></div></a></li>')
                        }
                        next(lis.join(''),page< pages); //总页数
                        var getUserId = localStorage.getItem("user_id");
                        //模拟点击 当用户直接从人才列表点击进来
                        for (var i = 0; i < $(".top-list li").length; i++) {
                            var getItem = $('.top-list li:eq('+i+')').attr('chatid');
                            // console.log(getItem)此处是conversation_id
                            if(getUserId==getItem){
                                $('.top-list li:eq('+i+')').trigger("click");
                                var height = $("#recordchat-main").height();
                                $("#chat-list").scrollTop(height);
                                //此处代码是项目中有需求,点击左侧联系人的时候,右侧聊天窗口显示最底部的消息
                            } 
                            if(localStorage[getItem]){
                            var chatSen = localStorage[getItem];
                            //console.log(chatSen)然后在加载左侧结束的同时,获取本地存储所有该用户的聊天记录
                            var chatSenGetItem = JSON.parse(localStorage[getItem]);
                            //console.log(chatSenGetItem)转换改聊天记录为json数组
                            var number = chatSenGetItem[0].message[0].number;
                            //console.log(number)此处为读取存储本地未读的消息
                            if(number!=''){
                            $('.top-list li:eq('+i+')').find("span.notice-badge").show();
                            $('.top-list li:eq('+i+')').find("span.notice-badge").text(number);
                            }
                            else{
                            $('.top-list li:eq('+i+')').find("span.notice-badge").hide();
                            $('.top-list li:eq('+i+')').find("span.notice-badge").text(number);
                            }
                            //未读消息的展示
                            }
                        } 
                    }
                });
            }
        })
//此处是左侧联系人列表点击的时候,由于没有后台支撑,所有我把所有的用户信息,全部存储到li里面,此处只是demo测试,正式版本肯定会有接口支持
        $(".top-list").on("click","li",function(){
            //此处点击切换数据代码已裁剪掉,只写本地存储,im相关   
           var chatId = $(this).attr("class") //上文提到过conversation_id为了li的class
            //获取聊天记录
            if(localStorage[chatId]){
            var localContent = JSON.parse(localStorage[chatId]);//点击获取该用户的所有聊天记录
            if(localContent[0].message[0].number!=''){//此number是未读消息的数量
            localContent[0].message[0].number='';//如果未读消息不为空的话,点击该用户,该用户的未读消息数量清空
            localStorage[chatId]=JSON.stringify(localContent);
            //console.log(localContent)
            }
                var localContent = JSON.parse(localStorage[chatId]);
                //console.log(localContent)
                for (var i = 0; i < localContent.length; i++) {
                    var data = localContent[i].message[0].data;
                    var chatId = localContent[i].message[0].chatId;
                    var from = localContent[i].message[0].from;
                    var time = localContent[i].message[0].time;
                    var type = localContent[i].message[0].type;
                    var filename = localContent[i].message[0].filename;
                    var id = localContent[i].message[0].id;
                    var mid = localContent[i].message[0].mid;
                    var chatIcon = localContent[i].message[0].chatIcon;
                    var reserve = localContent[i].message[0].reserve;
                    if(chatIcon==undefined){
                        chatIcon = '/head_img/12064_15087368603690.png';//如果数据库人物头像丢失,显示默认头像
                    }
                    if(localContent[i].message[0].reserve=='no_read'){
                       // localStorage[chatId].message[0].reserve = 'has_read'
                        localContent[i].message[0].reserve='has_read';
                        //console.log(localContent)
                        localContent[0].message[0].number='';
                        localStorage[chatId]=JSON.stringify(localContent);
                        //console.log(localStorage[chatId])  判断该消息的未读已读状态,点击该用户进行转换状态
                    }
                    var chat_status;
                    // console.log(localStorage[chatId])
                    if(localStorage[id]){
                        var localStatusContent = JSON.parse(localStorage[id]);
                        //console.log(localStatusContent)
                        chat_status = localStatusContent[0].status;
                    }
                    else{
                        //console.log(localStorage[id])
                        chat_status = '未读';
                    }
                    if(from=='me'){
                        if(type=='text'){
                            $("#recordchat-main").append('<li class="item-time"><span class="time">'+time+'</span></li><li class="item-myself" id='+id+' mid='+mid+'><div class="no-read">'+chat_status+'</div><div class="text">'+data+'</div></li>');
                        }
                        else if(type=='picture'){
                            $("#recordchat-main").append('<li class="item-time"><span class="time">'+time+'</span></li><li class="selfPrture item-myself" id='+id+' mid='+mid+'><div class="text"><img class="img_url" src='+data+' alt="内容图片" /></div></li>');
                        }
                    }else{
                        if(type=='text'){
                            $("#recordchat-main").append(
                             '<li class="item-time"><span class="time">'+time+'</span></li>'
                            +'<li class="item-friend" id='+mid+'>'
                            +'<div class="figure"><img src="http://beta.app.first-job-1.com'+chatIcon+'" alt="人物头像" /></div>'//<img src="http://beta.app.first-job-1.com'+message.ext.chatIcon+'">
                            +'<div class="text">'+data+'</div>'
                            +'</li>');
                        }
                        else if(type=='picture'){
                            $("#recordchat-main").append(
                             '<li class="item-time"><span class="time">'+time+'</span></li>'
                            +'<li class="item-friend selfPrture" id='+mid+'>'
                            +'<div class="figure"><img src="http://beta.app.first-job-1.com'+chatIcon+'" alt="人物头像" /></div>'
                            +'<div class="text"><img class="img_url" src='+data+' alt="内容图片" /></div>'
                            +'</li>');
                        }
                    }
                }
                //刷新时读取本地消息 展示在聊天窗口
                //此处为jq写法插入数据,正式版本是改成tpl模板渲染,想想用2句代码就可以展示消息 还是蛮激动的。
            }
            else{
                //console.log("暂无聊天记录")
            }
            $(this).find('span.notice-badge').hide();//隐藏数量小红点
        }) 
//发送文本消息函数
        var sendPrivateText = function(msg_content){
            var name = top.$(".figure>a>img").attr("src");//该企业用户头像
            var chatIcon = name.slice(31);//该企业用户头像地址裁剪
            var chatName = top.$(".figure>span").text();//该企业用户名称
            var chatId = $(".chatId").val();//chat-id
            var id = conn.getUniqueId();
            var msg = new WebIM.message('txt', id);
            msg.set({
                msg: msg_content,
                to: chatId,
                roomType: false,
                chatType: 'singleChat', 
                success: function(id, serverMsgId){
                    $(".chat-input").html("");
                    getNowFormatDate();
                    var emojiMessage = WebIM.utils.parseEmoji(msg_content); //表情解析工具
                   showChatMessage(timestamp3,str,message.data,message.id);
                    var recordchat = document.getElementById('chat-list');
                    recordchat.scrollTop = recordchat.scrollHeight;//发送消息时,滚动条出现在底部
                    //暂时插入数据,tpl绑定模板插入渲染
                    $("#btn-send").addClass("disabled");//发送按钮置灰
                    detailMessage(timestamp3,emojiMessage,"me",'text',id,serverMsgId,chatId,'','未读','','','');  
                },
                fail: function(e){
                    //console.log("fail")
                }
            });

            msg.body.chatType = 'singleChat';
            // msg.setGroup('singleChat');  
            msg.body.ext.chatName= chatName;//传递chatName->ios,java
            msg.body.ext.chatIcon= chatIcon;//传递chatIcon->ios,java
            conn.send(msg.body);
        };
//发送图片消息函数
        var sendPrivateFile = function () {
            var name = top.$(".figure>a>img").attr("src");//该企业用户头像
            var chatIcon = name.slice(31);//该企业用户头像地址裁剪
            var chatName = top.$(".figure>span").text();//该企业用户名称
            var chatId = $(".chatId").val();//chat-id
            var id = conn.getUniqueId();                   // 生成本地消息id  
            var msg = new WebIM.message('img', id);        // 创建图片消息  
            var input = document.getElementById('image');  // 选择图片的input   id必填  
            var file = WebIM.utils.getFileUrl(input);      // 将图片转化为二进制文件  
            var allowType = {'jpg': true,'gif': true,'png': true,'bmp': true};  
            var img_url;  
            if (file.filetype.toLowerCase() in allowType) {  
                var option = {  
                    apiUrl: WebIM.config.apiURL,  
                    file: file,  
                    to: chatId,                       // 接收消息对象  
                    roomType: false,  
                    chatType: 'singleChat',  
                    onFileUploadError: function () {      // 消息上传失败  
                        //console.log('图片发送失败!');  
                    },  
                    onFileUploadComplete: function (aa) {   // 消息上传成功  
                        //console.log('onFileUploadComplete');
                        img_url = aa.uri+"/"+aa.entities[0].uuid;  
                    },  
                    success: function (id, serverMsgId) {                // 消息发送成功 
                        //console.log(id)
                        getNowFormatDate() 
                        showChatMessage(timestamp3,str,message.data,message.id);
                        var recordchat = document.getElementById('chat-list');
                        recordchat.scrollTop = recordchat.scrollHeight;
                        detailMessage(timestamp3,img_url,"me",'picture',file.id,serverMsgId,chatId,'','','','',''); 
                        // showMessage();
                    },  
                    flashUpload: WebIM.flashUpload  
                };  
                msg.set(option); 
                msg.body.chatType = 'singleChat'; 
                msg.body.ext.chatName= chatName;//传递chatName->ios,java
                msg.body.ext.chatIcon= chatIcon;//传递chatIcon->ios,java
                conn.send(msg.body);  
            }  
        };
        //时间函数
        function getNowFormatDate(){
          var timestamp1 = new Date().getTime();//获取时间戳此方法准确
          var timestamp2 = new Date(timestamp1);
          timestamp3 = timestamp2.toLocaleDateString().replace(/\//g, "-") + " " + timestamp2.toTimeString().substr(0, 8);
        }
       //展示消息  根据个人项目需求,正式版本会使用tpl模板 
        function showChatMessage(timestamp3,str,data,id){
            $("#recordchat-main").append(
                             '<li class="item-time"><span class="time">'+timestamp3+'</span></li>'
                            +'<li class="item-friend" id='+id+'>'
                            +'<div class="figure"><img src="http://beta.app.first-job-1.com'+str+'"></div>'
                            +'<div class="text">'+data+'</div>'
                            +'</li>');
        }
     //绑定数据模板
        function detailMessage(timestamp3,data,from,type,id,mid,chatId,chatIcon,status,filename,reserve,number){  
            var localContent = new Array();
            if (localStorage[chatId]) {  
                localContent = JSON.parse(localStorage[chatId]);  
            }  
            localContent[localContent.length]= { 'message':[{ 
                'time':timestamp3,  
                'data':data,//数据  
                'from':from,//谁发的  
                'type':type,//文本类型 text,file,picture  
                'id':id,//消息id  
                'mid':mid,
                'chatId':chatId,
                'chatIcon':chatIcon,
                'status':status,//状态
                'filename':filename, //文件名字 
                'reserve':reserve,//已读未读
                'number':number,//未读数量
            }]};  
            localStorage[chatId] = JSON.stringify(localContent);//存储本地; 
            //console.log(JSON.parse(localStorage[chatId]))
            
        }  
       //根据每一条消息的id存储本地 存储未读已读状态
        function statusRead(status,mid,id,reserve){
            var localStatusContent = new Array();
            if(localStatusContent[id]){
                localStatusContent = JSON.parse(localStorage[id]); 

            }
            localStatusContent[localStatusContent.length]={
                'status':status,
                'mid':mid,
                'id':id,
                'reserve':reserve
            };
            localStorage[id] = JSON.stringify(localStatusContent);
            //console.log(localStorage[id])
        }

项目需求只有表情和图片,文件视频,音频其实都是属于文件的一种,若有帮助请赞赏一下吧。

微信图片_20180525164006.jpg


微信图片_20180525164014.png



  收起阅读 »

【有问必答】有温度,有态度,有速度的IMGeek社区!

5分钟,是一个从提问到解答的总时长,有温度有态度有速度!
-IMGeek社区“鲁迅”




    IMGeek循着极客们开放、分享、协作、创新的精神,努力构建一个具有服务质量保障(Service Level Assurance , SLA)的社区。

   在IMGeek社区里征集到一批热心的技术专家,得到他们的承诺自愿回复IMGeek社区问题。只要你在IMGeek社区发布问题,专家们将会收到消息提醒,并及时回复。 

   当然,如果你在提交一个问题之前,可以先搜索一下,说不定你要提的问题已经有人提过并且得到解答。这样可以省却不少你的时间。 
 
   最后提醒一下提问的小伙伴,如果您的问题被解决,占用您一秒钟时间将回复设置为最佳回复,方便后面遇到相同问题的同学快速找到答案!
TIM截图20180524175557.png

  现在,从一个提问开始你的IMGeek社区之旅。
继续阅读 »
5分钟,是一个从提问到解答的总时长,有温度有态度有速度!
-IMGeek社区“鲁迅”




    IMGeek循着极客们开放、分享、协作、创新的精神,努力构建一个具有服务质量保障(Service Level Assurance , SLA)的社区。

   在IMGeek社区里征集到一批热心的技术专家,得到他们的承诺自愿回复IMGeek社区问题。只要你在IMGeek社区发布问题,专家们将会收到消息提醒,并及时回复。 

   当然,如果你在提交一个问题之前,可以先搜索一下,说不定你要提的问题已经有人提过并且得到解答。这样可以省却不少你的时间。 
 
   最后提醒一下提问的小伙伴,如果您的问题被解决,占用您一秒钟时间将回复设置为最佳回复,方便后面遇到相同问题的同学快速找到答案!
TIM截图20180524175557.png

  现在,从一个提问开始你的IMGeek社区之旅。 收起阅读 »

教您5分钟体验客户互动云

 
欢迎各位小伙伴们注册环信客户互动云!

为了帮助您快速了解环信客户互动云系统,我们为您准备了一个“商城”Demo。您可以先使用“商城”Demo进行客服体验。试用期短暂,机不可失,快来集成体验吧!
 
请先打开客服后台按照以下步骤添加app渠道关联

55b6d3a7688d18c9c580650677277c4f.gif



添加关联后 您就可以模拟客服场景体验啦

 扫码下方二维码,下载并安装访客端DEMO【环信移动客服】APP

6733c1b61baab84b10dda5f97c866bf0.png

 
打开【环信移动客服】APP,点击右下角的【设置】按钮后,点击右上角【扫一扫】,扫描关联app页面下方二维码,将该客服访客端体验DEMO与您的客服账号关联起来,即可体验与客服聊天或与客户聊天
注意:
初次在苹果手机上使用“商城”Demo时,需要进入“设置 > 通用 > 设备管理 > EaseMob Inc.”页面,点击“信任EaseMob Inc.”。
  
图片1(07-06-12-09-(06-12-11-18-30).png


您也可以 快速集成环信SDK,体验来自您APP的 真实用户咨询
 Android SDK:请参考CEC Android SDK集成
iOS SDK:请参考CEC iOS SDK集成
也可以直接查看APP集成指南


 
 您还可以为您的其他渠道接入客户互动云
  微博快速集成指南 
  微信快速集成指南
  网页快速集成指南

在您使用中遇到任何问题,可从以下 4 个途径得到解答!

开发文档 - 常见问题的解决方案在这里都能找到!
开发文档收录了所有常见问题,并按照“新手上路、客服模式、管理员模式、多渠道集成、第三方系统对接”对内容进行分类,同时,提供模糊搜索。

在线技术咨询 - 超快的问题响应机制,专业技术团队在线解答!
点击【客服后台】-【管理员模式】-【技术支持】-【联系客服】,输入您的问题即可。

环信社区- 使用者交流专区,召唤老司机搞定技术难题!
山不在高,有仙则灵,社区不在大,有大神就行!

电话咨询 - 最直接的方式,专职客服一对一解答!
咨询热线:400-612-1986
 

感谢读到这里的您,下方附上最新鲜的集成说明文档,据说看完走桃花呦
继续阅读 »
 
欢迎各位小伙伴们注册环信客户互动云!

为了帮助您快速了解环信客户互动云系统,我们为您准备了一个“商城”Demo。您可以先使用“商城”Demo进行客服体验。试用期短暂,机不可失,快来集成体验吧!
 
请先打开客服后台按照以下步骤添加app渠道关联

55b6d3a7688d18c9c580650677277c4f.gif



添加关联后 您就可以模拟客服场景体验啦

 扫码下方二维码,下载并安装访客端DEMO【环信移动客服】APP

6733c1b61baab84b10dda5f97c866bf0.png

 
打开【环信移动客服】APP,点击右下角的【设置】按钮后,点击右上角【扫一扫】,扫描关联app页面下方二维码,将该客服访客端体验DEMO与您的客服账号关联起来,即可体验与客服聊天或与客户聊天
注意:
初次在苹果手机上使用“商城”Demo时,需要进入“设置 > 通用 > 设备管理 > EaseMob Inc.”页面,点击“信任EaseMob Inc.”。
  
图片1(07-06-12-09-(06-12-11-18-30).png


您也可以 快速集成环信SDK,体验来自您APP的 真实用户咨询
 Android SDK:请参考CEC Android SDK集成
iOS SDK:请参考CEC iOS SDK集成
也可以直接查看APP集成指南


 
 您还可以为您的其他渠道接入客户互动云
  微博快速集成指南 
  微信快速集成指南
  网页快速集成指南

在您使用中遇到任何问题,可从以下 4 个途径得到解答!

开发文档 - 常见问题的解决方案在这里都能找到!
开发文档收录了所有常见问题,并按照“新手上路、客服模式、管理员模式、多渠道集成、第三方系统对接”对内容进行分类,同时,提供模糊搜索。

在线技术咨询 - 超快的问题响应机制,专业技术团队在线解答!
点击【客服后台】-【管理员模式】-【技术支持】-【联系客服】,输入您的问题即可。

环信社区- 使用者交流专区,召唤老司机搞定技术难题!
山不在高,有仙则灵,社区不在大,有大神就行!

电话咨询 - 最直接的方式,专职客服一对一解答!
咨询热线:400-612-1986
 

感谢读到这里的您,下方附上最新鲜的集成说明文档,据说看完走桃花呦 收起阅读 »

环信Rest java sdk吐槽+你想怎么写?

先附上java sdk以及示例代码的GitHub地址:
https://github.com/easemob/emchat-server-examples/tree/master/emchat-server-java
 
相信各位服务端的朋友都已经瞅过了,最后是否使用了这个sdk呢?如果没有使用,为什么不使用呢?你..你...你能不能告诉我为什么??尽情的喷我吧,告诉我该怎么写,我改还不行吗..
 
先行感谢各位诚恳的意见和建议,期望得到您的回复。
继续阅读 »
先附上java sdk以及示例代码的GitHub地址:
https://github.com/easemob/emchat-server-examples/tree/master/emchat-server-java
 
相信各位服务端的朋友都已经瞅过了,最后是否使用了这个sdk呢?如果没有使用,为什么不使用呢?你..你...你能不能告诉我为什么??尽情的喷我吧,告诉我该怎么写,我改还不行吗..
 
先行感谢各位诚恳的意见和建议,期望得到您的回复。 收起阅读 »

环信公开课19期回放:快速搭建一套直播答题系统实操

   本着对技术的热衷,对环信的眷恋和对党的忠诚,环信生态圈开发者基于环信即时通讯云写了直播答题开源项目-小信竞答。作为国内首个直播答题开源项目,“小信竞答”发表在IMGeek社区就受到了技术小伙伴们的好评,鉴于大家对直播答题的热情,3月29日举办了主题为“直播答题开源项目”的环信公开课,直播讲解如何快速搭建一套直播答题系统。
公开课第19期回放
 



 
 
课程大纲:

直播答题技术架构
1.    【同步问答】环信IM下发问题到海量用户确保毫秒级必达
2.    【视频美颜】环信人脸特效实时视频动态捕捉,贴纸美颜多种特效
3.    【无上限聊天室】环信直播聊天室高并发异步架构,弹性扩容应对流量峰值
4.    【智能反垃圾】环信智能反垃圾实时消息过滤及关键词屏蔽,及时规避风险
5.    【红包功能】奖金统计发放
6.    【复活卡、开黑组团】新玩法层出不穷
 “小信竞答”源码分享
1.    运行效果演示,现场互动答题送礼
2.    源码解读,国内首个开源直播答题项目
3.    经验之鉴,快速搭建一套直播答题系统实操

环信直播聊天室
1.    高并发异步架构,弹性扩容应对流量峰值
2.    多种消息格式,文字、表情、位置、扩展消息
3.    实时配置消息分级策略,重要消息优先必答
4.    后台管理审核功能,直播数据统计
5.    智能反垃圾,自定义敏感词过滤
6.    快速集成demo高质量代码示例
7.    聊天室人数无上限

小信竞答项目源码:github源码地址
 
感谢合作伙伴APICLloud,APKBUS,码客,七牛云,icustomer对本期环信公开课的大力支持
第19期公开课2副本.jpg

 
继续阅读 »
   本着对技术的热衷,对环信的眷恋和对党的忠诚,环信生态圈开发者基于环信即时通讯云写了直播答题开源项目-小信竞答。作为国内首个直播答题开源项目,“小信竞答”发表在IMGeek社区就受到了技术小伙伴们的好评,鉴于大家对直播答题的热情,3月29日举办了主题为“直播答题开源项目”的环信公开课,直播讲解如何快速搭建一套直播答题系统。
公开课第19期回放
 



 
 
课程大纲:

直播答题技术架构
1.    【同步问答】环信IM下发问题到海量用户确保毫秒级必达
2.    【视频美颜】环信人脸特效实时视频动态捕捉,贴纸美颜多种特效
3.    【无上限聊天室】环信直播聊天室高并发异步架构,弹性扩容应对流量峰值
4.    【智能反垃圾】环信智能反垃圾实时消息过滤及关键词屏蔽,及时规避风险
5.    【红包功能】奖金统计发放
6.    【复活卡、开黑组团】新玩法层出不穷
 “小信竞答”源码分享
1.    运行效果演示,现场互动答题送礼
2.    源码解读,国内首个开源直播答题项目
3.    经验之鉴,快速搭建一套直播答题系统实操

环信直播聊天室
1.    高并发异步架构,弹性扩容应对流量峰值
2.    多种消息格式,文字、表情、位置、扩展消息
3.    实时配置消息分级策略,重要消息优先必答
4.    后台管理审核功能,直播数据统计
5.    智能反垃圾,自定义敏感词过滤
6.    快速集成demo高质量代码示例
7.    聊天室人数无上限

小信竞答项目源码:github源码地址
 
感谢合作伙伴APICLloud,APKBUS,码客,七牛云,icustomer对本期环信公开课的大力支持
第19期公开课2副本.jpg

  收起阅读 »

iOS 自定义cell

简书地址:自定义cell(一)https://www.jianshu.com/p/8befabfdc4f0
               自定义cell(二)https://www.jianshu.com/p/85921a5da8d0

QQ20180410-161412@2x.png


QQ20180410-161420@2x.png

 
继续阅读 »
简书地址:自定义cell(一)https://www.jianshu.com/p/8befabfdc4f0
               自定义cell(二)https://www.jianshu.com/p/85921a5da8d0

QQ20180410-161412@2x.png


QQ20180410-161420@2x.png

  收起阅读 »

【环信征文】|PHP WEBIM整合到项目(一条龙服务包括保存聊天记录到本地数据库)

   我是用think PHP做的项目,我们的需求是用IM跟他人聊天并且保存聊天记录到本地。我把整个环信文件下载了下来放在了项目的js文件里。并且没有做单独的页面渲染直接用的环信原来的页面,所以大部分功能都写在了控制器里最后跳转到IM的页面。
第一步,IM集成
  这个可以参照文档,但是这个文档好像是过时的,如果集成失败可以上官方登陆页面 http://webim.easemob.com/ 把页面加载的文件抄下来。
第二步,自动登陆和他人聊天
  此步要先把网站上的用户注册一个环信账号,然后自动登陆跳过环信的登陆页面,然后把对方加为好友(此步如果对方没有环信账号也需要把好友注册一个环信账号)。上代码

微信图片_20180410135435.png


逻辑是这么实现的,因为在控制器里做的操作,所以登陆信息要保存在cookie里。里面的方法都可以在http://api-docs.easemob.com/%2 ... token这个环信的服务器端集成Swagger文档里找到,然后用Curl调用,或者一个更简单的方法,有一个完整的类可以用,链接在此https://github.com/easemob/emc ... t.php,简单的curl是这么用的

微信图片_20180409114955.png


好此步就此完成
第三步,头像、昵称 渲染
   这步我的处理方法是把你网站用户的头像和昵称查出来然后存在cookie里,然后在demojs文件里进行调用,这步主要麻烦的地方是需要在demo.js的文件里找到页面渲染的地方,大约有8个地方(23288,27321,29764,29862,30101,31211,31360,31465)
   这三步完成基本就完成了IM整合进项目了,还有一个保存聊天记录到本地,我没有用官方的接口来下载历史记录,听说下载下来是一个包,而且数据还会有丢失,所以我的做法是每次发送消息调用自己的Ajax来进行保存数据库操作,然后新在打开聊天的时候请求后台获得聊天记录。
  好,我在demojs里找到了2个发送消息时候的方法,一个是发送文字和表情,一个是发送图片。sendText(27399)这个方法是发送文字和表情的

微信图片_20180409135255.png


  msg.boy里面可以获得到你需要保存在数据库里的所有信息,chat_time是因为聊天记录的时间格式是2018/4/9 上午9:53:03这种的所以我自己加上去的。
  pictureChange(22977)这个方法是发送图片的,这里的url非常重要,而且在msg.set在外面拿不到所以我放在了方法里面调用的Ajax,这个url 是
https://a1.easemob.com/1110170 ... 79a8b
这样的二进制一会需要下载到本地或者上传到你需要的图片存储空间,我放在了七牛上。

微信图片_20180409135626.png


  保存文字信息非常简单,就像下图一样就可以了,下面的做的判断是当消息类型是图片的时候调用一下图片上传方法

微信图片_20180409140143.png



微信图片_20180410135615.png


  这个图片是图片上传方法,我在后台用CURL进行了上传操作,最开始我先把图片保存在了本地然后再上传,后来我找了二进制可以上传,改成了这个样子。(值得注意的是我用CURL调用的url返回的值会有一个红色的符号,所以我用substr给它截掉了,之前没发现怎么都找不到图片。最后发现是database文件的编码有问题改成utf-8之后截取字符串的那步可以去掉了)这里的接口就是一个普通的上传方法然后返回的是图片名称。这样聊天记录也保存完毕了。
  表结构

微信图片_20180409142529.png


  下一步是读取聊天记录releaseChatRecord这个方法是demojs每次你点击聊天窗口都会触发的方法,我把ajax放在这这里。

微信图片_20180409141116.png


  图片里的targetId就是你当天聊天的好友id,自己的id怎么得到不需要说了吧,你肯定是在登陆状态才能聊天。Demo.chatRecord这个就是需要替换的聊天记录,Ajax请求后台

微信图片_20180409141344.png


  这里注意的是要获得聊天记录需要进行红圈里的判断,来分别你给我发的消息和你给我发的消息,我之前犯得错误没判断最后只获得了我给他人发的消息记录。。。循环里的是构造demojs需要用的json数据。这个对象结构可以在releaseChatRecord方法里console.log()出来然后自己拼装。
  这样已经基本完成了最后上成功后的图片

微信图片_20180409142025.png


  还有个问题没处理就是未送达那个文字,因为我构造的数据里没有加上read=true.你可以选择把IM里这个功能关闭或者在数据里加上去。
  这样保存聊天记录的方式是每次发送都进行保存如果人数太多或太频繁可能会出现问题,以后可能会换成调用官网的获得历史记录的接口。
 
继续阅读 »
   我是用think PHP做的项目,我们的需求是用IM跟他人聊天并且保存聊天记录到本地。我把整个环信文件下载了下来放在了项目的js文件里。并且没有做单独的页面渲染直接用的环信原来的页面,所以大部分功能都写在了控制器里最后跳转到IM的页面。
第一步,IM集成
  这个可以参照文档,但是这个文档好像是过时的,如果集成失败可以上官方登陆页面 http://webim.easemob.com/ 把页面加载的文件抄下来。
第二步,自动登陆和他人聊天
  此步要先把网站上的用户注册一个环信账号,然后自动登陆跳过环信的登陆页面,然后把对方加为好友(此步如果对方没有环信账号也需要把好友注册一个环信账号)。上代码

微信图片_20180410135435.png


逻辑是这么实现的,因为在控制器里做的操作,所以登陆信息要保存在cookie里。里面的方法都可以在http://api-docs.easemob.com/%2 ... token这个环信的服务器端集成Swagger文档里找到,然后用Curl调用,或者一个更简单的方法,有一个完整的类可以用,链接在此https://github.com/easemob/emc ... t.php,简单的curl是这么用的

微信图片_20180409114955.png


好此步就此完成
第三步,头像、昵称 渲染
   这步我的处理方法是把你网站用户的头像和昵称查出来然后存在cookie里,然后在demojs文件里进行调用,这步主要麻烦的地方是需要在demo.js的文件里找到页面渲染的地方,大约有8个地方(23288,27321,29764,29862,30101,31211,31360,31465)
   这三步完成基本就完成了IM整合进项目了,还有一个保存聊天记录到本地,我没有用官方的接口来下载历史记录,听说下载下来是一个包,而且数据还会有丢失,所以我的做法是每次发送消息调用自己的Ajax来进行保存数据库操作,然后新在打开聊天的时候请求后台获得聊天记录。
  好,我在demojs里找到了2个发送消息时候的方法,一个是发送文字和表情,一个是发送图片。sendText(27399)这个方法是发送文字和表情的

微信图片_20180409135255.png


  msg.boy里面可以获得到你需要保存在数据库里的所有信息,chat_time是因为聊天记录的时间格式是2018/4/9 上午9:53:03这种的所以我自己加上去的。
  pictureChange(22977)这个方法是发送图片的,这里的url非常重要,而且在msg.set在外面拿不到所以我放在了方法里面调用的Ajax,这个url 是
https://a1.easemob.com/1110170 ... 79a8b
这样的二进制一会需要下载到本地或者上传到你需要的图片存储空间,我放在了七牛上。

微信图片_20180409135626.png


  保存文字信息非常简单,就像下图一样就可以了,下面的做的判断是当消息类型是图片的时候调用一下图片上传方法

微信图片_20180409140143.png



微信图片_20180410135615.png


  这个图片是图片上传方法,我在后台用CURL进行了上传操作,最开始我先把图片保存在了本地然后再上传,后来我找了二进制可以上传,改成了这个样子。(值得注意的是我用CURL调用的url返回的值会有一个红色的符号,所以我用substr给它截掉了,之前没发现怎么都找不到图片。最后发现是database文件的编码有问题改成utf-8之后截取字符串的那步可以去掉了)这里的接口就是一个普通的上传方法然后返回的是图片名称。这样聊天记录也保存完毕了。
  表结构

微信图片_20180409142529.png


  下一步是读取聊天记录releaseChatRecord这个方法是demojs每次你点击聊天窗口都会触发的方法,我把ajax放在这这里。

微信图片_20180409141116.png


  图片里的targetId就是你当天聊天的好友id,自己的id怎么得到不需要说了吧,你肯定是在登陆状态才能聊天。Demo.chatRecord这个就是需要替换的聊天记录,Ajax请求后台

微信图片_20180409141344.png


  这里注意的是要获得聊天记录需要进行红圈里的判断,来分别你给我发的消息和你给我发的消息,我之前犯得错误没判断最后只获得了我给他人发的消息记录。。。循环里的是构造demojs需要用的json数据。这个对象结构可以在releaseChatRecord方法里console.log()出来然后自己拼装。
  这样已经基本完成了最后上成功后的图片

微信图片_20180409142025.png


  还有个问题没处理就是未送达那个文字,因为我构造的数据里没有加上read=true.你可以选择把IM里这个功能关闭或者在数据里加上去。
  这样保存聊天记录的方式是每次发送都进行保存如果人数太多或太频繁可能会出现问题,以后可能会换成调用官网的获得历史记录的接口。
  收起阅读 »

iOS 中会话列表无法在获取消息后自动刷新,只能通过跳转到别的页面返回之后才会刷新。。

      在网上找到相似的答案,-(void)conversationListDidUpdate:(NSArray *)aConversationList{

    [self.tableView reloadData];

}说要实现这个方法。。可是这个方法是EMChatmanagerdelegate中的方法,单实现这个方法并没有走。。
大家有遇到过的吗?
继续阅读 »
      在网上找到相似的答案,-(void)conversationListDidUpdate:(NSArray *)aConversationList{

    [self.tableView reloadData];

}说要实现这个方法。。可是这个方法是EMChatmanagerdelegate中的方法,单实现这个方法并没有走。。
大家有遇到过的吗? 收起阅读 »

基于环信的仿QQ即时通讯的简单实现

概述
 
今天看了环信的API,就利用下午的时间动手试了试,然后做了一个小Demo。详细

我的博客地址

之前一直想实现聊天的功能,但是感觉有点困难,今天看了环信的API,就利用下午的时间动手试了试,然后做了一个小Demo。
因为没有刻意去做聊天软件,花的时间也不多,然后界面就很简单,都是一些基本知识,如果觉得功能简单,可以自行添加,我这就不多介绍了。

照例先来一波动态演示:
4043475-d16a88926805236a.gif

 
功能很简单,注册用户 —> 用户登录 —> 选择聊天对象 —> 开始聊天

使用到的知识点:
  1. RecyclerView
  2. CardView
  3. 环信的API的简单使用


依赖的库
compile 'com.android.support:appcompat-v7:24.2.1'
compile 'com.android.support:cardview-v7:24.1.1'
compile 'com.android.support:recyclerview-v7:24.0.0'
1、聊天页面

首先是看了郭神的《第二行代码》做了聊天界面,用的是RecyclerView

a. 消息类的封装
public class MSG {
public static final int TYPE_RECEIVED = 0;//消息的类型:接收
public static final int TYPE_SEND = 1; //消息的类型:发送
private String content;//消息的内容
private int type; //消息的类型
public MSG(String content, int type) {
this.content = content;
this.type = type;
}
public String getContent() {
return content;
}
public int getType() {
return type;
}
}
b. RecyclerView子项的布局​
<LinearLayout
android:id="@+id/ll_msg_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
<!-- 设置点击效果为水波纹(5.0以上) -->
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:orientation="horizontal"
android:padding="2dp">
<android.support.v7.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:cardCornerRadius="20dp"
app:cardPreventCornerOverlap="false"
app:cardUseCompatPadding="true">
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:scaleType="centerCrop"
android:src="@mipmap/man" />
</android.support.v7.widget.CardView>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/message_left"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_msg_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="10dp"
android:textColor="#fff" />
</LinearLayout>
</LinearLayout>
这是左边的部分,至于右边应该也就简单了。我用CardView把ImageView包裹起来,这样比较好看。效果如下:
4043475-76ea5370b4d09d89.png

c. RecyclerView适配器​
 public class MsgAdapter extends RecyclerView.Adapter<MsgAdapter.MyViewHolder> {
private List<MSG> mMsgList;
public MsgAdapter(List<MSG> mMsgList) {
this.mMsgList = mMsgList;
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = View.inflate(parent.getContext(), R.layout.item_msg, null);
MyViewHolder holder = new MyViewHolder(view);
return holder;
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
MSG msg = mMsgList.get(position);
if (msg.getType() == MSG.TYPE_RECEIVED){
//如果是收到的消息,显示左边布局,隐藏右边布局
holder.llLeft.setVisibility(View.VISIBLE);
holder.llRight.setVisibility(View.GONE);
holder.tv_Left.setText(msg.getContent());
} else if (msg.getType() == MSG.TYPE_SEND){
//如果是发送的消息,显示右边布局,隐藏左边布局
holder.llLeft.setVisibility(View.GONE);
holder.llRight.setVisibility(View.VISIBLE);
holder.tv_Right.setText(msg.getContent());
}
}
@Override
public int getItemCount() {
return mMsgList.size();
}
static class MyViewHolder extends RecyclerView.ViewHolder{
LinearLayout llLeft;
LinearLayout llRight;
TextView tv_Left;
TextView tv_Right;
public MyViewHolder(View itemView) {
super(itemView);
llLeft = (LinearLayout) itemView.findViewById(R.id.ll_msg_left);
llRight = (LinearLayout) itemView.findViewById(R.id.ll_msg_right);
tv_Left = (TextView) itemView.findViewById(R.id.tv_msg_left);
tv_Right = (TextView) itemView.findViewById(R.id.tv_msg_right);
}
}
}
这部分应该也没什么问题,就是适配器的创建,我之前的文章也讲过 传送门:简单粗暴——RecyclerView

d. RecyclerView初始化

就是一些基本的初始化,我就不赘述了,讲一下添加数据的细节处理
 
  btSend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String content = etInput.getText().toString().trim();
if (!TextUtils.isEmpty(content)){
...//环信部分的发送消息
MSG msg = new MSG(content, MSG.TYPE_SEND);
mList.add(msg);
//当有新消息时,刷新RecyclerView中的显示
mAdapter.notifyItemInserted(mList.size() - 1);
//将RecyclerView定位到最后一行
mRecyclerView.scrollToPosition(mList.size() - 1);
etInput.setText("");
}
}
});
至此界面已经结束了,接下来就是数据的读取

2. 环信API的简单应用

官网有详细的API介绍 环信及时通讯V3.0,我这里就简单介绍如何简单集成

a. 环信开发账号的注册

环信官网


创建应用得到Appkey后面要用

4043475-e4dd45e05060467f.png


b. SDK导入

你可以直接下载然后拷贝工程的libs目录下

Android Studio可以直接添加依赖
 
将以下代码放到项目根目录的build.gradle文件里
repositories {
maven { url "https://raw.githubusercontent. ... ot%3B }
}
在你的module的build.gradle里加入以下代码
android {
//use legacy for android 6.0
useLibrary 'org.apache.http.legacy'
}
dependencies {
compile 'com.android.support:appcompat-v7:23.4.0'
//Optional compile for GCM (Google Cloud Messaging).
compile 'com.google.android.gms:play-services-gcm:9.4.0'
compile 'com.hyphenate:hyphenate-sdk:3.2.3'
}
如果想使用不包含音视频通话的sdk,用compile 'com.hyphenate [:hyphenate-sdk-lite:] 3.2.3'
 
c. 清单文件配置
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="Your Package"
android:versionCode="100"
android:versionName="1.0.0">
<!-- Required -->
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:name="Your Application">
<!-- 设置环信应用的AppKey -->
<meta-data android:name="EASEMOB_APPKEY" android:value="Your AppKey" />
<!-- 声明SDK所需的service SDK核心功能-->
<service android:name="com.hyphenate.chat.EMChatService" android:exported="true"/>
<service android:name="com.hyphenate.chat.EMJobService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="true"
/>
<!-- 声明SDK所需的receiver -->
<receiver android:name="com.hyphenate.chat.EMMonitorReceiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REMOVED"/>
<data android:scheme="package"/>
</intent-filter>
<!-- 可选filter -->
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.USER_PRESENT" />
</intent-filter>
</receiver>
</application>
</manifest>
APP打包混淆
-keep class com.hyphenate.** {*;}
-dontwarn com.hyphenate.**
d. 初始化SDK
在自定义Application的onCreate中初始化
public class MyApplication extends Application {
private Context appContext;
@Override
public void onCreate() {
super.onCreate();
EMOptions options = new EMOptions();
options.setAcceptInvitationAlways(false);
appContext = this;
int pid = android.os.Process.myPid();
String processAppName = getAppName(pid);
// 如果APP启用了远程的service,此application:onCreate会被调用2次
// 为了防止环信SDK被初始化2次,加此判断会保证SDK被初始化1次
// 默认的APP会在以包名为默认的process name下运行,如果查到的process name不是APP的process name就立即返回
if (processAppName == null || !processAppName.equalsIgnoreCase(appContext.getPackageName())) {
Log.e("--->", "enter the service process!");
// 则此application::onCreate 是被service 调用的,直接返回
return;
}
//初始化
EMClient.getInstance().init(getApplicationContext(), options);
//在做打包混淆时,关闭debug模式,避免消耗不必要的资源
EMClient.getInstance().setDebugMode(true);
}
private String getAppName(int pID) {
String processName = null;
ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
List l = am.getRunningAppProcesses();
Iterator i = l.iterator();
PackageManager pm = this.getPackageManager();
while (i.hasNext()) {
ActivityManager.RunningAppProcessInfo info = (ActivityManager.RunningAppProcessInfo) (i.next());
try {
if (info.pid == pID) {
processName = info.processName;
return processName;
}
} catch (Exception e) {
// Log.d("Process", "Error>> :"+ e.toString());
}
}
return processName;
}
}
e. 注册和登陆

注册要在子线程中执行
//注册失败会抛出HyphenateException
EMClient.getInstance().createAccount(username, pwd);//同步方法
EMClient.getInstance().login(userName,password,new EMCallBack() {//回调
@Override
public void onSuccess() {
EMClient.getInstance().groupManager().loadAllGroups();
EMClient.getInstance().chatManager().loadAllConversations();
Log.d("main", "登录聊天服务器成功!");
}
@Override
public void onProgress(int progress, String status) {
}
@Override
public void onError(int code, String message) {
Log.d("main", "登录聊天服务器失败!");
}
});
f. 发送消息
//创建一条文本消息,content为消息文字内容,toChatUsername为对方用户或者群聊的id,后文皆是如此
EMMessage message = EMMessage.createTxtSendMessage(content, toChatUsername);
//发送消息
EMClient.getInstance().chatManager().sendMessage(message);
g. 接收消息
msgListener = new EMMessageListener() {
@Override
public void onMessageReceived(List<EMMessage> messages) {
//收到消息
String result = messages.get(0).getBody().toString();
String msgReceived = result.substring(5, result.length() - 1);
Log.i(TAG, "onMessageReceived: " + msgReceived);
final MSG msg = new MSG(msgReceived, MSG.TYPE_RECEIVED);
runOnUiThread(new Runnable() {
@Override
public void run() {
mList.add(msg);
mAdapter.notifyDataSetChanged();
mRecyclerView.scrollToPosition(mList.size() - 1);
}
});
}
@Override
public void onCmdMessageReceived(List<EMMessage> messages) {
//收到透传消息
}
@Override
public void onMessageRead(List<EMMessage> list) {
}
@Override
public void onMessageDelivered(List<EMMessage> list) {
}
@Override
public void onMessageChanged(EMMessage message, Object change) {
//消息状态变动
}
};
接收消息的监听器分别需要在OnResume()和OnDestory()方法中注册和取消注册
EMClient.getInstance().chatManager().addMessageListener(msgListener);//注册
EMClient.getInstance().chatManager().removeMessageListener(msgListener);//取消注册
需要注意的是,当接收到消息,需要在主线程中更新适配器,否则会不能及时刷新出来

项目文件截图:

HyTYCEDIj4uugpEvJ59.jpg

到此,一个简单的及时聊天Demo已经完成,功能很简单,如果需要添加额外功能的话,可以自行参考官网,官网给出的教程还是很不错的!

最后希望大家能多多支持我,需要你们的支持喜欢!!
继续阅读 »
概述
 
今天看了环信的API,就利用下午的时间动手试了试,然后做了一个小Demo。详细

我的博客地址

之前一直想实现聊天的功能,但是感觉有点困难,今天看了环信的API,就利用下午的时间动手试了试,然后做了一个小Demo。
因为没有刻意去做聊天软件,花的时间也不多,然后界面就很简单,都是一些基本知识,如果觉得功能简单,可以自行添加,我这就不多介绍了。

照例先来一波动态演示:
4043475-d16a88926805236a.gif

 
功能很简单,注册用户 —> 用户登录 —> 选择聊天对象 —> 开始聊天

使用到的知识点:
  1. RecyclerView
  2. CardView
  3. 环信的API的简单使用


依赖的库
compile 'com.android.support:appcompat-v7:24.2.1'
compile 'com.android.support:cardview-v7:24.1.1'
compile 'com.android.support:recyclerview-v7:24.0.0'
1、聊天页面

首先是看了郭神的《第二行代码》做了聊天界面,用的是RecyclerView

a. 消息类的封装
public class MSG {
public static final int TYPE_RECEIVED = 0;//消息的类型:接收
public static final int TYPE_SEND = 1; //消息的类型:发送
private String content;//消息的内容
private int type; //消息的类型
public MSG(String content, int type) {
this.content = content;
this.type = type;
}
public String getContent() {
return content;
}
public int getType() {
return type;
}
}
b. RecyclerView子项的布局​
<LinearLayout
android:id="@+id/ll_msg_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
<!-- 设置点击效果为水波纹(5.0以上) -->
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:orientation="horizontal"
android:padding="2dp">
<android.support.v7.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:cardCornerRadius="20dp"
app:cardPreventCornerOverlap="false"
app:cardUseCompatPadding="true">
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:scaleType="centerCrop"
android:src="@mipmap/man" />
</android.support.v7.widget.CardView>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/message_left"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_msg_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="10dp"
android:textColor="#fff" />
</LinearLayout>
</LinearLayout>
这是左边的部分,至于右边应该也就简单了。我用CardView把ImageView包裹起来,这样比较好看。效果如下:
4043475-76ea5370b4d09d89.png

c. RecyclerView适配器​
 public class MsgAdapter extends RecyclerView.Adapter<MsgAdapter.MyViewHolder> {
private List<MSG> mMsgList;
public MsgAdapter(List<MSG> mMsgList) {
this.mMsgList = mMsgList;
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = View.inflate(parent.getContext(), R.layout.item_msg, null);
MyViewHolder holder = new MyViewHolder(view);
return holder;
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
MSG msg = mMsgList.get(position);
if (msg.getType() == MSG.TYPE_RECEIVED){
//如果是收到的消息,显示左边布局,隐藏右边布局
holder.llLeft.setVisibility(View.VISIBLE);
holder.llRight.setVisibility(View.GONE);
holder.tv_Left.setText(msg.getContent());
} else if (msg.getType() == MSG.TYPE_SEND){
//如果是发送的消息,显示右边布局,隐藏左边布局
holder.llLeft.setVisibility(View.GONE);
holder.llRight.setVisibility(View.VISIBLE);
holder.tv_Right.setText(msg.getContent());
}
}
@Override
public int getItemCount() {
return mMsgList.size();
}
static class MyViewHolder extends RecyclerView.ViewHolder{
LinearLayout llLeft;
LinearLayout llRight;
TextView tv_Left;
TextView tv_Right;
public MyViewHolder(View itemView) {
super(itemView);
llLeft = (LinearLayout) itemView.findViewById(R.id.ll_msg_left);
llRight = (LinearLayout) itemView.findViewById(R.id.ll_msg_right);
tv_Left = (TextView) itemView.findViewById(R.id.tv_msg_left);
tv_Right = (TextView) itemView.findViewById(R.id.tv_msg_right);
}
}
}
这部分应该也没什么问题,就是适配器的创建,我之前的文章也讲过 传送门:简单粗暴——RecyclerView

d. RecyclerView初始化

就是一些基本的初始化,我就不赘述了,讲一下添加数据的细节处理
 
  btSend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String content = etInput.getText().toString().trim();
if (!TextUtils.isEmpty(content)){
...//环信部分的发送消息
MSG msg = new MSG(content, MSG.TYPE_SEND);
mList.add(msg);
//当有新消息时,刷新RecyclerView中的显示
mAdapter.notifyItemInserted(mList.size() - 1);
//将RecyclerView定位到最后一行
mRecyclerView.scrollToPosition(mList.size() - 1);
etInput.setText("");
}
}
});
至此界面已经结束了,接下来就是数据的读取

2. 环信API的简单应用

官网有详细的API介绍 环信及时通讯V3.0,我这里就简单介绍如何简单集成

a. 环信开发账号的注册

环信官网


创建应用得到Appkey后面要用

4043475-e4dd45e05060467f.png


b. SDK导入

你可以直接下载然后拷贝工程的libs目录下

Android Studio可以直接添加依赖
 
将以下代码放到项目根目录的build.gradle文件里
repositories {
maven { url "https://raw.githubusercontent. ... ot%3B }
}
在你的module的build.gradle里加入以下代码
android {
//use legacy for android 6.0
useLibrary 'org.apache.http.legacy'
}
dependencies {
compile 'com.android.support:appcompat-v7:23.4.0'
//Optional compile for GCM (Google Cloud Messaging).
compile 'com.google.android.gms:play-services-gcm:9.4.0'
compile 'com.hyphenate:hyphenate-sdk:3.2.3'
}
如果想使用不包含音视频通话的sdk,用compile 'com.hyphenate [:hyphenate-sdk-lite:] 3.2.3'
 
c. 清单文件配置
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="Your Package"
android:versionCode="100"
android:versionName="1.0.0">
<!-- Required -->
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:name="Your Application">
<!-- 设置环信应用的AppKey -->
<meta-data android:name="EASEMOB_APPKEY" android:value="Your AppKey" />
<!-- 声明SDK所需的service SDK核心功能-->
<service android:name="com.hyphenate.chat.EMChatService" android:exported="true"/>
<service android:name="com.hyphenate.chat.EMJobService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="true"
/>
<!-- 声明SDK所需的receiver -->
<receiver android:name="com.hyphenate.chat.EMMonitorReceiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REMOVED"/>
<data android:scheme="package"/>
</intent-filter>
<!-- 可选filter -->
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.USER_PRESENT" />
</intent-filter>
</receiver>
</application>
</manifest>
APP打包混淆
-keep class com.hyphenate.** {*;}
-dontwarn com.hyphenate.**
d. 初始化SDK
在自定义Application的onCreate中初始化
public class MyApplication extends Application {
private Context appContext;
@Override
public void onCreate() {
super.onCreate();
EMOptions options = new EMOptions();
options.setAcceptInvitationAlways(false);
appContext = this;
int pid = android.os.Process.myPid();
String processAppName = getAppName(pid);
// 如果APP启用了远程的service,此application:onCreate会被调用2次
// 为了防止环信SDK被初始化2次,加此判断会保证SDK被初始化1次
// 默认的APP会在以包名为默认的process name下运行,如果查到的process name不是APP的process name就立即返回
if (processAppName == null || !processAppName.equalsIgnoreCase(appContext.getPackageName())) {
Log.e("--->", "enter the service process!");
// 则此application::onCreate 是被service 调用的,直接返回
return;
}
//初始化
EMClient.getInstance().init(getApplicationContext(), options);
//在做打包混淆时,关闭debug模式,避免消耗不必要的资源
EMClient.getInstance().setDebugMode(true);
}
private String getAppName(int pID) {
String processName = null;
ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
List l = am.getRunningAppProcesses();
Iterator i = l.iterator();
PackageManager pm = this.getPackageManager();
while (i.hasNext()) {
ActivityManager.RunningAppProcessInfo info = (ActivityManager.RunningAppProcessInfo) (i.next());
try {
if (info.pid == pID) {
processName = info.processName;
return processName;
}
} catch (Exception e) {
// Log.d("Process", "Error>> :"+ e.toString());
}
}
return processName;
}
}
e. 注册和登陆

注册要在子线程中执行
//注册失败会抛出HyphenateException
EMClient.getInstance().createAccount(username, pwd);//同步方法
EMClient.getInstance().login(userName,password,new EMCallBack() {//回调
@Override
public void onSuccess() {
EMClient.getInstance().groupManager().loadAllGroups();
EMClient.getInstance().chatManager().loadAllConversations();
Log.d("main", "登录聊天服务器成功!");
}
@Override
public void onProgress(int progress, String status) {
}
@Override
public void onError(int code, String message) {
Log.d("main", "登录聊天服务器失败!");
}
});
f. 发送消息
//创建一条文本消息,content为消息文字内容,toChatUsername为对方用户或者群聊的id,后文皆是如此
EMMessage message = EMMessage.createTxtSendMessage(content, toChatUsername);
//发送消息
EMClient.getInstance().chatManager().sendMessage(message);
g. 接收消息
msgListener = new EMMessageListener() {
@Override
public void onMessageReceived(List<EMMessage> messages) {
//收到消息
String result = messages.get(0).getBody().toString();
String msgReceived = result.substring(5, result.length() - 1);
Log.i(TAG, "onMessageReceived: " + msgReceived);
final MSG msg = new MSG(msgReceived, MSG.TYPE_RECEIVED);
runOnUiThread(new Runnable() {
@Override
public void run() {
mList.add(msg);
mAdapter.notifyDataSetChanged();
mRecyclerView.scrollToPosition(mList.size() - 1);
}
});
}
@Override
public void onCmdMessageReceived(List<EMMessage> messages) {
//收到透传消息
}
@Override
public void onMessageRead(List<EMMessage> list) {
}
@Override
public void onMessageDelivered(List<EMMessage> list) {
}
@Override
public void onMessageChanged(EMMessage message, Object change) {
//消息状态变动
}
};
接收消息的监听器分别需要在OnResume()和OnDestory()方法中注册和取消注册
EMClient.getInstance().chatManager().addMessageListener(msgListener);//注册
EMClient.getInstance().chatManager().removeMessageListener(msgListener);//取消注册
需要注意的是,当接收到消息,需要在主线程中更新适配器,否则会不能及时刷新出来

项目文件截图:

HyTYCEDIj4uugpEvJ59.jpg

到此,一个简单的及时聊天Demo已经完成,功能很简单,如果需要添加额外功能的话,可以自行参考官网,官网给出的教程还是很不错的!

最后希望大家能多多支持我,需要你们的支持喜欢!! 收起阅读 »

源码下载:全国首个直播答题开源项目了解一下!!!(基于环信即时通讯云)

   2018年伊始,全民直播答题浪潮来袭,一度被认为是一个新的互联网风口,王思聪凭借在现象级产品《冲顶大会》上疯狂"撒币"一时风光无二,凭借超高奖金和超低门槛吸引了大量网民参与和市场的目光。正因为直播答题是一种通过极低的成本来推动APP获客、保留存、拉活跃的新模式,各类直播答题APP如雨后春笋般进入大家的视野,越来越多企业希望赶上这波风口,快速搭建一套直播答题系统。作为一名环信生态圈资深开发者,本着对技术的热衷,对环信的眷恋和对党的忠诚,基于环信即时通讯云写了“小信竞答”这个直播答题开源项目,目前项目源码已全部免费开放,希望对有需求的企业和开发者提供一个思路和参考。



 

[b]
微信图片_20180316190105.jpg
[/b]
 
小信竞答技术架构图:
theFlowChart.png

     整个项目分为管理员端,观众端和服务端,首先在服务端预设好题目,由管理员发起直播开始答题,服务端收到指令将12道题目利用环信IM推送到观众端,观众端收到题目开始答题,将答案返回给服务端由服务端进行判断,如果答题正确进入下一题,答题错误判断是否使用复活卡,这里要注意的是需要加一个复活卡的使用次数判断。
 
   在整个答题过程中,管理员端会定时去服务端查询答题结果,等到全部答题结束,点击结束本次答题,服务端将计算好的结果返回并发放奖金,使用环信IM推送将答题结果推给观众端。
 
小信竞答效果图

微信图片_20180316190206.jpg


关于直播间:
   直播间由直播画面和聊天室两个部分组成,“小信竞答”的聊天室使用环信聊天室,集成比较简单,基础版就能支持5000人在线聊天,增值服务版聊天室人数无上限,可以去环信官网注册一个开发者账号,创建应用将APPKEY替换成自己的;环信直播聊天室可以集成所有市场主流CDN厂商的推拉流功能(腾讯,七牛,UCloud,网宿等)。
 
环信直播聊天室特点  
This is Title
 
1、采用支持高并发的异步架构,轻松应对千万级并发请求; 各项基础服务集群化,确保系统高可用性; 系统冗余度高,容量评估体系完善,弹性扩容应对流量峰值;
2、支持各种消息格式:文字、表情、图片、声音、视频、附件、位置、扩展消息;
3、支持实时配置的消息分级策略,确保重要消息优先必达; 
4、支持直播聊天室后台管理及审核功能,提供直播相关数据统计;
5、提供智能反垃圾和自定义敏感词过滤功能;
6、快速集成,demo提供高质量代码示例,可根据运营情况随时扩展;
7、聊天室人数无上限 
小信竞答项目源码: github源码地址
 
写到最后:
   
     小信竞答源码全部开放,仅供学习和参考,如果作为商业用途,按照广电总局对网络直播答题节目管理的,需要 “网络视听许可证、主持人持证、还有通过审批发放的节目备案号”,三证缺一不可,未持有《信息网络传播视听节目许可证》的任何机构和个人,一律不得开办网络直播答题节目。
 
本月底《环信公开课第19期-直播答题开源项目》将线上讲解“小信竞答”实现思路,手把手教您从零开始搭建一个直播答题项目,扫码加入公开课微信群与大牛面对面交流。
微信图片_20180316190334.jpg

 
继续阅读 »
   2018年伊始,全民直播答题浪潮来袭,一度被认为是一个新的互联网风口,王思聪凭借在现象级产品《冲顶大会》上疯狂"撒币"一时风光无二,凭借超高奖金和超低门槛吸引了大量网民参与和市场的目光。正因为直播答题是一种通过极低的成本来推动APP获客、保留存、拉活跃的新模式,各类直播答题APP如雨后春笋般进入大家的视野,越来越多企业希望赶上这波风口,快速搭建一套直播答题系统。作为一名环信生态圈资深开发者,本着对技术的热衷,对环信的眷恋和对党的忠诚,基于环信即时通讯云写了“小信竞答”这个直播答题开源项目,目前项目源码已全部免费开放,希望对有需求的企业和开发者提供一个思路和参考。



 

[b]
微信图片_20180316190105.jpg
[/b]
 
小信竞答技术架构图:
theFlowChart.png

     整个项目分为管理员端,观众端和服务端,首先在服务端预设好题目,由管理员发起直播开始答题,服务端收到指令将12道题目利用环信IM推送到观众端,观众端收到题目开始答题,将答案返回给服务端由服务端进行判断,如果答题正确进入下一题,答题错误判断是否使用复活卡,这里要注意的是需要加一个复活卡的使用次数判断。
 
   在整个答题过程中,管理员端会定时去服务端查询答题结果,等到全部答题结束,点击结束本次答题,服务端将计算好的结果返回并发放奖金,使用环信IM推送将答题结果推给观众端。
 
小信竞答效果图

微信图片_20180316190206.jpg


关于直播间:
   直播间由直播画面和聊天室两个部分组成,“小信竞答”的聊天室使用环信聊天室,集成比较简单,基础版就能支持5000人在线聊天,增值服务版聊天室人数无上限,可以去环信官网注册一个开发者账号,创建应用将APPKEY替换成自己的;环信直播聊天室可以集成所有市场主流CDN厂商的推拉流功能(腾讯,七牛,UCloud,网宿等)。
 
环信直播聊天室特点  
This is Title
 
1、采用支持高并发的异步架构,轻松应对千万级并发请求; 各项基础服务集群化,确保系统高可用性; 系统冗余度高,容量评估体系完善,弹性扩容应对流量峰值;
2、支持各种消息格式:文字、表情、图片、声音、视频、附件、位置、扩展消息;
3、支持实时配置的消息分级策略,确保重要消息优先必达; 
4、支持直播聊天室后台管理及审核功能,提供直播相关数据统计;
5、提供智能反垃圾和自定义敏感词过滤功能;
6、快速集成,demo提供高质量代码示例,可根据运营情况随时扩展;
7、聊天室人数无上限 
小信竞答项目源码: github源码地址
 
写到最后:
   
     小信竞答源码全部开放,仅供学习和参考,如果作为商业用途,按照广电总局对网络直播答题节目管理的,需要 “网络视听许可证、主持人持证、还有通过审批发放的节目备案号”,三证缺一不可,未持有《信息网络传播视听节目许可证》的任何机构和个人,一律不得开办网络直播答题节目。
 
本月底《环信公开课第19期-直播答题开源项目》将线上讲解“小信竞答”实现思路,手把手教您从零开始搭建一个直播答题项目,扫码加入公开课微信群与大牛面对面交流。
微信图片_20180316190334.jpg

  收起阅读 »

【环信集成指南】Android sdk 的两种导入方式

环信AndroidSDK官网文档提供了两种导入方式,线上gradle远程连接导入和本地导入,线上导入方便快捷,本地导入较为灵活,今天就跟大家详细讲解下两种方式的具体实现,小伙伴们可以根据自己的业务自由选择。
1.线上gradle远程连接导入

线上导入方便快捷,直接使用环信最新版sdk,但是值得注意的是线上的sdk不包含实施音视频,也不支持easeui库线上导入


首先在项目根目录build.gradle文件的allprojects→repositories属性下加入远程库地址
maven { url "https://raw.githubusercontent. ... ot%3B }


 然后在module的build.gradle里加入以下代码
android {
//use legacy for android 6.0
useLibrary 'org.apache.http.legacy'
}
dependencies {
compile 'com.android.support:appcompat-v7:23.4.0'
//Optional compile for GCM (Google Cloud Messaging).
compile 'com.google.android.gms:play-services-gcm:9.4.0'
compile 'com.hyphenate:hyphenate-sdk:3.3.0'
}
官网文档参考
 
2.本地导入(sdk和easeui两种)

本地导入比较灵活,可以根据自己项目的开发需求根据功能去导入,比如sdk分为有实时音视频的sdk和没有实时音视频的sdk包,用户大可根据自己的需求去导入,这样稍微减少了sdk包大的问题


 easeui的导入
   本地导入也可以导入easeui库,easeui库是基于sdk开发的带ui的库,功能基本都实现,并携带写好的ui,用户在看过demo示例之后认为easeui符合自己的开发需求可以直接依赖Easeui,导入方式 
Eclipse 中导入
   先把 EaseUI 项目导入到 Eclipse 中。
在自己的项目中把 EaseUI 作为一个 library 引入。右键你的项目→Android→点击右下角的Add按钮→选中 EaseUI→OK→OK。
Android Studio 中导入
打开你的 AS 项目→File→New→Import Module→选择或输入 EaseUI 库路径→Next→Next→Finish。
 
sdk导入
   很多用户是不知道sdk是包含哪些文件的,sdk是有jar包和so包组成,其中jar包命名hyphenatechat_3.3.8.jar  
其中后边的数字3.3.8就是使用的sdk的具体版本号。
   So包有arm64-v8a、armeabi、armeabi-v7a、x86 其中x86根据自己项目需求可以去了,x86针对的是虚拟机,可以直接去了。
   armeabi和armeabi-v7a是相近似的指令集,armeabi和armeabi-v7a可以互换,v7a是增强型指令集,运行速度,效率均有所提高,他们都是32位指令,并且兼容,arm64-v8a对应arm64位指令集

3.3_.8_sdk_.png


有实时音视频和没有实时音视频包
   libs.av 文件夹:包含IM和实时音视频功能所需要的 jar 和 so 文件
   libs.lite 文件夹:无实时语音、实时视频功能的 SDK 包,如果项目中只用到聊天功能,可以把项目里的 jar 和 so 文件替换成此文件夹里的文件  文档参考

 

 

 
 
 
 
继续阅读 »
环信AndroidSDK官网文档提供了两种导入方式,线上gradle远程连接导入和本地导入,线上导入方便快捷,本地导入较为灵活,今天就跟大家详细讲解下两种方式的具体实现,小伙伴们可以根据自己的业务自由选择。
1.线上gradle远程连接导入

线上导入方便快捷,直接使用环信最新版sdk,但是值得注意的是线上的sdk不包含实施音视频,也不支持easeui库线上导入


首先在项目根目录build.gradle文件的allprojects→repositories属性下加入远程库地址
maven { url "https://raw.githubusercontent. ... ot%3B }


 然后在module的build.gradle里加入以下代码
android {
//use legacy for android 6.0
useLibrary 'org.apache.http.legacy'
}
dependencies {
compile 'com.android.support:appcompat-v7:23.4.0'
//Optional compile for GCM (Google Cloud Messaging).
compile 'com.google.android.gms:play-services-gcm:9.4.0'
compile 'com.hyphenate:hyphenate-sdk:3.3.0'
}
官网文档参考
 
2.本地导入(sdk和easeui两种)

本地导入比较灵活,可以根据自己项目的开发需求根据功能去导入,比如sdk分为有实时音视频的sdk和没有实时音视频的sdk包,用户大可根据自己的需求去导入,这样稍微减少了sdk包大的问题


 easeui的导入
   本地导入也可以导入easeui库,easeui库是基于sdk开发的带ui的库,功能基本都实现,并携带写好的ui,用户在看过demo示例之后认为easeui符合自己的开发需求可以直接依赖Easeui,导入方式 
Eclipse 中导入
   先把 EaseUI 项目导入到 Eclipse 中。
在自己的项目中把 EaseUI 作为一个 library 引入。右键你的项目→Android→点击右下角的Add按钮→选中 EaseUI→OK→OK。
Android Studio 中导入
打开你的 AS 项目→File→New→Import Module→选择或输入 EaseUI 库路径→Next→Next→Finish。
 
sdk导入
   很多用户是不知道sdk是包含哪些文件的,sdk是有jar包和so包组成,其中jar包命名hyphenatechat_3.3.8.jar  
其中后边的数字3.3.8就是使用的sdk的具体版本号。
   So包有arm64-v8a、armeabi、armeabi-v7a、x86 其中x86根据自己项目需求可以去了,x86针对的是虚拟机,可以直接去了。
   armeabi和armeabi-v7a是相近似的指令集,armeabi和armeabi-v7a可以互换,v7a是增强型指令集,运行速度,效率均有所提高,他们都是32位指令,并且兼容,arm64-v8a对应arm64位指令集

3.3_.8_sdk_.png


有实时音视频和没有实时音视频包
   libs.av 文件夹:包含IM和实时音视频功能所需要的 jar 和 so 文件
   libs.lite 文件夹:无实时语音、实时视频功能的 SDK 包,如果项目中只用到聊天功能,可以把项目里的 jar 和 so 文件替换成此文件夹里的文件  文档参考

 

 

 
 
 
  收起阅读 »