环信官方Demo源码分析及SDK简单应用-ChatDemoUI3.0
环信官方Demo源码分析及SDK简单应用-LoginActivity
环信官方Demo源码分析及SDK简单应用-主界面的三个fragment-会话界面
环信官方Demo源码分析及SDK简单应用-主界面的三个fragment-通讯录界面
环信官方Demo源码分析及SDK简单应用-主界面的三个fragment-设置界面
环信官方Demo源码分析及SDK简单应用-IM集成开发详案及具体代码实现
前言
手头工作上,正好需要在已有的两个App上集成IM功能。且迭代流程中是有开发详案这一项的。就分享给大家,边写开发详案边写代码。好吧,废话不多说,我们一起来学习如何集成和改造这款简单易用而又非常强大的环信SDK。
具体步骤
迭代点
需要做的功能点及工作
1.集成环信
2.围绕UE和UI进行编码
房源详情增加咨询按钮,点击进入咨询对话框,并且将房源信息带入对话框。
消息中心
主界面TABBAR点击消息进入该界面
包含系统消息入库和咨询用户列表
从TABBAR点击“消息”图标进入本页面后,可以在本页面进入”系统消息“,并且将咨询过的用户会话显示在本页,长按任意一条会话,提示删除当前会话确定。
列表排序:“系统通知“仍然在最上面的位置,不受排序影响
咨询排序:按最后聊天时间倒序排列,咨询列表默认显示20条,多了拖动加载分页数据。
无咨询用户时,只显示”系统通知“入口即可。
咨询列表:长按可删除当前聊天对象,需要有确认对话框(只删除会话,不删除聊天记录)
根据UE和UI改造聊天窗口(EaseUI库)
注意以下几点
从房源详情页进入时,就返回房源详情页,从消息中心进入时,就返回消息中心。
显示当前咨询人的经纪人姓名,并显示当前咨询的对象的在线状态(在线/离线)
标题头中的电话按钮可以直接拨打电话
对于从房源详情进入时带入的房源详情类型的聊天条目,经纪人可以点击查看该房源在STM中的详情。
显示经纪人照片上传的照片,如果经纪人没有上传照片,就显示一个经纪人的占位图(要区别于用户的占位图)
当前用户头像默认显示当前用户的头像,如果没有头像,就显示一个默认的占位图
聊天内容上长按可复制
发送的是手机号码时可以直接打电话。
思路
先做加法,再做减法
我们来按照原有代码改造和设计环信SDK部分相关代码改造,两个部分来做工作。将具体的功能点拆分并给出实现。
我们在Demo上修改,修改完成后剔除无关代码抽取成独立的我们需要的相关代码。整个工作也就结束了。
通过之前的代码阅读,我们知道整个Demo是一个相对完整的App,而我们实际工作中集成个im基本出不了这个范围。
就好比这次迭代也是。
因为实际整个涉及的只有会话列表和聊天界面,我们主要关注ConversationListFragment与ChatActivity就行了。
实现
SeeHouse相关改造
原有代码改造
房源详情增加咨询按钮,点击进入咨询对话框,并且将房源信息带入对话框。
主界面TABBAR点击消息进入该界面
涉及环信SDK部分相关代码改造
包含系统消息入库和咨询用户列表
同列表,不同type类型区分,并置顶系统消息
从TABBAR点击“消息”图标进入本页面后,可以在本页面进入”系统消息“,并且将咨询过的用户会话显示在本页,长按任意一条会话,提示删除当前会话确定。
直接贴过去,Demo已经实现。
列表排序:“系统通知“仍然在最上面的位置,不受排序影响根据Type来判断类型,并排序置顶。
咨询排序:按最后聊天时间倒序排列,咨询列表默认显示20条,多了拖动加载分页数据。sort算法改一下,看下本身是否带分页。
无咨询用户时,只显示”系统通知“入口即可。
无需实现。
咨询列表:长按可删除当前聊天对象,需要有确认对话框(只删除会话,不删除聊天记录)
环信的哥哥们已经帮我们实现了。但是根据要求呢,我没只需要删除会话,所以我们把第二项注释掉。
我们把对应处的判断代码和对应的menu文件em_delete_message中的标签给注释掉。看效果。
从房源详情页进入时,就返回房源详情页,从消息中心进入时,就返回消息中心。
直接finish();
显示当前咨询人的经纪人姓名,并显示当前咨询的对象的在线状态(在线/离线)官方的EaseUi是这么说的
我们来找下EaseTitleBar
我们来看下他的布局
http://schemas.android.com/apk/res/android"android:id="@+id/root" android:layout_width="match_parent" android:layout_height="@dimen/height_top_bar" android:background="@color/top_bar_normal_bg" android:gravity="center_vertical" >
其实有title和rightview的。
我们来对title加入一个是否在线的状态
1.获取token
MacBook:~ mli$ curl -X POST "https://a1.easemob.com/1177170 ... ot%3B-d '{"grant_type":"client_credentials","client_id":"YXA6vcNInEeatzGVyK0tA","client_secret":"YXA6YACo7qumFfgYdWher3D3Cs"}'
{"access_token":"YWMtOT73nvcIEeaPCCuTQsCAAAVuOB_MQchxsIsxVJFXsW6lZ8f2l__xn8","expires_in":5168429,"application":"bd09c370-d227-11e6-adcc-65700322b4b4"}
2.拿token获取用户状态
MacBook:~ mli$ curl -X GET -i -H "Authorization: Bearer YWMtOT73nvcIEeaPCCuTQsC6kwAAAVuOB_MQchxsIsxybVJFXsW6lZ8f2l__xn8" "https://a1.easemob.com/1177170 ... ot%3BHTTP/1.1 200 OKServer: Tengine/2.0.3Date: Mon, 20 Feb 2017 05:24:00 GMTContent-Type: application/json;charset=UTF-8Transfer-Encoding: chunkedConnection: keep-aliveAccess-Control-Allow-Origin: *Set-Cookie: rememberMe=deleteMe; Path=/; Max-Age=0; Expires=Sun, 19-Feb-2017 05:24:00 GMT{ "action" : "get", "uri" : "http://a1.easemob.com/11771701 ... ot%3B, "entities" :[], "data" : { "2" : "offline" }, "timestamp" : 1487568240699, "duration" : 25, "count" : 0}MacBook:~ mli$ curl -X GET -i -H "Authorization: Bearer YWMtOT73nvcIEeaPCCuCkwAAAVuOB_MQchxsIJFXsW6lZ8f2l__xn8" "https://a1.easemob.com/1177170 ... ot%3BHTTP/1.1 200 OKServer: Tengine/2.0.3Date: Mon, 20 Feb 2017 05:24:08 GMTContent-Type: application/json;charset=UTF-8Transfer-Encoding: chunkedConnection: keep-aliveAccess-Control-Allow-Origin: *Set-Cookie: rememberMe=deleteMe; Path=/; Max-Age=0; Expires=Sun, 19-Feb-2017 05:24:08 GMT{ "action" : "get", "uri" : "http://a1.easemob.com/11771701 ... ot%3B, "entities" :[], "data" : { "1" : "online" }, "timestamp" : 1487568248135, "duration" : 14, "count" : 0MacBook:~ mli$
我们可以看到2是离线,1是在线的。
注意一点
所以昵称是在咱自己的体系的。可以从现有的App里提取,如果有的话。
我们知道从列表ConversationListFragment->ChatActivity->ChatFragment
那么如何接受和发送自己与他人的头像和昵称呢?
我们来玩这个ChatFragment
在OnSetMessageAttributes中,设置我们要发送时的消息扩展属性。
那么接收怎么办呢,我们来看下DemoHelper中的getUserInfo()方法。
无聊的用鄙人蹩脚的英文写了一把注释。英文若是写的不对就不对吧。
标题头中的电话按钮可以直接拨打电话
修改删除按钮为打电话,并改动相关代码
显示经纪人照片上传的照片,如果经纪人没有上传照片,就显示一个经纪人的占位图(要区别于用户的占位图)
修改原demo
当前用户头像默认显示当前用户的头像,如果没有头像,就显示一个默认的占位图
修改原demo。
聊天内容上长按可复制
自带了,后面我们可能需要去掉转发。
发送的是手机号码时可以直接打电话。
我们再长按后判断其是否为电话号码,如果是添加一项拨打电话。
引用关系是这样的
ChatFragment->ContextMenuActivity->em_context_menu_for_location.xml
最后调回ChatFragment的onActivityResult
我们来改em_context_menu_for_location.xml
http://schemas.android.com/apk/res/android"android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_marginRight="20dp" android:gravity="center_horizontal" android:orientation="vertical" > -->
再来改ContextMenuActivity
/** * Copyright (C) 2016 Hyphenate Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *http://www.apache.org/licenses/LICENSE-2.0* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.hyphenate.chatuidemo.ui;import android.content.Intent;import android.os.Bundle;import android.text.TextUtils;import android.view.MotionEvent;import android.view.View;import android.widget.TextView;import com.easemob.redpacketsdk.constant.RPConstant;import com.hyphenate.chat.EMMessage;import com.hyphenate.chatuidemo.Constant;import com.hyphenate.chatuidemo.R;public class ContextMenuActivity extends BaseActivity { public static final int RESULT_CODE_COPY = 1; public static final int RESULT_CODE_DELETE = 2; public static final int RESULT_CODE_FORWARD = 3; public static final int RESUTL_CALL_PHONE = 4; String phoneNumber; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); EMMessage message = getIntent().getParcelableExtra("message"); boolean isChatroom = getIntent().getBooleanExtra("ischatroom", false); phoneNumber = getIntent().getStringExtra("phone_number"); int type = message.getType().ordinal(); if (type == EMMessage.Type.TXT.ordinal()) { if(message.getBooleanAttribute(Constant.MESSAGE_ATTR_IS_VIDEO_CALL, false) || message.getBooleanAttribute(Constant.MESSAGE_ATTR_IS_VOICE_CALL, false) //red packet code : 屏蔽红包消息、转账消息的转发功能 || message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_RED_PACKET_MESSAGE, false) || message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_TRANSFER_PACKET_MESSAGE, false)){ //end of red packet code setContentView(R.layout.em_context_menu_for_location); }else if(message.getBooleanAttribute(Constant.MESSAGE_ATTR_IS_BIG_EXPRESSION, false)){ setContentView(R.layout.em_context_menu_for_image); }else{ //for text content setContentView(R.layout.em_context_menu_for_text); //for call phone number TextView callPhone = (TextView) findViewById(R.id.call_phone); if(!TextUtils.isEmpty(phoneNumber)){ callPhone.setVisibility(View.VISIBLE); callPhone.setText("拨打电话:" + phoneNumber); }else{ callPhone.setVisibility(View.GONE); } } } else if (type == EMMessage.Type.LOCATION.ordinal()) { setContentView(R.layout.em_context_menu_for_location); } else if (type == EMMessage.Type.IMAGE.ordinal()) { setContentView(R.layout.em_context_menu_for_image); } else if (type == EMMessage.Type.VOICE.ordinal()) { setContentView(R.layout.em_context_menu_for_voice); } else if (type == EMMessage.Type.VIDEO.ordinal()) { setContentView(R.layout.em_context_menu_for_video); } else if (type == EMMessage.Type.FILE.ordinal()) { setContentView(R.layout.em_context_menu_for_location); } if (isChatroom //red packet code : 屏蔽红包消息、转账消息的撤回功能 || message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_RED_PACKET_MESSAGE, false) || message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_TRANSFER_PACKET_MESSAGE, false)) { //end of red packet code View v = (View) findViewById(R.id.forward); if (v != null) { v.setVisibility(View.GONE); } } } @Override public boolean onTouchEvent(MotionEvent event) { finish(); return true; } public void copy(View view){ setResult(RESULT_CODE_COPY); finish(); } public void delete(View view){ setResult(RESULT_CODE_DELETE); finish(); } public void forward(View view){ setResult(RESULT_CODE_FORWARD); finish(); } public void call(View view) { Intent it = new Intent(); it.putExtra("phone_number",phoneNumber); setResult(RESUTL_CALL_PHONE,it); finish(); }}
再来判断内容是否为电话号码
String phoneNumber="";
if(isPhoneNumber(content)){
phoneNumber = content;
}
// no message forward when in chat room
startActivityForResult((new Intent(getActivity(), ContextMenuActivity.class)).putExtra("message",message)
//if message's context is a phone number ,make it can be call it.
.putExtra("ischatroom", chatType == EaseConstant.CHATTYPE_CHATROOM).putExtra("phone_number",phoneNumber),
REQUEST_CODE_CONTEXT_MENU);
onActivityResult部分
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_CONTEXT_MENU) {
//for Context MenuActivity Result
switch (resultCode) {
case ContextMenuActivity.RESULT_CODE_COPY: // copy
clipboard.setPrimaryClip(ClipData.newPlainText(null,
((EMTextMessageBody) contextMenuMessage.getBody()).getMessage()));
break;
case ContextMenuActivity.RESULT_CODE_DELETE: // delete
conversation.removeMessage(contextMenuMessage.getMsgId());
messageList.refresh();
break;
// case ContextMenuActivity.RESULT_CODE_FORWARD: // forward
// Intent intent = new Intent(getActivity(), ForwardMessageActivity.class);
// intent.putExtra("forward_msg_id", contextMenuMessage.getMsgId());
// startActivity(intent);
//
// break;
case ContextMenuActivity.RESUTL_CALL_PHONE:
Intent intent = new Intent(Intent.ACTION_DIAL);
Uri callData = Uri.parse("tel:" +data.getStringExtra("phone_number"));
intent.setData(callData);
startActivity(intent);
break;
default:
break;
}
}
记住先提取字符串中的数字,再去匹配正则。
STM集成
在本质上是相同的。不同的是一个是用户端,一个是经纪人端
标注下需要注意的几个地方
头像和昵称的扩展互通,是SeeHouse和STM两边都需要做的。
因为有一条对于从房源详情进入时带入的房源详情类型的聊天条目,经纪人可以点击查看该房源在STM中的详情。是在STM中单独实现的。SeeHouse负责带入,STM负责点击跳转。
对于从房源详情进入时带入的房源详情类型的聊天条目,经纪人可以点击查看该房源在STM中的详情。
创建图文chatrow并设置对应点击事件代码。
集成至目标App
不需要的代码,我们只做注释,不删除,防止后面增加了,需要了。避免一系列麻烦。
剔除红包库
在ChatUIDemo3.0的build.gradle中注释编译红包依赖库。
各种编译,遇到报错就删除相关代码
剔除不需要的代码
注意EaseUI下有个SimpleDemo
目标App集成与调试
因为是公司的商业项目,这里就不贴出来了。接着完成需调试才能完成的功能点
总结
好了,至此,我们开发详案写完了,代码也写完了。因为本文写的时候UI还未出,所以后面就是根据UI改改的调整调整界面的小事情了。
有任何问题或者其他事宜请联系我:5108168@qq.com,欢迎指正和勘误。