集成融云
在“打卡助手”APP中集成了即时通讯功能,采用了来自融云的第三方技术。
集成融云即时通讯的步骤: 首先从融云官网注册账号,注册你的APP并且获取一个APP key和APP Secret。然后下载融云的sdk,并依赖到项目里来。融云的sdk,有4个主要的module: IMKit, IMLib ,CallKit, CallLib。在本项目中我们主要使用了前两个module,暂时可以满足当前的需求。此外融云还提供了一些so文件,小米推送jar包,高德地图的jar包。为了能准确接到消息推送请务必兴建asset目录,并在asset下放入push_daemon(推送守护)中的相关文件。以上准备工作在照着官网步骤即可完成,在这里主要说说曾经遇到的问题。曾经没有添加高德地图jar包,在聊天的底部面板就没有出现发送定位的功能,添加上后默认出现发送定位。其次配置Manifest文件和相关的Activity,Receiver。在application里面初始化融云。这一步照着官网来。但是这里我遇到了跟原来的fileProvider冲突的问题。自己原来的android:resource="@xml/rc_file_path"和通云IMKit里的文件冲突。 采取的办法: xml文件夹下放上融云的file_path 和 自己原来的,在AndroidManifest.xml里做如下配置
接下来开始相关代码的编写。首先进行融云的连接,RongIM.connect(token, new RongIMClient.ConnectCallback(){});
然后给融云提供通讯录里的联系人。融云提供了2种方式,这里我们介绍第一种方式
List<Friend> userList = null;
private void initUserInfo() {
userList = new ArrayList<>();
userList.add(new Friend("639952043", "gui1", ""));
userList.add(new Friend("9916271905", "gui2", ""));
userList.add(new Friend("123456", "gui3", ""));
userList.add(new Friend("1234567", "gui4", ""));
}
Friend是自己写的bean类,只有3个成员变量 userId,username,图像的url。 这里是以之为例初始化了几个自己写的用户。在给融云提供用户的时候应当这样写:
RongIM.setUserInfoProvider(new RongIM.UserInfoProvider() {
@Override
public UserInfo getUserInfo(String s) {
for (Friend i : userList) {
if (i.getUserId().equals(s)) {
Log.e(TAG, i.getName());
return new UserInfo(i.getUserId(), i.getName(), Uri.parse(i.getImageUrl()));
}
}
return null;
}
},true);
这样融云才能区分出自己和对方。该方法在融云里只会被调用一次,希望在初始化会话列表Fragment之前被调用。
如果联系人列表发生更新,采用以下方式刷新给融云提供的联系人列表
for (Friend i : userList) {
if (i.getUserId().equals(s)) {
Log.e(TAG, i.getName());
RongIM.getInstance().refreshUserInfoCache(new UserInfo(i.getUserId(),i.getName(),Uri.parse(i.getImageUrl())));
}
}
另外单独修改自己的名称和头像也用这个方法来刷新。
RongIM.getInstance().refreshUserInfoCache(new UserInfo(i.getUserId(),i.getName(),Uri.parse(i.getImageUrl())));
然后初始化聊天列表,聊天列表是个Fragment,我们使用融云官方的Fragment。这里我们这样来写:
private Fragment initConversationList() {
if (mConversationListFragment == null) {
ConversationListFragment listFragment = new ConversationListFragment();
Uri uri = Uri.parse("rong://" + getApplicationInfo().packageName).buildUpon()
.appendPath("conversationlist")
.appendQueryParameter(Conversation.ConversationType.PRIVATE.getName(), "false") //设置私聊会话是否聚合显示
.appendQueryParameter(Conversation.ConversationType.GROUP.getName(), "false")//群组
.appendQueryParameter(Conversation.ConversationType.PUBLIC_SERVICE.getName(), "false")//公共服务号
.appendQueryParameter(Conversation.ConversationType.APP_PUBLIC_SERVICE.getName(), "false")//订阅号
// .appendQueryParameter(Conversation.ConversationType.SYSTEM.getName(), "false")//系统 注销掉之后不会接收来自系统的推送
.build();
mConversationsTypes = new Conversation.ConversationType[]{Conversation.ConversationType.PRIVATE,
Conversation.ConversationType.GROUP,
Conversation.ConversationType.PUBLIC_SERVICE,
Conversation.ConversationType.APP_PUBLIC_SERVICE,
// Conversation.ConversationType.SYSTEM //注销掉之后不会接收来自系统的推送
};
listFragment.setUri(uri);
mConversationListFragment = listFragment;
return listFragment;
} else {
return mConversationListFragment;
}
}
`
这里mConversationsTypes 其实可以不写,决定当前app接收哪几种会话的是.appendQueryParameter(Conversation.ConversationType.APP_PUBLIC_SERVICE.getName(), "false")//订阅号 参数1:会话类型;参数2:是否聚合显示,项目里我填的都是false。
在通讯录里找到联系人调用 RongIM.getInstance().startPrivateChat(context, targetId, targetName); 会话列表里就会显示出你们之间的会话条目。
自定义ConversationActivity。这个会话activity 中一些布局,参数你可能跟融云的不太一样需要自己去自定义一些,在本项目里我改了标题和背景颜色,控件直接在activity_conversation.xml里修改就可以。参数通过 title = intent.getData().getQueryParameter("title"); 来获取。 在IMKit的资源目录下有个rc_config.xml。 res>values>rc_config.xml, 有很多关于对话页面的配置,可以在这里进行修改。 例如:
增加底部面板的扩展功能:
以增加请假按钮为例,融云官网已经说的很清楚了,这里贴上代码:
@MessageTag(value = "app:QingJia", flag = MessageTag.ISCOUNTED | MessageTag.ISPERSISTED)
public class QingJiaMessage extends MessageContent {
//自定义的属性
private String name;
private String type;
private String startDay;
private String endDay;
public QingJiaMessage() {}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getStartDay() {
return startDay;
}
public void setStartDay(String startDay) {
this.startDay = startDay;
}
public String getEndDay() {
return endDay;
}
public void setEndDay(String endDay) {
this.endDay = endDay;
}
private Parcel mParcel;
public QingJiaMessage(Parcel parcel) {
mParcel = parcel;
setName(ParcelUtils.readFromParcel(parcel));
setType(ParcelUtils.readFromParcel(parcel));
setStartDay(ParcelUtils.readFromParcel(parcel));
setEndDay(ParcelUtils.readFromParcel(parcel));
}
public static QingJiaMessage obtain(String name, String type, String startDay, String endDay) {
QingJiaMessage qingJiaMessage = new QingJiaMessage();
qingJiaMessage.name = name;
qingJiaMessage.type = type;
qingJiaMessage.startDay = startDay;
qingJiaMessage.endDay = endDay;
return qingJiaMessage;
}
public QingJiaMessage(byte[] data) {
String jsonStr = null;
try {
jsonStr = new String(data, "UTF-8");
} catch (UnsupportedEncodingException e1) {
}
try {
JSONObject jsonObj = new JSONObject(jsonStr);
if (jsonObj.has("name")) {
name = jsonObj.optString("name");
}
if (jsonObj.has("type")) {
type = jsonObj.optString("type");
}
if (jsonObj.has("startDay")) {
startDay = jsonObj.optString("startDay");
}
if (jsonObj.has("endDay")) {
endDay = jsonObj.optString("endDay");
}
} catch (JSONException e) {
Log.e("TAG", e.getMessage());
}
}
public static final Creator<QingJiaMessage> CREATOR = new Creator<QingJiaMessage>() {
@Override
public QingJiaMessage createFromParcel(Parcel source) {
return new QingJiaMessage(source);
}
@Override
public QingJiaMessage[] newArray(int size) {
return new QingJiaMessage[size];
}
};
@Override
public byte[] encode() {
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("name", this.getName());
jsonObject.put("type", this.getType());
jsonObject.put("startDay", this.getStartDay());
jsonObject.put("endDay", this.getEndDay());
return jsonObject.toString().getBytes("UTF-8");
} catch (Exception e) {
Log.e("JSONException", e.getMessage());
}
return new byte[0];
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int flags) {
// ParcelUtils.writeToParcel(parcel, getContent());//该类为工具类,对消息中属性进行序列化
ParcelUtils.writeToParcel(parcel, getName());
ParcelUtils.writeToParcel(parcel, getType());
ParcelUtils.writeToParcel(parcel, getStartDay());
ParcelUtils.writeToParcel(parcel, getEndDay());
}
}
@ProviderTag(messageContent = QingJiaMessage.class,//(这里是你自定义的消息实体)
showReadState = true)
public class QingJiaMessageProvider extends IContainerItemProvider.MessageProvider<QingJiaMessage> {
@Override
public void bindView(View view, int i, QingJiaMessage qingJiaMessage, UIMessage uiMessage) {
ViewHolder holder = (ViewHolder) view.getTag();
holder.tvName.setText(qingJiaMessage.getName() + "");
holder.tvType.setText(qingJiaMessage.getType() + "");
holder.tvEnd.setText(qingJiaMessage.getEndDay() + "");
holder.tvStart.setText(qingJiaMessage.getStartDay() + "");
}
@Override
public Spannable getContentSummary(QingJiaMessage qingJiaMessage) {
return new SpannableString(qingJiaMessage.getName() + "");
}
@Override
public void onItemClick(View view, int position, QingJiaMessage qingJiaMessage, UIMessage uiMessage) {
}
@Override
public View newView(Context context, ViewGroup viewGroup) {
View view = LayoutInflater.from(context).inflate(R.layout.item_qingjia, null);
ViewHolder holder = new ViewHolder();
holder.tvName = (TextView) view.findViewById(R.id.tv_name);
holder.tvType = (TextView) view.findViewById(R.id.tv_qingjialeixing);
holder.tvStart = (TextView) view.findViewById(R.id.tv_start);
holder.tvEnd = (TextView) view.findViewById(R.id.tv_end_day);
view.setTag(holder);
return view;
}
private static class ViewHolder {
public TextView tvName, tvType, tvStart, tvEnd;
}
}
public class QingJiaPlugin implements IPluginModule {
@Override
public Drawable obtainDrawable(Context context) {
return context.getResources().getDrawable(R.mipmap.ic_launcher);
}
@Override
public String obtainTitle(Context context) {
return "请假";
}
@Override
public void onClick(Fragment fragment, RongExtension rongExtension) {
QingJiaMessage qingJiaMessage = QingJiaMessage.obtain("张三", "年假", "2018412", "2018413");
//发送消息
RongIM.getInstance().sendMessage(Conversation.ConversationType.PRIVATE, rongExtension.getTargetId(), qingJiaMessage, "请假条", "99999999999999", new RongIMClient.SendMessageCallback() {
@Override
public void onError(Integer integer, RongIMClient.ErrorCode errorCode) {
Log.e("tagError",errorCode.getMessage()+"");
}
@Override
public void onSuccess(Integer integer) {
Log.e("tagSu",integer+"");
}
});
}
@Override
public void onActivityResult(int i, int i1, Intent intent) {
// 获取回调的信息,然后用sendMessage发送出去
}
}
这个是扩展后的底部面板代码:
public class MyExtensionModule extends DefaultExtensionModule {
@Override
public List<IPluginModule> getPluginModules(Conversation.ConversationType conversationType) {
List<IPluginModule> pluginModules = super.getPluginModules(conversationType);
pluginModules.add(new QingJiaPlugin());
return pluginModules;
}
}
最后在application里面进行注册面板,把下面这个写在初始化融云之后
private void rongYunPlugin() {
List<IExtensionModule> moduleList = RongExtensionManager.getInstance().getExtensionModules();
IExtensionModule defaultModule = null;
if (moduleList != null) {
for (IExtensionModule module : moduleList) {
if (module instanceof DefaultExtensionModule) {
defaultModule = module;
break;
}
}
if (defaultModule != null) {
RongExtensionManager.getInstance().unregisterExtensionModule(defaultModule);
RongExtensionManager.getInstance().registerExtensionModule(new MyExtensionModule());
}
}
}
未读消息监听: //设置未读消息数变化监听器。 ```java mTagUnread = new IUnReadMessageObserver() { @Override public void onCountChanged(int i) { //聊天的tab显示未读消息的数量
} }; RongIM.getInstance().addUnReadMessageCountChangedObserver(mTagUnread, Conversation.ConversationType.PRIVATE, Conversation.ConversationType.GROUP, Conversation.ConversationType.PUBLIC_SERVICE, Conversation.ConversationType.APP_PUBLIC_SERVICE);
>>会话界面操作监听:
```java
RongIM.setConversationListBehaviorListener(new RongIM.ConversationListBehaviorListener() {
@Override
public boolean onConversationPortraitClick(Context context, Conversation.ConversationType conversationType, String s) {
return false;
}
@Override
public boolean onConversationPortraitLongClick(Context context, Conversation.ConversationType conversationType, String s) {
return false;
}
@Override
public boolean onConversationLongClick(Context context, View view, UIConversation uiConversation) {
return false;
}
@Override
public boolean onConversationClick(Context context, View view, UIConversation uiConversation) {
return false;
}
});