注册

环信官方Demo源码分析及SDK简单应用-IM集成开发详案及具体代码实现

环信官方Demo源码分析及SDK简单应用

环信官方Demo源码分析及SDK简单应用-ChatDemoUI3.0

环信官方Demo源码分析及SDK简单应用-LoginActivity

环信官方Demo源码分析及SDK简单应用-主界面的三个fragment-会话界面

环信官方Demo源码分析及SDK简单应用-主界面的三个fragment-通讯录界面

环信官方Demo源码分析及SDK简单应用-主界面的三个fragment-设置界面

环信官方Demo源码分析及SDK简单应用-EaseUI
 
环信官方Demo源码分析及SDK简单应用-IM集成开发详案及具体代码实现
 前言

   手头工作上,正好需要在已有的两个App上集成IM功能。且迭代流程中是有开发详案这一项的。就分享给大家,边写开发详案边写代码。好吧,废话不多说,我们一起来学习如何集成和改造这款简单易用而又非常强大的环信SDK。
 具体步骤
迭代点

需要做的功能点及工作

1.集成环信

2.围绕UE和UI进行编码
  • 房源详情增加咨询按钮,点击进入咨询对话框,并且将房源信息带入对话框。
  • 消息中心
[list=1]主界面TABBAR点击消息进入该界面包含系统消息入库和咨询用户列表从TABBAR点击“消息”图标进入本页面后,可以在本页面进入”系统消息“,并且将咨询过的用户会话显示在本页,长按任意一条会话,提示删除当前会话确定。列表排序:“系统通知“仍然在最上面的位置,不受排序影响咨询排序:按最后聊天时间倒序排列,咨询列表默认显示20条,多了拖动加载分页数据。无咨询用户时,只显示”系统通知“入口即可。咨询列表:长按可删除当前聊天对象,需要有确认对话框(只删除会话,不删除聊天记录) 
  • 根据UE和UI改造聊天窗口(EaseUI库)
注意以下几点[list=1]从房源详情页进入时,就返回房源详情页,从消息中心进入时,就返回消息中心。显示当前咨询人的经纪人姓名,并显示当前咨询的对象的在线状态(在线/离线)标题头中的电话按钮可以直接拨打电话对于从房源详情进入时带入的房源详情类型的聊天条目,经纪人可以点击查看该房源在STM中的详情。显示经纪人照片上传的照片,如果经纪人没有上传照片,就显示一个经纪人的占位图(要区别于用户的占位图)当前用户头像默认显示当前用户的头像,如果没有头像,就显示一个默认的占位图聊天内容上长按可复制发送的是手机号码时可以直接打电话。思路先做加法,再做减法我们来按照原有代码改造和设计环信SDK部分相关代码改造,两个部分来做工作。将具体的功能点拆分并给出实现。我们在Demo上修改,修改完成后剔除无关代码抽取成独立的我们需要的相关代码。整个工作也就结束了。通过之前的代码阅读,我们知道整个Demo是一个相对完整的App,而我们实际工作中集成个im基本出不了这个范围。就好比这次迭代也是。因为实际整个涉及的只有会话列表和聊天界面,我们主要关注ConversationListFragmentChatActivity就行了。实现SeeHouse相关改造原有代码改造房源详情增加咨询按钮,点击进入咨询对话框,并且将房源信息带入对话框。主界面TABBAR点击消息进入该界面涉及环信SDK部分相关代码改造包含系统消息入库和咨询用户列表同列表,不同type类型区分,并置顶系统消息 从TABBAR点击“消息”图标进入本页面后,可以在本页面进入”系统消息“,并且将咨询过的用户会话显示在本页,长按任意一条会话,提示删除当前会话确定。直接贴过去,Demo已经实现。 列表排序:“系统通知“仍然在最上面的位置,不受排序影响根据Type来判断类型,并排序置顶。 咨询排序:按最后聊天时间倒序排列,咨询列表默认显示20条,多了拖动加载分页数据。sort算法改一下,看下本身是否带分页。 无咨询用户时,只显示”系统通知“入口即可。无需实现。 咨询列表:长按可删除当前聊天对象,需要有确认对话框(只删除会话,不删除聊天记录)

001.jpg

环信的哥哥们已经帮我们实现了。但是根据要求呢,我没只需要删除会话,所以我们把第二项注释掉。

002.jpg

我们把对应处的判断代码和对应的menu文件em_delete_message中的标签给注释掉。看效果。

003.jpg

从房源详情页进入时,就返回房源详情页,从消息中心进入时,就返回消息中心。​直接finish();显示当前咨询人的经纪人姓名,并显示当前咨询的对象的在线状态(在线/离线)官方的EaseUi是这么说的

004.png

我们来找下EaseTitleBar

004.jpg

我们来看下他的布局
 <?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="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" >​    <RelativeLayout        android:id="@+id/left_layout"        android:layout_width="50dip"        android:layout_height="match_parent"        android:background="@drawable/ease_common_tab_bg"        android:clickable="true" >​        <ImageView            android:id="@+id/left_image"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_centerInParent="true"            android:scaleType="centerInside" />    </RelativeLayout>​    <TextView        android:id="@+id/title"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_centerInParent="true"        android:textColor="#ffffff"        android:textSize="20sp" />​    <RelativeLayout        android:id="@+id/right_layout"        android:layout_width="50dp"        android:layout_height="match_parent"        android:layout_alignParentRight="true"        android:background="@drawable/ease_common_tab_bg" >​        <ImageView            android:id="@+id/right_image"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_centerInParent="true"            android:scaleType="centerInside" />    </RelativeLayout>​</RelativeLayout>
其实有title和rightview的。

005.jpg

我们来对title加入一个是否在线的状态1.获取token
MacBook:~ mli$ curl -X POST "https://a1.easemob.com/1177170104178912/demo/token" -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/1177170104178912/demo/users/2/status"HTTP/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/1177170104178912/demo/users/2/status",  "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/1177170104178912/demo/users/1/status"HTTP/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/1177170104178912/demo/users/1/status",  "entities" : [ ],  "data" : {    "1" : "online"  },  "timestamp" : 1487568248135,  "duration" : 14,  "count" : 0MacBook:~ mli$ 
我们可以看到2是离线,1是在线的。注意一点

006.jpg

所以昵称是在咱自己的体系的。可以从现有的App里提取,如果有的话。我们知道从列表ConversationListFragment->ChatActivity->ChatFragment那么如何接受和发送自己与他人的头像和昵称呢?我们来玩这个ChatFragment

007.jpg

在OnSetMessageAttributes中,设置我们要发送时的消息扩展属性。那么接收怎么办呢,我们来看下DemoHelper中的getUserInfo()方法。

008.jpg

无聊的用鄙人蹩脚的英文写了一把注释。英文若是写的不对就不对吧。标题头中的电话按钮可以直接拨打电话修改删除按钮为打电话,并改动相关代码显示经纪人照片上传的照片,如果经纪人没有上传照片,就显示一个经纪人的占位图(要区别于用户的占位图)修改原demo当前用户头像默认显示当前用户的头像,如果没有头像,就显示一个默认的占位图修改原demo。聊天内容上长按可复制

009.jpg

自带了,后面我们可能需要去掉转发。发送的是手机号码时可以直接打电话。我们再长按后判断其是否为电话号码,如果是添加一项拨打电话。引用关系是这样的ChatFragment->ContextMenuActivity->em_context_menu_for_location.xml最后调回ChatFragment的onActivityResult我们来改em_context_menu_for_location.xml
 <?xml version="1.0" encoding="UTF-8"?><LinearLayout xmlns:android="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" >​    <TextView        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_marginTop="1dp"        android:background="@drawable/em_context_menu_item_bg"        android:clickable="true"        android:gravity="center_vertical"        android:onClick="copy"        android:padding="10dp"        android:text="@string/copy_message"        android:textColor="@android:color/black"        android:textSize="20sp" />​    <View        android:layout_width="match_parent"        android:layout_height="1px"        android:background="@android:color/darker_gray" />​    <TextView        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:background="@drawable/em_context_menu_item_bg"        android:clickable="true"        android:gravity="center_vertical"        android:onClick="delete"        android:padding="10dp"        android:text="@string/delete_message"        android:textColor="@android:color/black"        android:textSize="20sp" /><!--    <View        android:layout_width="match_parent"        android:layout_height="1px"        android:background="@android:color/darker_gray" />​   <TextView        android:id="@+id/forward"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:background="@drawable/em_context_menu_item_bg"        android:clickable="true"        android:gravity="center_vertical"        android:onClick="forward"        android:padding="10dp"        android:text="@string/forward"        android:textColor="@android:color/black"        android:textSize="20sp" />-->    <View        android:layout_width="match_parent"        android:layout_height="1px"        android:background="@android:color/darker_gray" />    <TextView        android:id="@+id/call_phone"        android:visibility="gone"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:background="@drawable/em_context_menu_item_bg"        android:clickable="true"        android:gravity="center_vertical"        android:onClick="call"        android:padding="10dp"        android:text="@string/call_phone"        android:textColor="@android:color/black"        android:textSize="20sp" /></LinearLayout>
再来改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;            }        }
记住先提取字符串中的数字,再去匹配正则。

010.jpg


011.jpg

STM集成在本质上是相同的。不同的是一个是用户端,一个是经纪人端标注下需要注意的几个地方
  • 头像和昵称的扩展互通,是SeeHouse和STM两边都需要做的。
  • 因为有一条对于从房源详情进入时带入的房源详情类型的聊天条目,经纪人可以点击查看该房源在STM中的详情。是在STM中单独实现的。SeeHouse负责带入,STM负责点击跳转。

对于从房源详情进入时带入的房源详情类型的聊天条目,经纪人可以点击查看该房源在STM中的详情。

创建图文chatrow并设置对应点击事件代码。

集成至目标App
不需要的代码,我们只做注释,不删除,防止后面增加了,需要了。避免一系列麻烦。
​剔除红包库​
在ChatUIDemo3.0的build.gradle中注释编译红包依赖库。

各种编译,遇到报错就删除相关代码
剔除不需要的代码

注意EaseUI下有个SimpleDemo

012.jpg


目标App集成与调试

因为是公司的商业项目,这里就不贴出来了。接着完成需调试才能完成的功能点

总结
好了,至此,我们开发详案写完了,代码也写完了。因为本文写的时候UI还未出,所以后面就是根据UI改改的调整调整界面的小事情了。

有任何问题或者其他事宜请联系我: 5108168@qq.com,欢迎指正和勘误。

0 个评论

要回复文章请先登录注册