环信官方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进行编码

房源详情增加咨询按钮,点击进入咨询对话框,并且将房源信息带入对话框。

消息中心

主界面TABBAR点击消息进入该界面

包含系统消息入库和咨询用户列表

从TABBAR点击“消息”图标进入本页面后,可以在本页面进入”系统消息“,并且将咨询过的用户会话显示在本页,长按任意一条会话,提示删除当前会话确定。

列表排序:“系统通知“仍然在最上面的位置,不受排序影响

咨询排序:按最后聊天时间倒序排列,咨询列表默认显示20条,多了拖动加载分页数据。

无咨询用户时,只显示”系统通知“入口即可。

咨询列表:长按可删除当前聊天对象,需要有确认对话框(只删除会话,不删除聊天记录)

根据UE和UI改造聊天窗口(EaseUI库)

注意以下几点

从房源详情页进入时,就返回房源详情页,从消息中心进入时,就返回消息中心。

显示当前咨询人的经纪人姓名,并显示当前咨询的对象的在线状态(在线/离线)

标题头中的电话按钮可以直接拨打电话

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

显示经纪人照片上传的照片,如果经纪人没有上传照片,就显示一个经纪人的占位图(要区别于用户的占位图)

当前用户头像默认显示当前用户的头像,如果没有头像,就显示一个默认的占位图

聊天内容上长按可复制

发送的是手机号码时可以直接打电话。

思路

先做加法,再做减法

我们来按照原有代码改造和设计环信SDK部分相关代码改造,两个部分来做工作。将具体的功能点拆分并给出实现。

我们在Demo上修改,修改完成后剔除无关代码抽取成独立的我们需要的相关代码。整个工作也就结束了。

通过之前的代码阅读,我们知道整个Demo是一个相对完整的App,而我们实际工作中集成个im基本出不了这个范围。

就好比这次迭代也是。

因为实际整个涉及的只有会话列表和聊天界面,我们主要关注ConversationListFragmentChatActivity就行了。

实现

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,欢迎指正和勘误。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,978评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,954评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,623评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,324评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,390评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,741评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,892评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,655评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,104评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,451评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,569评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,254评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,834评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,725评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,950评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,260评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,446评论 2 348

推荐阅读更多精彩内容