在您阅读此文档之前,我们假定您已经具备了基础的 Android 应用开发经验,并能够理解相关基础概念。
如有疑问,欢迎加入 美洽 SDK 开发 QQ 群:748720588
仓库地址:GitHub Android
ScreenShot
集成美洽 SDK
Environment Required
- JDK7+
AndroidStudio
implementation 'com.meiqia:androidx:4.0.3'
implementation 'com.github.bumptech.glide:glide:4.9.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
使用美洽
1.初始化
MQConfig.init(this, "Your Appkey", new OnInitCallback() {
@Override
public void onSuccess(String clientId) {
Toast.makeText(MainActivity.this, "init success", Toast.LENGTH_SHORT).show();
}
@Override
public void onFailure(int code, String message) {
Toast.makeText(MainActivity.this, "int failure", Toast.LENGTH_SHORT).show();
}
});
如果您不知道 Appkey ,请使用美洽管理员帐号登录 美洽,在「设置」 -> 「SDK」 菜单中查看。如下图:
2.启动对话界面
初始化成功后,就可以直接启动对话界面
Intent intent = new MQIntentBuilder(this).build();
startActivity(intent);
3.启动留言表单界面
目前是两种模式: (1) 完全对话模式 无机器人时:如果当前客服不在线,直接聊天界面输入就是留言,客服上线后能够直接回复,如果客服在线,则进入正常客服对话模式。 有机器人时:如果当前客服不在线时,直接聊天界面输入的话,还是由机器人回答,顾客点击留言就会跳转到表单。 (2) 单一表单模式 不管客服是否在线都会进入表单,顾客提交后,不会有聊天的界面。这种主要用于一些 App 只需要用户反馈,不需要直接回复的形式。
startActivity(new Intent(this, MQMessageFormActivity.class));
4.Android M 权限处理
如果你的 App 需要兼容 Android M,需要处理权限问题。 参考 Demo
5.Android O 权限处理
如果你的 App 需要兼容 Android O,需要在 App 后台的时候确保关闭美洽服务。
MQManager.getInstance(context).closeMeiqiaService();
6.常见使用场景
开发者的 App 有自己的账号系统,希望每个账号对应不同的顾客,有不同的聊天记录。那就需要开发者在启动对话的时候,绑定账号:
Intent intent = new MQIntentBuilder(this)
.setCustomizedId("开发者的 id") // 相同的 id 会被识别为同一个顾客
.build();
startActivity(intent);
开发者希望顾客上线的时候,能够上传(或者更新)一些用户的自定义信息:
HashMap<String, String> clientInfo = new HashMap<>();
clientInfo.put("name", "富坚义博");
clientInfo.put("avatar", "https://s3.cn-north-1.amazonaws.com.cn/pics.meiqia.bucket/1dee88eabfbd7bd4");
clientInfo.put("gender", "男");
clientInfo.put("tel", "1300000000");
clientInfo.put("技能1", "休刊");
HashMap<String, String> updateInfo = new HashMap<>();
updateInfo.put("name", "update name");
Intent intent = new MQIntentBuilder(this)
.setClientInfo(clientInfo) // 设置顾客信息 PS: 这个接口只会生效一次,如果需要更新顾客信息,需要调用更新接口
// .updateClientInfo(updateInfo) // 更新顾客信息 PS: 如果客服在工作台更改了顾客信息,更新接口会覆盖之前的内容
.build();
startActivity(intent);
指定客服分配
Intent intent = new MQIntentBuilder(this)
.setScheduledAgent(agentId) // agentId 可以从工作台查询
.build();
startActivity(intent);
指定客服分组分配
Intent intent = new MQIntentBuilder(this)
.setScheduledGroup(groupId) // groupId 可以从工作台查询
.build();
startActivity(intent);
设置预发送消息
Intent intent = new MQIntentBuilder(this)
.setPreSendTextMessage("我是预发送文字消息")
.setPreSendImageMessage(new File("预发送图片的路径"))
.setPreSendProductCardMessage(productCardBundle) // 预发送商品卡片
.build();
startActivity(intent);
// productCardBundle 构造
Bundle productCardBundle = new Bundle();
productCardBundle.putString("title", "我是标题");
productCardBundle.putString("description", "我是描述文字");
productCardBundle.putString("pic_url", "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fi04.c.aliimg.com%2Fimg%2Fibank%2F2013%2F211%2F016%2F791610112_758613609.jpg&refer=http%3A%2F%2Fi04.c.aliimg.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1633076260&t=a46d823f8bb9fd773e93e2a7ab3f481e");
productCardBundle.putString("product_url", "https://www.baidu.com");
productCardBundle.putLong("sales_count", 1000);
设置监听 MQConversationActivity 生命周期的回调接口
MQConfig.setActivityLifecycleCallback(new MQSimpleActivityLifecycleCallback() {
});
设置用户事件
MQClientEvent clientEvent = new MQClientEvent();
// 用户添加新产品
clientEvent.setEvent("add_product", "id_xxx"); // 事件字段需要先在后台创建
MQManager.getInstance().setClientEvent(clientEvent);
7.常见问题列表
- java.lang.NoClassDefFoundError: com.meiqia.core.xx
没有依赖 okhttp3.5.0 或者 以上版本,检查依赖设置 - code == 400 track_id 错误
如果需要绑定用户 id,请使用 setCustomizedId 接口;如果还是有问题,就换一个 id 绑定再试试
- 客服名字显示 null
更新最新版 SDK - java.lang.NoSuchMethodError: No Virtual method displayImage xxxx
如果使用的是 glide 4.x ,可以参考 https://github.com/Meiqia/MeiqiaSDK-Android/blob/master/imageloader/MQGlideImageLoader4.java - 后台改了配置,SDK 不生效
SDK 的配置不是立即生效,会至少间隔 15 分钟刷新一次,刷新后下次生效。如果想要立即看到配置改变的效果,可以卸载应用重新安装。
API 接口介绍
获取 MQManager 实例后,
MQManager mqManager = MQManager.getInstacne(context);
就可以调用下面的接口:
设置当前 Client 上线
初始化 SDK 成功后,会默认生成一个顾客,如果没有更改过顾客 id,将以默认生成的顾客上线。
/**
* 设置当前 Client 上线
*
* @param onlineCallback 回调
*/
setCurrentClientOnline(final OnClientOnlineCallback onlineCallback)
绑定美洽 id 并设置上线
开发者可通过 【获取当前顾客的 id】 接口,取得顾客 id ,保存到开发者的服务端,以此来绑定美洽顾客和开发者用户系统。 如果开发者保存了美洽的顾客 id,可调用如下接口让其上线。调用此接口后,当前可用的顾客即为开发者传的顾客 id。
/**
* 绑定美洽 id,并设置上线
*
* @param mqClientId 美洽 id
* @param onlineCallback 回调接口
*/
setClientOnlineWithClientId(String mqClientId, final OnClientOnlineCallback onlineCallback)
MQConversationActivity.class 内部调用了此接口,所以可以直接通过 MQIntentBuilder 来构造 intent。
Example:
// 假设 meiqia_id 是美洽生成的顾客 id
Intent intent = new MQIntentBuilder(this)
.setClientId(meiqia_id)
.build();
startActivity(intent);
绑定自定义 id 并设置上线
如果开发者不愿保存「美洽顾客 id」来绑定自己的用户系统,也可以将自己的用户 id当做参数,进行顾客的上线,美洽将会为开发者绑定一个顾客,下次开发者直接调用如下接口,就能让这个绑定的顾客上线。 调用此接口后,当前可用的顾客即为该自定义 id 对应的顾客 id。
特别注意:传给美洽的自定义 id 不能为自增长的,否则非常容易受到中间人攻击,此情况的开发者建议保存美洽顾客 id,自定义id为长度不超过 32 位的字符串。
/**
* 绑定自定义 id,并设置上线
*
* @param customizedId 自定义 id
* @param onlineCallback 回调接口
*/
setClientOnlineWithCustomizedId(String customizedId, final OnClientOnlineCallback onlineCallback)
MQConversationActivity.class 内部调用了此接口,所以可以直接通过 MQIntentBuilder 来构造 intent。
Example:
// 假设 developer@dev.com 是开发者的用户 id
Intent intent = new MQIntentBuilder(this)
.setCustomizedId("developer@dev.com") // 相同的 id 会被识别为同一个顾客
.build();
startActivity(intent);
指定客服或者分组
美洽默认会按照管理员设置的分配方式智能分配客服,但如果需要让来自 App 的顾客指定分配给某个客服或者某组客服。
/**
* 指定客服或者分组,默认分配全企业
*
* @param agentId 指定客服的 id,不指定传 null
* @param groupId 指定分组的 id,不指定传 null
* @param scheduleRule 分配规则 MQScheduleRule.java
*/
setScheduledAgentOrGroupWithId(String agentId, String groupId, MQScheduleRule scheduleRule)
分配规则:
MQScheduleRule.REDIRECT_NONE // 指定分配客服失败,则进入留言
MQScheduleRule.REDIRECT_GROUP // 分配给组内的人,分配失败,则进入留言
MQScheduleRule.REDIRECT_ENTERPRISE // 分配给企业随机一个人,分配失败,则进入留言 (默认)
MQConversationActivity.class 内部调用了此接口,所以可以直接通过 MQIntentBuilder 来构造 intent。
Example:
Intent intent = new MQIntentBuilder(this)
.setScheduledAgent(agentId) // agentId 可以从工作台查询
.setScheduledGroup(groupId) // groupId 可以从工作台查询
.setScheduleRule(MQScheduleRule.REDIRECT_ENTERPRISE) // 默认
.build();
startActivity(intent);
注意:
- 该选项需要在用户上线前设置。
- 客服组 ID 和客服 ID 可以通过管理员帐号在后台的「设置」中查看。
设置顾客离线
设置顾客离线后,将停止监听客服发送的消息,开发者不会再监听到即时消息广播。
如果设置了顾客离线,并且在美洽工作台配置了推送服务器,则客服发送的消息将会发送给开发者的服务端。
美洽建议:如果退出界面后需要监听客服消息,不设置顾客离线,这样开发者仍能监听到收到消息的广播,以便提醒顾客有新消息。
/**
* 设置顾客离线
* 需要初始化成功后才能调用
* 如果设置了顾客离线,则客服发送的消息将会发送给开发者的推送服务器
*/
setClientOffline()
发送文字消息 / 图片消息 / 语音消息
/**
* 发送文字消息
*
* @param content 消息内容
* @param onMessageSendCallback 消息状态回调
*/
sendMQTextMessage(String content, final OnMessageSendCallback onMessageSendCallback)
/**
* 发送图片消息
*
* @param localPath 图片的本地路径
* @param onMessageSendCallback 消息状态回调
*/
sendMQPhotoMessage(String localPath, final OnMessageSendCallback onMessageSendCallback)
/**
* 发送语音消息
*
* @param localPath 语音的本地路径
* @param onMessageSendCallback 消息状态回调
*/
sendMQVoiceMessage(String localPath, final OnMessageSendCallback onMessageSendCallback)
从服务器获取历史消息
/**
* 从服务器获取历史消息
*
* @param lastMessageCreateOn 获取该日期之前的消息
* @param length 获取的消息长度
* @param onGetMessageListCallback 回调
*/
getMQMessageFromService(final long lastMessageCreateOn, final int length, final OnGetMessageListCallback onGetMessageListCallback)
从本地获取历史消息
/**
* 从服务器获取历史消息
*
* @param lastMessageCreateOn 获取该日期之前的消息
* @param length 获取的消息长度
* @param onGetMessageListCallback 回调
*/
getMQMessageFromDatabase(final long lastMessageCreateOn, final int length, final OnGetMessageListCallback onGetMessageListCallback)
设置用户的设备唯一标识
/**
* 设置用户的设备唯一标识
*
* @param token 唯一标识
*/
registerDeviceToken(String token, OkHttpUtils.OnRegisterDeviceTokenCallback onRegisterDeviceTokenCallback)
App 进入后台后,美洽推送给开发者服务端的消息数据格式中,会有 deviceToken 的字段。
美洽推送消息给开发者服务端的数据格式,可参考【推送消息数据结构】。
开发者自定义当前顾客的信息
/**
* 开发者自定义当前顾客的信息,用于展示给客服
*
* @param clientInfo 顾客信息
* @param onClientInfoCallback 回调
*/
setClientInfo(Map<String, String> clientInfo, OnClientInfoCallback onClientInfoCallback)
功能效果展示:
为了让客服能更准确帮助用户,开发者可上传不同用户的属性信息。示例如下:
Map<String, String> info = new HashMap<>();
info.put("name", "富坚义博");
info.put("avatar", "https://s3.cn-north-1.amazonaws.com.cn/pics.meiqia.bucket/1dee88eabfbd7bd4");
info.put("gender", "男");
info.put("tel", "111111");
info.put("技能1", "休刊");
info.put("技能2", "外出取材");
info.put("技能3", "打麻将");
MQManager.getInstance(context).setClientInfo(info, new OnClientInfoCallback());
MQConversationActivity.class 内部调用了此接口,所以可以直接通过 MQIntentBuilder 来构造 intent。
HashMap<String, String> clientInfo = new HashMap<>();
clientInfo.put("name", "富坚义博");
clientInfo.put("avatar", "https://s3.cn-north-1.amazonaws.com.cn/pics.meiqia.bucket/1dee88eabfbd7bd4");
clientInfo.put("gender", "男");
clientInfo.put("tel", "1300000000");
clientInfo.put("技能1", "休刊");
Intent intent = new MQIntentBuilder(this)
.setClientInfo(clientInfo)
.build();
startActivity(intent);
以下字段是美洽定义好的,开发者可通过上方提到的接口,直接对下方的字段进行设置:
Key | 说明 |
name | 真实姓名 |
gender | 性别 |
age | 年龄 |
tel | 电话 |
weixin | 微信 |
微博 | |
address | 地址 |
邮件 | |
avatar | 头像 URL |
tags | 标签,数组形式,且必须是企业中已经存在的标签 |
comment | 备注 |
获取当前正在接待的客服信息
/**
* 获取当前正在接待的客服信息
*
* @return 如果存在,返回当前客服信息;不存在,返回 null
*/
getCurrentAgent()
获取当前顾客的 id
/**
* 获取当前顾客的顾客 id,开发者可保存该顾客id,下次使用 setClientOnlineWithMQClientId 接口来让该顾客登陆美洽客服系统
*
* @return 当前顾客 id
*/
getCurrentClientId()
获取一个新的顾客
/**
* 获取一个新的顾客
*
* @param onGetMQClientIdCallBack 回调
*/
createMQClient(OnGetMQClientIdCallBackOn onGetMQClientIdCallBack)
如果开发者想初始化一个新的顾客,可调用此接口。
该顾客没有任何历史记录及用户信息。
更新消息阅读状态
/**
* 更新消息阅读状态
*
* @param messageId 消息id
* @param isRead 将替换的状态
*/
updateMessage(long messageId, boolean isRead)
结束当前对话
/**
* 结束当前对话
*
* @param onEndConversationCallback 回调
*/
endCurrentConversation(OnEndConversationCallback onEndConversationCallback)
给客服发送「正在输入」
/**
* 将用户正在输入的内容,提供给客服查看。该接口没有调用限制,但每1秒内只会向服务器发送一次数据
*
* @param content 正在输入的内容
*/
sendClientInputtingWithContent(String content)
切换当前顾客
/**
* 切换当前顾客
*
* @param clientIdOrCustomizedId clientId 或者 customized
* @param simpleCallback 回调
*/
MQManager.getInstance(context).setCurrentClient(String clientIdOrCustomizedId, SimpleCallback simpleCallback);
获取未读消息
退出界面后收到的消息,都将算作未读消息。
/**
* 获取当前 Client 的未读消息
*
* @param onGetMessageListCallback 回调
*/
MQManager.getInstance(context).getUnreadMessages(new OnGetMessageListCallback());
/**
* 获取指定 ClientId 或者 customized 顾客的未读消息
*
* @param clientIdOrCustomizedId clientId 或者 customized
* @param onGetMessageListCallback 回调
*/
MQManager.getInstance(context).getUnreadMessages(String clientIdOrCustomizedId, new OnGetMessageListCallback());
接收即时消息
在未开启【离线消息推送】的情况下,开发者可以通过注册一个 BroadcastReceiver ,监听广播
注意:必须通过 LocalBroadcastManager 注册 和 取消注册 BroadcastReceiver。
Example:
// 注册
LocalBroadcastManager.getInstance(this).registerReceiver(messageReceiver, intentFilter);
// 取消注册
LocalBroadcastManager.getInstance(this).unregisterReceiver(messageReceiver);
BroadcastReceiver:
public class MessageReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 获取 ACTION
final String action = intent.getAction();
// 接收新消息
if (MQMessageManager.ACTION_NEW_MESSAGE_RECEIVED.equals(action)) {
// 从 intent 获取消息 id
String msgId = intent.getStringExtra("msgId");
// 从 MCMessageManager 获取消息对象
MQMessageManager messageManager = MQMessageManager.getInstance(context);
MQMessage message = messageManager.getMQMessage(msgId);
// do something
}
// 客服正在输入
else if (MQMessageManager.ACTION_AGENT_INPUTTING.equals(action)) {
// do something
}
// 客服转接
else if (MQMessageManager.ACTION_AGENT_CHANGE_EVENT.equals(action)) {
// 获取转接后的客服
MQAgent mqAgent = messageManager.getCurrentAgent();
// do something
}
}
}
获取 SDK 版本号
/**
* 获取 SDK 版本号
*/
getMeiqiaSDKVersion()
离线消息推送
当前仅支持一种推送方案,即美洽服务端发送消息至开发者的服务端,开发者再推送消息到 App。
设置接收推送的服务器地址
推送消息将会发送至开发者的服务器。
设置服务器地址,请使用美洽管理员帐号登录 美洽,在「设置」 -> 「SDK」中设置。
设置用户的设备唯一标识
/**
* 设置用户的设备唯一标识
*
* @param token 唯一标识
*/
registerDeviceToken(String token, OkHttpUtils.OnRegisterDeviceTokenCallback onRegisterDeviceTokenCallback)
关闭美洽服务,美洽推送给开发者服务端的消息数据格式中,会有 deviceToken 的字段。
关闭美洽服务
关闭服务后,将停止监听消息,此时美洽服务端将会推送消息给开发者提供的消息推送服务端,如下代码:
MQManager.getInstance(context).closeMeiqiaService();
美洽建议:在 App 后台以后,关闭美洽服务。App 进入前台,如果需要监听客服消息,再开启美洽服务。
开启美洽服务
开启服务后,将重新监听消息,此时美洽服务端将不会推送消息给开发者提供的消息推送服务端,如下代码:
MQManager.getInstance(context).openMeiqiaService();
美洽建议:在 App 后台以后,关闭美洽服务。App 进入前台,如果需要监听客服消息,再开启美洽服务。
推送消息数据结构
当有消息需要推送时,美洽服务器会向开发者设置的服务器地址发送推送消息,方法类型为 POST,数据格式为 JSON 。
发送的请求格式介绍:
request.header.authorization 为数据签名。
request.body 为消息数据,数据结构为:
Key | 说明 |
messageId | 消息 id |
content | 消息内容 |
messageTime | 发送时间 |
fromName | 发送人姓名 |
deviceToken | 发送对象设备的 deviceToken,格式为字符串 |
clientId | 发送对象的顾客 id |
customizedId | 开发者传的自定义 id |
contentType | 消息类型 – text/photo/audio |
deviceOS | 设备系统 |
customizedData | 开发者上传的自定义的属性 |
开发者可以根据请求中的签名,对推送消息进行数据验证,美洽提供了 Java、Python、Ruby、JavaScript、PHP
5种语言的计算签名的代码,具体请移步 美洽 SDK 3.0 推送的数据结构签名算法。
*请注意,如果你是旧版 SDK 更换到新版 SDK,我们的推送数据格式统一成了 JSON 格式,具体请参见离线消息推送
自定义UI
- 配置标题文本对其方式
自定义属性说明 | 说明 | 默认值 |
MQConfig.ui.titleGravity | 居中对齐 MQTitleGravity.CENTER、居左对齐 MQTitleGravity.LEFT | MQTitleGravity.CENTER |
- 通过在自己工程中覆盖相应资源ID的方式配置自定义 UI「这里只列出常用自定义属性,如需特殊定制,请查看美洽 SDK 源码里相应的资源 ID 并在自己工程中覆盖」
自定义属性名 | 说明 |
mq_activity_bg | Activity 背景颜色资源 |
mq_activity_title_textColor | 标题栏文字颜色资源 |
mq_chat_left_textColor | 聊天界面左边气泡文字颜色资源 |
mq_chat_right_textColor | 聊天界面右边气泡文字颜色资源 |
mq_chat_left_bubble | 聊天界面左边气泡背景颜色资源 |
mq_chat_right_bubble | 聊天界面右边气泡背景颜色资源 |
mq_ic_back.png | 左上角返回箭头图片资源 |
- 以下属性可通过 Java 代码的方式配置「不建议使用这种方式」
自定义属性名 | 说明 |
MQConfig.ui.backArrowIconResId | 标题栏返回箭头图片的资源 ID |
MQConfig.ui.titleBackgroundResId | 标题栏背景颜色的资源 ID |
MQConfig.ui.titleTextColorResId | 标题栏文字颜色的资源 ID |
MQConfig.ui.leftChatBubbleColorResId | 聊天界面左边气泡背景颜色的资源 ID |
MQConfig.ui.rightChatBubbleColorResId | 聊天界面右边气泡背景颜色的资源 ID |
MQConfig.ui.leftChatTextColorResId | 聊天界面左边气泡文字颜色的资源 ID |
MQConfig.ui.rightChatTextColorResId | 聊天界面右边气泡文字颜色的资源 ID |
自定义业务
属性说明 | 说明 | 默认值 |
MQConfig.isVoiceSwitchOpen | 是否开启语音功能 | 默认为 true |
MQConfig.isSoundSwitchOpen | 是否开启声音提示 | 默认为 true |
MQConfig.isLoadMessagesFromNativeOpen | 是否加载本地数据 | 默认为 false |
MQConfig.isShowClientAvatar | 是否显示客户头像 | 默认为 false |
代码混淆
// OkHttp相关
-keepattributes Signature
-keepattributes *Annotation*
-keep class com.squareup.okhttp3.** { *; }
-keep interface com.squareup.okhttp3.** { *; }
-dontwarn com.squareup.okhttp3.**
// Okio相关
-keep class sun.misc.Unsafe { *; }
-dontwarn java.nio.file.*
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
-dontwarn okio.**
// UIL相关
-keep class com.nostra13.universalimageloader.** { *; }
-keepclassmembers class com.nostra13.universalimageloader.** {*;}
-dontwarn com.nostra13.universalimageloader.**
// Glide相关
-keep class com.bumptech.glide.Glide { *; }
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
**[] $VALUES;
public *;
}
-dontwarn com.bumptech.glide.**
// Picasso相关
-keep class com.squareup.picasso.Picasso { *; }
-dontwarn com.squareup.okhttp.**
-dontwarn com.squareup.picasso.**
// xUtils3相关
-keepattributes Signature,*Annotation*
-keep public class org.xutils.** {
public protected *;
}
-keep public interface org.xutils.** {
public protected *;
}
-keepclassmembers class * extends org.xutils.** {
public protected *;
}
-keepclassmembers @org.xutils.db.annotation.* class * {*;}
-keepclassmembers @org.xutils.http.annotation.* class * {*;}
-keepclassmembers class * {
@org.xutils.view.annotation.Event <methods>;
}
-dontwarn org.xutils.**