Android

Android

2
最佳

环信服务端集成,如何集成? iOS Android webim

执念 回复了问题 • 3 人关注 • 828 次浏览 • 2017-03-13 11:36 • 来自相关话题

0
评论

Android ios V3.3.0 SDK 已发布,增加群组、聊天室管理员权限 iOS Android 产品更新

产品更新 发表了文章 • 276 次浏览 • 2017-03-08 16:37 • 来自相关话题

 Android​ V3.3.0 2017-03-07
 
新功能:
群组和聊天室改造:增加管理员权限,新增禁言,增减管理员的功能,支持使用分批的方式获取成员,禁言,管理员列表,支持完善的聊天室功能。新增加API请查看链接3.3.0 api修改优化dns劫持时的处理增加EMConversation.latestMessageFromOthers,表示收到对方的最后一条消息增加EMClient.compressLogs,压缩log,Demo中增加通过邮件发送log的示例libs.without.audio继续支持armeabi,解决armeabi-v5te的支持问题

bug 修订:
修复2.x升级3.x消息未读数为0的bugDemo在视频通话时,主叫方铃声没有播放的问题Demo在视频通话时,主叫方在建立连接成功后,文字提示不正确Demo在聊天窗口界面,清空消息后,收到新的消息,返回会话列表,未读消息数显示不正确修复在Oppo和Vivo手机上出现的JobService报错。EMGroupManager.createGroup成员列表数超过512产生的overflow错误修复部分手机在网络切换时发消息慢的bug
 
ios V3.3.0 2017-03-07
 
新功能:
新增:群组改造,增加一系列新接口,具体查看iOS iOS 3.3.0 api修改新增:获取SDK日志路径接口,将日志文件压缩成.gz文件,返回gz文件路径,[EMClient getLogFilesPath:]更新:使用视频通话录制功能时,必须在开始通话之前调用[EMVideoRecorderPlugin initGlobalConfig]

优化:
优化DNS劫持时的处理切换网络时,减小消息重发的等待时间

修复:
音视频通话丢包率(以前返回的是丢包数)IOS动态库用H264编码在iPhone6s上崩溃实时音视频新旧版互通崩溃
 
版本历史:Android SDK更新日志  ios SDK更新日志
下载地址:SDK下载 查看全部

7658.jpg_wh860_.jpg

 Android​ V3.3.0 2017-03-07
 
新功能:
  1. 群组和聊天室改造:增加管理员权限,新增禁言,增减管理员的功能,支持使用分批的方式获取成员,禁言,管理员列表,支持完善的聊天室功能。新增加API请查看链接3.3.0 api修改
  2. 优化dns劫持时的处理
  3. 增加EMConversation.latestMessageFromOthers,表示收到对方的最后一条消息
  4. 增加EMClient.compressLogs,压缩log,Demo中增加通过邮件发送log的示例
  5. libs.without.audio继续支持armeabi,解决armeabi-v5te的支持问题


bug 修订:
  1. 修复2.x升级3.x消息未读数为0的bug
  2. Demo在视频通话时,主叫方铃声没有播放的问题
  3. Demo在视频通话时,主叫方在建立连接成功后,文字提示不正确
  4. Demo在聊天窗口界面,清空消息后,收到新的消息,返回会话列表,未读消息数显示不正确
  5. 修复在Oppo和Vivo手机上出现的JobService报错。
  6. EMGroupManager.createGroup成员列表数超过512产生的overflow错误
  7. 修复部分手机在网络切换时发消息慢的bug

 
ios V3.3.0 2017-03-07
 
新功能:
  1. 新增:群组改造,增加一系列新接口,具体查看iOS iOS 3.3.0 api修改
  2. 新增:获取SDK日志路径接口,将日志文件压缩成.gz文件,返回gz文件路径,[EMClient getLogFilesPath:]
  3. 更新:使用视频通话录制功能时,必须在开始通话之前调用[EMVideoRecorderPlugin initGlobalConfig]


优化:
  1. 优化DNS劫持时的处理
  2. 切换网络时,减小消息重发的等待时间


修复:
  1. 音视频通话丢包率(以前返回的是丢包数)
  2. IOS动态库用H264编码在iPhone6s上崩溃
  3. 实时音视频新旧版互通崩溃

 
版本历史:Android SDK更新日志  ios SDK更新日志
下载地址:SDK下载
2
回复

Android集成环信注册后登录不上去,总是返回user is already login 环信 Android

z735576328 回复了问题 • 2 人关注 • 118 次浏览 • 2017-03-07 15:03 • 来自相关话题

0
评论

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

随缘 发表了文章 • 440 次浏览 • 2017-02-21 16:55 • 来自相关话题

环信官方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基本出不了这个范围。

就好比这次迭代也是。

因为实际整个涉及的只有会话列表和聊天界面,我们主要关注ConversationListFragment与ChatActivity就行了。
实现
SeeHouse相关改造
原有代码改造
房源详情增加咨询按钮,点击进入咨询对话框,并且将房源信息带入对话框。
主界面TABBAR点击消息进入该界面
涉及环信SDK部分相关代码改造
包含系统消息入库和咨询用户列表

同列表,不同type类型区分,并置顶系统消息
 从TABBAR点击“消息”图标进入本页面后,可以在本页面进入”系统消息“,并且将咨询过的用户会话显示在本页,长按任意一条会话,提示删除当前会话确定。
直接贴过去,Demo已经实现。
 
列表排序:“系统通知“仍然在最上面的位置,不受排序影响根据Type来判断类型,并排序置顶。
 
咨询排序:按最后聊天时间倒序排列,咨询列表默认显示20条,多了拖动加载分页数据。sort算法改一下,看下本身是否带分页。
 
无咨询用户时,只显示”系统通知“入口即可。
无需实现。
 
咨询列表:长按可删除当前聊天对象,需要有确认对话框(只删除会话,不删除聊天记录)




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




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




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




我们来找下EaseTitleBar




我们来看下他的布局
<?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的。




我们来对title加入一个是否在线的状态
1.获取tokenMacBook:~ 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%3B

HTTP/1.1 200 OK

Server: Tengine/2.0.3

Date: Mon, 20 Feb 2017 05:24:00 GMT

Content-Type: application/json;charset=UTF-8

Transfer-Encoding: chunked

Connection: keep-alive

Access-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%3B

HTTP/1.1 200 OK

Server: Tengine/2.0.3

Date: Mon, 20 Feb 2017 05:24:08 GMT

Content-Type: application/json;charset=UTF-8

Transfer-Encoding: chunked

Connection: keep-alive

Access-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" : 0

MacBook:~ 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
<?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;
}
}记住先提取字符串中的数字,再去匹配正则。









STM集成

在本质上是相同的。不同的是一个是用户端,一个是经纪人端

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

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

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

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

注意EaseUI下有个SimpleDemo




目标App集成与调试

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

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

有任何问题或者其他事宜请联系我: 5108168@qq.com,欢迎指正和勘误。 查看全部
环信官方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进行编码
  • 房源详情增加咨询按钮,点击进入咨询对话框,并且将房源信息带入对话框。
  • 消息中心

  1. 主界面TABBAR点击消息进入该界面
  2. 包含系统消息入库和咨询用户列表
  3. 从TABBAR点击“消息”图标进入本页面后,可以在本页面进入”系统消息“,并且将咨询过的用户会话显示在本页,长按任意一条会话,提示删除当前会话确定。
  4. 列表排序:“系统通知“仍然在最上面的位置,不受排序影响
  5. 咨询排序:按最后聊天时间倒序排列,咨询列表默认显示20条,多了拖动加载分页数据。
  6. 无咨询用户时,只显示”系统通知“入口即可。
  7. 咨询列表:长按可删除当前聊天对象,需要有确认对话框(只删除会话,不删除聊天记录)

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

注意以下几点
  1. 从房源详情页进入时,就返回房源详情页,从消息中心进入时,就返回消息中心。
  2. 显示当前咨询人的经纪人姓名,并显示当前咨询的对象的在线状态(在线/离线)
  3. 标题头中的电话按钮可以直接拨打电话
  4. 对于从房源详情进入时带入的房源详情类型的聊天条目,经纪人可以点击查看该房源在STM中的详情。
  5. 显示经纪人照片上传的照片,如果经纪人没有上传照片,就显示一个经纪人的占位图(要区别于用户的占位图)
  6. 当前用户头像默认显示当前用户的头像,如果没有头像,就显示一个默认的占位图
  7. 聊天内容上长按可复制
  8. 发送的是手机号码时可以直接打电话。

思路
先做加法,再做减法

我们来按照原有代码改造和设计环信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/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%3B

HTTP/1.1 200 OK

Server: Tengine/2.0.3

Date: Mon, 20 Feb 2017 05:24:00 GMT

Content-Type: application/json;charset=UTF-8

Transfer-Encoding: chunked

Connection: keep-alive

Access-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%3B

HTTP/1.1 200 OK

Server: Tengine/2.0.3

Date: Mon, 20 Feb 2017 05:24:08 GMT

Content-Type: application/json;charset=UTF-8

Transfer-Encoding: chunked

Connection: keep-alive

Access-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" : 0

MacBook:~ 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
评论

环信官方Demo源码分析及SDK简单应用-EaseUI Android Demo源码分析 集成笔记

随缘 发表了文章 • 365 次浏览 • 2017-02-21 16:19 • 来自相关话题

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

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

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

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

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

环信官方Demo源码分析及SDK简单应用-主界面的三个fragment-设置界面
 
环信官方Demo源码分析及SDK简单应用-EaseUI
 
环信官方Demo源码分析及SDK简单应用-IM集成开发详案及具体代码实现

EaseUI

实际工作过程中,我们是用不了太多东西的,如果只是集成个im最多用到的就是聊天列表和聊天页面。

我们来看重头戏EaseUI这个库。

官方文档

其实官方的WiKi已经介绍的特别详细了。官方EaseUI文档
我们来看Demo
// start chat acitivity
Intent intent = new Intent(getActivity(), ChatActivity.class);
if(conversation.isGroup()){
if(conversation.getType() == EMConversationType.ChatRoom){
// it's group chat
intent.putExtra(Constant.EXTRA_CHAT_TYPE, Constant.CHATTYPE_CHATROOM);
}else{
intent.putExtra(Constant.EXTRA_CHAT_TYPE, Constant.CHATTYPE_GROUP);
}

}
// it's single chat
intent.putExtra(Constant.EXTRA_USER_ID, username);
startActivity(intent);ChatActivity

我们来看看ChatActivitypackage com.hyphenate.chatuidemo.ui;

import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import com.hyphenate.chatuidemo.R;
import com.hyphenate.chatuidemo.runtimepermissions.PermissionsManager;
import com.hyphenate.easeui.ui.EaseChatFragment;
import com.hyphenate.util.EasyUtils;

/**
* chat activity,EaseChatFragment was used {@link #EaseChatFragment}
*
*/
public class ChatActivity extends BaseActivity{
public static ChatActivity activityInstance;
private EaseChatFragment chatFragment;
String toChatUsername;

@Override
protected void onCreate(Bundle arg0) {
super.onCreate(arg0);
setContentView(R.layout.em_activity_chat);
activityInstance = this;
//get user id or group id
toChatUsername = getIntent().getExtras().getString("userId");
//use EaseChatFratFragment
chatFragment = new ChatFragment();
//pass parameters to chat fragment
chatFragment.setArguments(getIntent().getExtras());
getSupportFragmentManager().beginTransaction().add(R.id.container, chatFragment).commit();

}

@Override
protected void onDestroy() {
super.onDestroy();
activityInstance = null;
}

@Override
protected void onNewIntent(Intent intent) {
// make sure only one chat activity is opened
String username = intent.getStringExtra("userId");
if (toChatUsername.equals(username))
super.onNewIntent(intent);
else {
finish();
startActivity(intent);
}

}

@Override
public void onBackPressed() {
chatFragment.onBackPressed();
if (EasyUtils.isSingleActivity(this)) {
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
}
}

public String getToChatUsername(){
return toChatUsername;
}

@Override public void onRequestPermissionsResult(int requestCode, @NonNull String permissions,
@NonNull int grantResults) {
PermissionsManager.getInstance().notifyPermissionsChange(permissions, grantResults);
}
}官方文档是这么说的

封装EaseChatFragment的ChatFragment

那么Demo中是做了一层封装的。package com.hyphenate.chatuidemo.ui;

import android.app.Activity;
import android.content.ClipData;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Toast;

import com.easemob.redpacketsdk.constant.RPConstant;
import com.easemob.redpacketui.utils.RPRedPacketUtil;
import com.easemob.redpacketui.utils.RedPacketUtil;
import com.easemob.redpacketui.widget.ChatRowRandomPacket;
import com.easemob.redpacketui.widget.ChatRowRedPacket;
import com.easemob.redpacketui.widget.ChatRowRedPacketAck;
import com.easemob.redpacketui.widget.ChatRowTransfer;
import com.hyphenate.chat.EMClient;
import com.hyphenate.chat.EMCmdMessageBody;
import com.hyphenate.chat.EMGroup;
import com.hyphenate.chat.EMMessage;
import com.hyphenate.chat.EMTextMessageBody;
import com.hyphenate.chatuidemo.Constant;
import com.hyphenate.chatuidemo.DemoHelper;
import com.hyphenate.chatuidemo.R;
import com.hyphenate.chatuidemo.domain.EmojiconExampleGroupData;
import com.hyphenate.chatuidemo.domain.RobotUser;
import com.hyphenate.chatuidemo.widget.ChatRowVoiceCall;
import com.hyphenate.easeui.EaseConstant;
import com.hyphenate.easeui.ui.EaseChatFragment;
import com.hyphenate.easeui.ui.EaseChatFragment.EaseChatFragmentHelper;
import com.hyphenate.easeui.widget.chatrow.EaseChatRow;
import com.hyphenate.easeui.widget.chatrow.EaseCustomChatRowProvider;
import com.hyphenate.easeui.widget.emojicon.EaseEmojiconMenu;
import com.hyphenate.util.EasyUtils;
import com.hyphenate.util.PathUtil;

import java.io.File;
import java.io.FileOutputStream;
import java.util.List;
import java.util.Map;

public class ChatFragment extends EaseChatFragment implements EaseChatFragmentHelper{

// constant start from 11 to avoid conflict with constant in base class
private static final int ITEM_VIDEO = 11;
private static final int ITEM_FILE = 12;
private static final int ITEM_VOICE_CALL = 13;
private static final int ITEM_VIDEO_CALL = 14;

private static final int REQUEST_CODE_SELECT_VIDEO = 11;
private static final int REQUEST_CODE_SELECT_FILE = 12;
private static final int REQUEST_CODE_GROUP_DETAIL = 13;
private static final int REQUEST_CODE_CONTEXT_MENU = 14;
private static final int REQUEST_CODE_SELECT_AT_USER = 15;


private static final int MESSAGE_TYPE_SENT_VOICE_CALL = 1;
private static final int MESSAGE_TYPE_RECV_VOICE_CALL = 2;
private static final int MESSAGE_TYPE_SENT_VIDEO_CALL = 3;
private static final int MESSAGE_TYPE_RECV_VIDEO_CALL = 4;

//red packet code : 红包功能使用的常量
private static final int MESSAGE_TYPE_RECV_RED_PACKET = 5;
private static final int MESSAGE_TYPE_SEND_RED_PACKET = 6;
private static final int MESSAGE_TYPE_SEND_RED_PACKET_ACK = 7;
private static final int MESSAGE_TYPE_RECV_RED_PACKET_ACK = 8;
private static final int MESSAGE_TYPE_RECV_TRANSFER_PACKET = 9;
private static final int MESSAGE_TYPE_SEND_TRANSFER_PACKET = 10;
private static final int MESSAGE_TYPE_RECV_RANDOM = 11;
private static final int MESSAGE_TYPE_SEND_RANDOM = 12;
private static final int REQUEST_CODE_SEND_RED_PACKET = 16;
private static final int ITEM_RED_PACKET = 16;
private static final int REQUEST_CODE_SEND_TRANSFER_PACKET = 17;
private static final int ITEM_TRANSFER_PACKET = 17;
//end of red packet code

/**
* if it is chatBot
*/
private boolean isRobot;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return super.onCreateView(inflater, container, savedInstanceState);
}

@Override
protected void setUpView() {
setChatFragmentListener(this);
if (chatType == Constant.CHATTYPE_SINGLE) {
Map<String,RobotUser> robotMap = DemoHelper.getInstance().getRobotList();
if(robotMap!=null && robotMap.containsKey(toChatUsername)){
isRobot = true;
}
}
super.setUpView();
// set click listener
titleBar.setLeftLayoutClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
if (EasyUtils.isSingleActivity(getActivity())) {
Intent intent = new Intent(getActivity(), MainActivity.class);
startActivity(intent);
}
onBackPressed();
}
});
((EaseEmojiconMenu)inputMenu.getEmojiconMenu()).addEmojiconGroup(EmojiconExampleGroupData.getData());
if(chatType == EaseConstant.CHATTYPE_GROUP){
inputMenu.getPrimaryMenu().getEditText().addTextChangedListener(new TextWatcher() {

@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if(count == 1 && "@".equals(String.valueOf(s.charAt(start)))){
startActivityForResult(new Intent(getActivity(), PickAtUserActivity.class).
putExtra("groupId", toChatUsername), REQUEST_CODE_SELECT_AT_USER);
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {

}
@Override
public void afterTextChanged(Editable s) {

}
});
}
}

@Override
protected void registerExtendMenuItem() {
//use the menu in base class
super.registerExtendMenuItem();
//extend menu items
inputMenu.registerExtendMenuItem(R.string.attach_video, R.drawable.em_chat_video_selector, ITEM_VIDEO, extendMenuItemClickListener);
inputMenu.registerExtendMenuItem(R.string.attach_file, R.drawable.em_chat_file_selector, ITEM_FILE, extendMenuItemClickListener);
if(chatType == Constant.CHATTYPE_SINGLE){
inputMenu.registerExtendMenuItem(R.string.attach_voice_call, R.drawable.em_chat_voice_call_selector, ITEM_VOICE_CALL, extendMenuItemClickListener);
inputMenu.registerExtendMenuItem(R.string.attach_video_call, R.drawable.em_chat_video_call_selector, ITEM_VIDEO_CALL, extendMenuItemClickListener);
}
//聊天室暂时不支持红包功能
//red packet code : 注册红包菜单选项
if (chatType != Constant.CHATTYPE_CHATROOM) {
inputMenu.registerExtendMenuItem(R.string.attach_red_packet, R.drawable.em_chat_red_packet_selector, ITEM_RED_PACKET, extendMenuItemClickListener);
}
//red packet code : 注册转账菜单选项
if (chatType == Constant.CHATTYPE_SINGLE) {
inputMenu.registerExtendMenuItem(R.string.attach_transfer_money, R.drawable.em_chat_transfer_selector, ITEM_TRANSFER_PACKET, extendMenuItemClickListener);
}
//end of red packet code
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_CONTEXT_MENU) {
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;

default:
break;
}
}
if(resultCode == Activity.RESULT_OK){
switch (requestCode) {
case REQUEST_CODE_SELECT_VIDEO: //send the video
if (data != null) {
int duration = data.getIntExtra("dur", 0);
String videoPath = data.getStringExtra("path");
File file = new File(PathUtil.getInstance().getImagePath(), "thvideo" + System.currentTimeMillis());
try {
FileOutputStream fos = new FileOutputStream(file);
Bitmap ThumbBitmap = ThumbnailUtils.createVideoThumbnail(videoPath, 3);
ThumbBitmap.compress(CompressFormat.JPEG, 100, fos);
fos.close();
sendVideoMessage(videoPath, file.getAbsolutePath(), duration);
} catch (Exception e) {
e.printStackTrace();
}
}
break;
case REQUEST_CODE_SELECT_FILE: //send the file
if (data != null) {
Uri uri = data.getData();
if (uri != null) {
sendFileByUri(uri);
}
}
break;
case REQUEST_CODE_SELECT_AT_USER:
if(data != null){
String username = data.getStringExtra("username");
inputAtUsername(username, false);
}
break;
//red packet code : 发送红包消息到聊天界面
case REQUEST_CODE_SEND_RED_PACKET:
if (data != null){
sendMessage(RedPacketUtil.createRPMessage(getActivity(), data, toChatUsername));
}
break;
case REQUEST_CODE_SEND_TRANSFER_PACKET://发送转账消息
if (data != null) {
sendMessage(RedPacketUtil.createTRMessage(getActivity(), data, toChatUsername));
}
break;
//end of red packet code
default:
break;
}
}

}

@Override
public void onSetMessageAttributes(EMMessage message) {
if(isRobot){
//set message extension
message.setAttribute("em_robot_message", isRobot);
}
}

@Override
public EaseCustomChatRowProvider onSetCustomChatRowProvider() {
return new CustomChatRowProvider();
}


@Override
public void onEnterToChatDetails() {
if (chatType == Constant.CHATTYPE_GROUP) {
EMGroup group = EMClient.getInstance().groupManager().getGroup(toChatUsername);
if (group == null) {
Toast.makeText(getActivity(), R.string.gorup_not_found, Toast.LENGTH_SHORT).show();
return;
}
startActivityForResult(
(new Intent(getActivity(), GroupDetailsActivity.class).putExtra("groupId", toChatUsername)),
REQUEST_CODE_GROUP_DETAIL);
}else if(chatType == Constant.CHATTYPE_CHATROOM){
startActivityForResult(new Intent(getActivity(), ChatRoomDetailsActivity.class).putExtra("roomId", toChatUsername), REQUEST_CODE_GROUP_DETAIL);
}
}

@Override
public void onAvatarClick(String username) {
//handling when user click avatar
Intent intent = new Intent(getActivity(), UserProfileActivity.class);
intent.putExtra("username", username);
startActivity(intent);
}

@Override
public void onAvatarLongClick(String username) {
inputAtUsername(username);
}


@Override
public boolean onMessageBubbleClick(EMMessage message) {
//消息框点击事件,demo这里不做覆盖,如需覆盖,return true
//red packet code : 拆红包页面
if (message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_RED_PACKET_MESSAGE, false)){
if (RedPacketUtil.isRandomRedPacket(message)){
RedPacketUtil.openRandomPacket(getActivity(),message);
} else {
RedPacketUtil.openRedPacket(getActivity(), chatType, message, toChatUsername, messageList);
}
return true;
} else if (message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_TRANSFER_PACKET_MESSAGE, false)) {
RedPacketUtil.openTransferPacket(getActivity(), message);
return true;
}
//end of red packet code
return false;
}
@Override
public void onCmdMessageReceived(List<EMMessage> messages) {
//red packet code : 处理红包回执透传消息
for (EMMessage message : messages) {
EMCmdMessageBody cmdMsgBody = (EMCmdMessageBody) message.getBody();
String action = cmdMsgBody.action();//获取自定义action
if (action.equals(RPConstant.REFRESH_GROUP_RED_PACKET_ACTION)){
RedPacketUtil.receiveRedPacketAckMessage(message);
messageList.refresh();
}
}
//end of red packet code
super.onCmdMessageReceived(messages);
}

@Override
public void onMessageBubbleLongClick(EMMessage message) {
// no message forward when in chat room
startActivityForResult((new Intent(getActivity(), ContextMenuActivity.class)).putExtra("message",message)
.putExtra("ischatroom", chatType == EaseConstant.CHATTYPE_CHATROOM),
REQUEST_CODE_CONTEXT_MENU);
}

@Override
public boolean onExtendMenuItemClick(int itemId, View view) {
switch (itemId) {
case ITEM_VIDEO:
Intent intent = new Intent(getActivity(), ImageGridActivity.class);
startActivityForResult(intent, REQUEST_CODE_SELECT_VIDEO);
break;
case ITEM_FILE: //file
selectFileFromLocal();
break;
case ITEM_VOICE_CALL:
startVoiceCall();
break;
case ITEM_VIDEO_CALL:
startVideoCall();
break;
//red packet code : 进入发红包页面
case ITEM_RED_PACKET:
if (chatType == Constant.CHATTYPE_SINGLE) {
//单聊红包修改进入红包的方法,可以在小额随机红包和普通单聊红包之间切换
RedPacketUtil.startRandomPacket(new RPRedPacketUtil.RPRandomCallback() {
@Override
public void onSendPacketSuccess(Intent data) {
sendMessage(RedPacketUtil.createRPMessage(getActivity(), data, toChatUsername));
}

@Override
public void switchToNormalPacket() {
RedPacketUtil.startRedPacketActivityForResult(ChatFragment.this, chatType, toChatUsername, REQUEST_CODE_SEND_RED_PACKET);
}
},getActivity(),toChatUsername);
} else {
RedPacketUtil.startRedPacketActivityForResult(this, chatType, toChatUsername, REQUEST_CODE_SEND_RED_PACKET);
}
break;
case ITEM_TRANSFER_PACKET://进入转账页面
RedPacketUtil.startTransferActivityForResult(this, toChatUsername, REQUEST_CODE_SEND_TRANSFER_PACKET);
break;
//end of red packet code
default:
break;
}
//keep exist extend menu
return false;
}

/**
* select file
*/
protected void selectFileFromLocal() {
Intent intent = null;
if (Build.VERSION.SDK_INT < 19) { //api 19 and later, we can't use this way, demo just select from images
intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("*/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);

} else {
intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
}
startActivityForResult(intent, REQUEST_CODE_SELECT_FILE);
}

/**
* make a voice call
*/
protected void startVoiceCall() {
if (!EMClient.getInstance().isConnected()) {
Toast.makeText(getActivity(), R.string.not_connect_to_server, Toast.LENGTH_SHORT).show();
} else {
startActivity(new Intent(getActivity(), VoiceCallActivity.class).putExtra("username", toChatUsername)
.putExtra("isComingCall", false));
// voiceCallBtn.setEnabled(false);
inputMenu.hideExtendMenuContainer();
}
}

/**
* make a video call
*/
protected void startVideoCall() {
if (!EMClient.getInstance().isConnected())
Toast.makeText(getActivity(), R.string.not_connect_to_server, Toast.LENGTH_SHORT).show();
else {
startActivity(new Intent(getActivity(), VideoCallActivity.class).putExtra("username", toChatUsername)
.putExtra("isComingCall", false));
// videoCallBtn.setEnabled(false);
inputMenu.hideExtendMenuContainer();
}
}

/**
* chat row provider
*
*/
private final class CustomChatRowProvider implements EaseCustomChatRowProvider {
@Override
public int getCustomChatRowTypeCount() {
//here the number is the message type in EMMessage::Type
//which is used to count the number of different chat row
return 12;
}

@Override
public int getCustomChatRowType(EMMessage message) {
if(message.getType() == EMMessage.Type.TXT){
//voice call
if (message.getBooleanAttribute(Constant.MESSAGE_ATTR_IS_VOICE_CALL, false)){
return message.direct() == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_VOICE_CALL : MESSAGE_TYPE_SENT_VOICE_CALL;
}else if (message.getBooleanAttribute(Constant.MESSAGE_ATTR_IS_VIDEO_CALL, false)){
//video call
return message.direct() == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_VIDEO_CALL : MESSAGE_TYPE_SENT_VIDEO_CALL;
}
//red packet code : 红包消息、红包回执消息以及转账消息的chatrow type
else if (RedPacketUtil.isRandomRedPacket(message)) {
//小额随机红包
return message.direct() == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_RANDOM : MESSAGE_TYPE_SEND_RANDOM;
} else if (message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_RED_PACKET_MESSAGE, false)) {
//发送红包消息
return message.direct() == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_RED_PACKET : MESSAGE_TYPE_SEND_RED_PACKET;
} else if (message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_RED_PACKET_ACK_MESSAGE, false)) {
//领取红包消息
return message.direct() == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_RED_PACKET_ACK : MESSAGE_TYPE_SEND_RED_PACKET_ACK;
} else if (message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_TRANSFER_PACKET_MESSAGE, false)) {
//转账消息
return message.direct() == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_TRANSFER_PACKET : MESSAGE_TYPE_SEND_TRANSFER_PACKET;
}
//end of red packet code
}
return 0;
}

@Override
public EaseChatRow getCustomChatRow(EMMessage message, int position, BaseAdapter adapter) {
if(message.getType() == EMMessage.Type.TXT){
// voice call or video call
if (message.getBooleanAttribute(Constant.MESSAGE_ATTR_IS_VOICE_CALL, false) ||
message.getBooleanAttribute(Constant.MESSAGE_ATTR_IS_VIDEO_CALL, false)){
return new ChatRowVoiceCall(getActivity(), message, position, adapter);
}
//red packet code : 红包消息、红包回执消息以及转账消息的chat row
else if (RedPacketUtil.isRandomRedPacket(message)) {//小额随机红包
return new ChatRowRandomPacket(getActivity(), message, position, adapter);
} else if (message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_RED_PACKET_MESSAGE, false)) {//红包消息
return new ChatRowRedPacket(getActivity(), message, position, adapter);
} else if (message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_RED_PACKET_ACK_MESSAGE, false)) {//红包回执消息
return new ChatRowRedPacketAck(getActivity(), message, position, adapter);
} else if (message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_TRANSFER_PACKET_MESSAGE, false)) {//转账消息
return new ChatRowTransfer(getActivity(), message, position, adapter);
}
//end of red packet code
}
return null;
}

}

}判断是不是机器人及添加监听
setChatFragmentListener(this);
if (chatType == Constant.CHATTYPE_SINGLE) {
Map<String,RobotUser> robotMap = DemoHelper.getInstance().getRobotList();
if(robotMap!=null && robotMap.containsKey(toChatUsername)){
isRobot = true;
}
}点击标题返回及群聊@别人的功能​// set click listener
titleBar.setLeftLayoutClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
if (EasyUtils.isSingleActivity(getActivity())) {
Intent intent = new Intent(getActivity(), MainActivity.class);
startActivity(intent);
}
onBackPressed();
}
});
((EaseEmojiconMenu)inputMenu.getEmojiconMenu()).addEmojiconGroup(EmojiconExampleGroupData.getData());
if(chatType == EaseConstant.CHATTYPE_GROUP){
inputMenu.getPrimaryMenu().getEditText().addTextChangedListener(new TextWatcher() {

@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if(count == 1 && "@".equals(String.valueOf(s.charAt(start)))){
startActivityForResult(new Intent(getActivity(), PickAtUserActivity.class).
putExtra("groupId", toChatUsername), REQUEST_CODE_SELECT_AT_USER);
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {

}
@Override
public void afterTextChanged(Editable s) {

}
});
}菜单的操作​
super.registerExtendMenuItem();
//extend menu items
inputMenu.registerExtendMenuItem(R.string.attach_video, R.drawable.em_chat_video_selector, ITEM_VIDEO, extendMenuItemClickListener);
inputMenu.registerExtendMenuItem(R.string.attach_file, R.drawable.em_chat_file_selector, ITEM_FILE, extendMenuItemClickListener);
if(chatType == Constant.CHATTYPE_SINGLE){
inputMenu.registerExtendMenuItem(R.string.attach_voice_call, R.drawable.em_chat_voice_call_selector, ITEM_VOICE_CALL, extendMenuItemClickListener);
inputMenu.registerExtendMenuItem(R.string.attach_video_call, R.drawable.em_chat_video_call_selector, ITEM_VIDEO_CALL, extendMenuItemClickListener);
}
//聊天室暂时不支持红包功能
//red packet code : 注册红包菜单选项
if (chatType != Constant.CHATTYPE_CHATROOM) {
inputMenu.registerExtendMenuItem(R.string.attach_red_packet, R.drawable.em_chat_red_packet_selector, ITEM_RED_PACKET, extendMenuItemClickListener);
}
//red packet code : 注册转账菜单选项
if (chatType == Constant.CHATTYPE_SINGLE) {
inputMenu.registerExtendMenuItem(R.string.attach_transfer_money, R.drawable.em_chat_transfer_selector, ITEM_TRANSFER_PACKET, extendMenuItemClickListener);
}
//end of red packet code一些功能操作​if (requestCode == REQUEST_CODE_CONTEXT_MENU) {
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;

default:
break;
}
}
if(resultCode == Activity.RESULT_OK){
switch (requestCode) {
case REQUEST_CODE_SELECT_VIDEO: //send the video
if (data != null) {
int duration = data.getIntExtra("dur", 0);
String videoPath = data.getStringExtra("path");
File file = new File(PathUtil.getInstance().getImagePath(), "thvideo" + System.currentTimeMillis());
try {
FileOutputStream fos = new FileOutputStream(file);
Bitmap ThumbBitmap = ThumbnailUtils.createVideoThumbnail(videoPath, 3);
ThumbBitmap.compress(CompressFormat.JPEG, 100, fos);
fos.close();
sendVideoMessage(videoPath, file.getAbsolutePath(), duration);
} catch (Exception e) {
e.printStackTrace();
}
}
break;
case REQUEST_CODE_SELECT_FILE: //send the file
if (data != null) {
Uri uri = data.getData();
if (uri != null) {
sendFileByUri(uri);
}
}
break;
case REQUEST_CODE_SELECT_AT_USER:
if(data != null){
String username = data.getStringExtra("username");
inputAtUsername(username, false);
}
break;
//red packet code : 发送红包消息到聊天界面
case REQUEST_CODE_SEND_RED_PACKET:
if (data != null){
sendMessage(RedPacketUtil.createRPMessage(getActivity(), data, toChatUsername));
}
break;
case REQUEST_CODE_SEND_TRANSFER_PACKET://发送转账消息
if (data != null) {
sendMessage(RedPacketUtil.createTRMessage(getActivity(), data, toChatUsername));
}
break;
//end of red packet code
default:
break;
}
}进入聊天详情​
@Override
public void onEnterToChatDetails() {
if (chatType == Constant.CHATTYPE_GROUP) {
EMGroup group = EMClient.getInstance().groupManager().getGroup(toChatUsername);
if (group == null) {
Toast.makeText(getActivity(), R.string.gorup_not_found, Toast.LENGTH_SHORT).show();
return;
}
startActivityForResult(
(new Intent(getActivity(), GroupDetailsActivity.class).putExtra("groupId", toChatUsername)),
REQUEST_CODE_GROUP_DETAIL);
}else if(chatType == Constant.CHATTYPE_CHATROOM){
startActivityForResult(new Intent(getActivity(), ChatRoomDetailsActivity.class).putExtra("roomId", toChatUsername), REQUEST_CODE_GROUP_DETAIL);
}
}点击头像​@Override
public void onAvatarClick(String username) {
//handling when user click avatar
Intent intent = new Intent(getActivity(), UserProfileActivity.class);
intent.putExtra("username", username);
startActivity(intent);
}消息框点击事件、拆红包@Override
public boolean onMessageBubbleClick(EMMessage message) {
//消息框点击事件,demo这里不做覆盖,如需覆盖,return true
//red packet code : 拆红包页面
if (message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_RED_PACKET_MESSAGE, false)){
if (RedPacketUtil.isRandomRedPacket(message)){
RedPacketUtil.openRandomPacket(getActivity(),message);
} else {
RedPacketUtil.openRedPacket(getActivity(), chatType, message, toChatUsername, messageList);
}
return true;
} else if (message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_TRANSFER_PACKET_MESSAGE, false)) {
RedPacketUtil.openTransferPacket(getActivity(), message);
return true;
}
//end of red packet code
return false;
}红包回执及消息框长按​@Override
public void onCmdMessageReceived(List<EMMessage> messages) {
//red packet code : 处理红包回执透传消息
for (EMMessage message : messages) {
EMCmdMessageBody cmdMsgBody = (EMCmdMessageBody) message.getBody();
String action = cmdMsgBody.action();//获取自定义action
if (action.equals(RPConstant.REFRESH_GROUP_RED_PACKET_ACTION)){
RedPacketUtil.receiveRedPacketAckMessage(message);
messageList.refresh();
}
}
//end of red packet code
super.onCmdMessageReceived(messages);
}

@Override
public void onMessageBubbleLongClick(EMMessage message) {
// no message forward when in chat room
startActivityForResult((new Intent(getActivity(), ContextMenuActivity.class)).putExtra("message",message)
.putExtra("ischatroom", chatType == EaseConstant.CHATTYPE_CHATROOM),
REQUEST_CODE_CONTEXT_MENU);
}扩展按钮​@Override
public boolean onExtendMenuItemClick(int itemId, View view) {
switch (itemId) {
case ITEM_VIDEO:
Intent intent = new Intent(getActivity(), ImageGridActivity.class);
startActivityForResult(intent, REQUEST_CODE_SELECT_VIDEO);
break;
case ITEM_FILE: //file
selectFileFromLocal();
break;
case ITEM_VOICE_CALL:
startVoiceCall();
break;
case ITEM_VIDEO_CALL:
startVideoCall();
break;
//red packet code : 进入发红包页面
case ITEM_RED_PACKET:
if (chatType == Constant.CHATTYPE_SINGLE) {
//单聊红包修改进入红包的方法,可以在小额随机红包和普通单聊红包之间切换
RedPacketUtil.startRandomPacket(new RPRedPacketUtil.RPRandomCallback() {
@Override
public void onSendPacketSuccess(Intent data) {
sendMessage(RedPacketUtil.createRPMessage(getActivity(), data, toChatUsername));
}

@Override
public void switchToNormalPacket() {
RedPacketUtil.startRedPacketActivityForResult(ChatFragment.this, chatType, toChatUsername, REQUEST_CODE_SEND_RED_PACKET);
}
},getActivity(),toChatUsername);
} else {
RedPacketUtil.startRedPacketActivityForResult(this, chatType, toChatUsername, REQUEST_CODE_SEND_RED_PACKET);
}
break;
case ITEM_TRANSFER_PACKET://进入转账页面
RedPacketUtil.startTransferActivityForResult(this, toChatUsername, REQUEST_CODE_SEND_TRANSFER_PACKET);
break;
//end of red packet code
default:
break;
}
//keep exist extend menu
return false;
}本地文件选择、语音通话、视频通话、及自定义chatrow类型​
/**
* select file
*/
protected void selectFileFromLocal() {
Intent intent = null;
if (Build.VERSION.SDK_INT < 19) { //api 19 and later, we can't use this way, demo just select from images
intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("*/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);

} else {
intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
}
startActivityForResult(intent, REQUEST_CODE_SELECT_FILE);
}

/**
* make a voice call
*/
protected void startVoiceCall() {
if (!EMClient.getInstance().isConnected()) {
Toast.makeText(getActivity(), R.string.not_connect_to_server, Toast.LENGTH_SHORT).show();
} else {
startActivity(new Intent(getActivity(), VoiceCallActivity.class).putExtra("username", toChatUsername)
.putExtra("isComingCall", false));
// voiceCallBtn.setEnabled(false);
inputMenu.hideExtendMenuContainer();
}
}

/**
* make a video call
*/
protected void startVideoCall() {
if (!EMClient.getInstance().isConnected())
Toast.makeText(getActivity(), R.string.not_connect_to_server, Toast.LENGTH_SHORT).show();
else {
startActivity(new Intent(getActivity(), VideoCallActivity.class).putExtra("username", toChatUsername)
.putExtra("isComingCall", false));
// videoCallBtn.setEnabled(false);
inputMenu.hideExtendMenuContainer();
}
}

/**
* chat row provider
*
*/
private final class CustomChatRowProvider implements EaseCustomChatRowProvider {
@Override
public int getCustomChatRowTypeCount() {
//here the number is the message type in EMMessage::Type
//which is used to count the number of different chat row
return 12;
}

@Override
public int getCustomChatRowType(EMMessage message) {
if(message.getType() == EMMessage.Type.TXT){
//voice call
if (message.getBooleanAttribute(Constant.MESSAGE_ATTR_IS_VOICE_CALL, false)){
return message.direct() == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_VOICE_CALL : MESSAGE_TYPE_SENT_VOICE_CALL;
}else if (message.getBooleanAttribute(Constant.MESSAGE_ATTR_IS_VIDEO_CALL, false)){
//video call
return message.direct() == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_VIDEO_CALL : MESSAGE_TYPE_SENT_VIDEO_CALL;
}
//red packet code : 红包消息、红包回执消息以及转账消息的chatrow type
else if (RedPacketUtil.isRandomRedPacket(message)) {
//小额随机红包
return message.direct() == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_RANDOM : MESSAGE_TYPE_SEND_RANDOM;
} else if (message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_RED_PACKET_MESSAGE, false)) {
//发送红包消息
return message.direct() == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_RED_PACKET : MESSAGE_TYPE_SEND_RED_PACKET;
} else if (message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_RED_PACKET_ACK_MESSAGE, false)) {
//领取红包消息
return message.direct() == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_RED_PACKET_ACK : MESSAGE_TYPE_SEND_RED_PACKET_ACK;
} else if (message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_TRANSFER_PACKET_MESSAGE, false)) {
//转账消息
return message.direct() == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_TRANSFER_PACKET : MESSAGE_TYPE_SEND_TRANSFER_PACKET;
}
//end of red packet code
}
return 0;
}

@Override
public EaseChatRow getCustomChatRow(EMMessage message, int position, BaseAdapter adapter) {
if(message.getType() == EMMessage.Type.TXT){
// voice call or video call
if (message.getBooleanAttribute(Constant.MESSAGE_ATTR_IS_VOICE_CALL, false) ||
message.getBooleanAttribute(Constant.MESSAGE_ATTR_IS_VIDEO_CALL, false)){
return new ChatRowVoiceCall(getActivity(), message, position, adapter);
}
//red packet code : 红包消息、红包回执消息以及转账消息的chat row
else if (RedPacketUtil.isRandomRedPacket(message)) {//小额随机红包
return new ChatRowRandomPacket(getActivity(), message, position, adapter);
} else if (message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_RED_PACKET_MESSAGE, false)) {//红包消息
return new ChatRowRedPacket(getActivity(), message, position, adapter);
} else if (message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_RED_PACKET_ACK_MESSAGE, false)) {//红包回执消息
return new ChatRowRedPacketAck(getActivity(), message, position, adapter);
} else if (message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_TRANSFER_PACKET_MESSAGE, false)) {//转账消息
return new ChatRowTransfer(getActivity(), message, position, adapter);
}
//end of red packet code
}
return null;
}

}Redpacketlibrary

由于业务未涉及,暂不作分析。
 
总结及其他

其实正常集成,按照于海同学所说也就半天时间,这是因为的确环信的SDK使用起来比较方便。

通过大致的阅读代码,环信的Demo代码写的还是很不错的,功能齐全,注释完整。值得学习和研究。

写在最后

多学习,多积累,多输出。!
 
附:最近两天实际工作采用环信SDK的开发详案

环信官方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集成开发详案及具体代码实现

EaseUI

实际工作过程中,我们是用不了太多东西的,如果只是集成个im最多用到的就是聊天列表和聊天页面。

我们来看重头戏EaseUI这个库。

官方文档

其实官方的WiKi已经介绍的特别详细了。官方EaseUI文档
我们来看Demo
 
// start chat acitivity
Intent intent = new Intent(getActivity(), ChatActivity.class);
if(conversation.isGroup()){
if(conversation.getType() == EMConversationType.ChatRoom){
// it's group chat
intent.putExtra(Constant.EXTRA_CHAT_TYPE, Constant.CHATTYPE_CHATROOM);
}else{
intent.putExtra(Constant.EXTRA_CHAT_TYPE, Constant.CHATTYPE_GROUP);
}

}
// it's single chat
intent.putExtra(Constant.EXTRA_USER_ID, username);
startActivity(intent);
ChatActivity

我们来看看ChatActivity
package com.hyphenate.chatuidemo.ui;

import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import com.hyphenate.chatuidemo.R;
import com.hyphenate.chatuidemo.runtimepermissions.PermissionsManager;
import com.hyphenate.easeui.ui.EaseChatFragment;
import com.hyphenate.util.EasyUtils;

/**
* chat activity,EaseChatFragment was used {@link #EaseChatFragment}
*
*/
public class ChatActivity extends BaseActivity{
public static ChatActivity activityInstance;
private EaseChatFragment chatFragment;
String toChatUsername;

@Override
protected void onCreate(Bundle arg0) {
super.onCreate(arg0);
setContentView(R.layout.em_activity_chat);
activityInstance = this;
//get user id or group id
toChatUsername = getIntent().getExtras().getString("userId");
//use EaseChatFratFragment
chatFragment = new ChatFragment();
//pass parameters to chat fragment
chatFragment.setArguments(getIntent().getExtras());
getSupportFragmentManager().beginTransaction().add(R.id.container, chatFragment).commit();

}

@Override
protected void onDestroy() {
super.onDestroy();
activityInstance = null;
}

@Override
protected void onNewIntent(Intent intent) {
// make sure only one chat activity is opened
String username = intent.getStringExtra("userId");
if (toChatUsername.equals(username))
super.onNewIntent(intent);
else {
finish();
startActivity(intent);
}

}

@Override
public void onBackPressed() {
chatFragment.onBackPressed();
if (EasyUtils.isSingleActivity(this)) {
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
}
}

public String getToChatUsername(){
return toChatUsername;
}

@Override public void onRequestPermissionsResult(int requestCode, @NonNull String permissions,
@NonNull int grantResults) {
PermissionsManager.getInstance().notifyPermissionsChange(permissions, grantResults);
}
}
官方文档是这么说的

封装EaseChatFragment的ChatFragment

那么Demo中是做了一层封装的。
package com.hyphenate.chatuidemo.ui;

import android.app.Activity;
import android.content.ClipData;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Toast;

import com.easemob.redpacketsdk.constant.RPConstant;
import com.easemob.redpacketui.utils.RPRedPacketUtil;
import com.easemob.redpacketui.utils.RedPacketUtil;
import com.easemob.redpacketui.widget.ChatRowRandomPacket;
import com.easemob.redpacketui.widget.ChatRowRedPacket;
import com.easemob.redpacketui.widget.ChatRowRedPacketAck;
import com.easemob.redpacketui.widget.ChatRowTransfer;
import com.hyphenate.chat.EMClient;
import com.hyphenate.chat.EMCmdMessageBody;
import com.hyphenate.chat.EMGroup;
import com.hyphenate.chat.EMMessage;
import com.hyphenate.chat.EMTextMessageBody;
import com.hyphenate.chatuidemo.Constant;
import com.hyphenate.chatuidemo.DemoHelper;
import com.hyphenate.chatuidemo.R;
import com.hyphenate.chatuidemo.domain.EmojiconExampleGroupData;
import com.hyphenate.chatuidemo.domain.RobotUser;
import com.hyphenate.chatuidemo.widget.ChatRowVoiceCall;
import com.hyphenate.easeui.EaseConstant;
import com.hyphenate.easeui.ui.EaseChatFragment;
import com.hyphenate.easeui.ui.EaseChatFragment.EaseChatFragmentHelper;
import com.hyphenate.easeui.widget.chatrow.EaseChatRow;
import com.hyphenate.easeui.widget.chatrow.EaseCustomChatRowProvider;
import com.hyphenate.easeui.widget.emojicon.EaseEmojiconMenu;
import com.hyphenate.util.EasyUtils;
import com.hyphenate.util.PathUtil;

import java.io.File;
import java.io.FileOutputStream;
import java.util.List;
import java.util.Map;

public class ChatFragment extends EaseChatFragment implements EaseChatFragmentHelper{

// constant start from 11 to avoid conflict with constant in base class
private static final int ITEM_VIDEO = 11;
private static final int ITEM_FILE = 12;
private static final int ITEM_VOICE_CALL = 13;
private static final int ITEM_VIDEO_CALL = 14;

private static final int REQUEST_CODE_SELECT_VIDEO = 11;
private static final int REQUEST_CODE_SELECT_FILE = 12;
private static final int REQUEST_CODE_GROUP_DETAIL = 13;
private static final int REQUEST_CODE_CONTEXT_MENU = 14;
private static final int REQUEST_CODE_SELECT_AT_USER = 15;


private static final int MESSAGE_TYPE_SENT_VOICE_CALL = 1;
private static final int MESSAGE_TYPE_RECV_VOICE_CALL = 2;
private static final int MESSAGE_TYPE_SENT_VIDEO_CALL = 3;
private static final int MESSAGE_TYPE_RECV_VIDEO_CALL = 4;

//red packet code : 红包功能使用的常量
private static final int MESSAGE_TYPE_RECV_RED_PACKET = 5;
private static final int MESSAGE_TYPE_SEND_RED_PACKET = 6;
private static final int MESSAGE_TYPE_SEND_RED_PACKET_ACK = 7;
private static final int MESSAGE_TYPE_RECV_RED_PACKET_ACK = 8;
private static final int MESSAGE_TYPE_RECV_TRANSFER_PACKET = 9;
private static final int MESSAGE_TYPE_SEND_TRANSFER_PACKET = 10;
private static final int MESSAGE_TYPE_RECV_RANDOM = 11;
private static final int MESSAGE_TYPE_SEND_RANDOM = 12;
private static final int REQUEST_CODE_SEND_RED_PACKET = 16;
private static final int ITEM_RED_PACKET = 16;
private static final int REQUEST_CODE_SEND_TRANSFER_PACKET = 17;
private static final int ITEM_TRANSFER_PACKET = 17;
//end of red packet code

/**
* if it is chatBot
*/
private boolean isRobot;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return super.onCreateView(inflater, container, savedInstanceState);
}

@Override
protected void setUpView() {
setChatFragmentListener(this);
if (chatType == Constant.CHATTYPE_SINGLE) {
Map<String,RobotUser> robotMap = DemoHelper.getInstance().getRobotList();
if(robotMap!=null && robotMap.containsKey(toChatUsername)){
isRobot = true;
}
}
super.setUpView();
// set click listener
titleBar.setLeftLayoutClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
if (EasyUtils.isSingleActivity(getActivity())) {
Intent intent = new Intent(getActivity(), MainActivity.class);
startActivity(intent);
}
onBackPressed();
}
});
((EaseEmojiconMenu)inputMenu.getEmojiconMenu()).addEmojiconGroup(EmojiconExampleGroupData.getData());
if(chatType == EaseConstant.CHATTYPE_GROUP){
inputMenu.getPrimaryMenu().getEditText().addTextChangedListener(new TextWatcher() {

@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if(count == 1 && "@".equals(String.valueOf(s.charAt(start)))){
startActivityForResult(new Intent(getActivity(), PickAtUserActivity.class).
putExtra("groupId", toChatUsername), REQUEST_CODE_SELECT_AT_USER);
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {

}
@Override
public void afterTextChanged(Editable s) {

}
});
}
}

@Override
protected void registerExtendMenuItem() {
//use the menu in base class
super.registerExtendMenuItem();
//extend menu items
inputMenu.registerExtendMenuItem(R.string.attach_video, R.drawable.em_chat_video_selector, ITEM_VIDEO, extendMenuItemClickListener);
inputMenu.registerExtendMenuItem(R.string.attach_file, R.drawable.em_chat_file_selector, ITEM_FILE, extendMenuItemClickListener);
if(chatType == Constant.CHATTYPE_SINGLE){
inputMenu.registerExtendMenuItem(R.string.attach_voice_call, R.drawable.em_chat_voice_call_selector, ITEM_VOICE_CALL, extendMenuItemClickListener);
inputMenu.registerExtendMenuItem(R.string.attach_video_call, R.drawable.em_chat_video_call_selector, ITEM_VIDEO_CALL, extendMenuItemClickListener);
}
//聊天室暂时不支持红包功能
//red packet code : 注册红包菜单选项
if (chatType != Constant.CHATTYPE_CHATROOM) {
inputMenu.registerExtendMenuItem(R.string.attach_red_packet, R.drawable.em_chat_red_packet_selector, ITEM_RED_PACKET, extendMenuItemClickListener);
}
//red packet code : 注册转账菜单选项
if (chatType == Constant.CHATTYPE_SINGLE) {
inputMenu.registerExtendMenuItem(R.string.attach_transfer_money, R.drawable.em_chat_transfer_selector, ITEM_TRANSFER_PACKET, extendMenuItemClickListener);
}
//end of red packet code
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_CONTEXT_MENU) {
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;

default:
break;
}
}
if(resultCode == Activity.RESULT_OK){
switch (requestCode) {
case REQUEST_CODE_SELECT_VIDEO: //send the video
if (data != null) {
int duration = data.getIntExtra("dur", 0);
String videoPath = data.getStringExtra("path");
File file = new File(PathUtil.getInstance().getImagePath(), "thvideo" + System.currentTimeMillis());
try {
FileOutputStream fos = new FileOutputStream(file);
Bitmap ThumbBitmap = ThumbnailUtils.createVideoThumbnail(videoPath, 3);
ThumbBitmap.compress(CompressFormat.JPEG, 100, fos);
fos.close();
sendVideoMessage(videoPath, file.getAbsolutePath(), duration);
} catch (Exception e) {
e.printStackTrace();
}
}
break;
case REQUEST_CODE_SELECT_FILE: //send the file
if (data != null) {
Uri uri = data.getData();
if (uri != null) {
sendFileByUri(uri);
}
}
break;
case REQUEST_CODE_SELECT_AT_USER:
if(data != null){
String username = data.getStringExtra("username");
inputAtUsername(username, false);
}
break;
//red packet code : 发送红包消息到聊天界面
case REQUEST_CODE_SEND_RED_PACKET:
if (data != null){
sendMessage(RedPacketUtil.createRPMessage(getActivity(), data, toChatUsername));
}
break;
case REQUEST_CODE_SEND_TRANSFER_PACKET://发送转账消息
if (data != null) {
sendMessage(RedPacketUtil.createTRMessage(getActivity(), data, toChatUsername));
}
break;
//end of red packet code
default:
break;
}
}

}

@Override
public void onSetMessageAttributes(EMMessage message) {
if(isRobot){
//set message extension
message.setAttribute("em_robot_message", isRobot);
}
}

@Override
public EaseCustomChatRowProvider onSetCustomChatRowProvider() {
return new CustomChatRowProvider();
}


@Override
public void onEnterToChatDetails() {
if (chatType == Constant.CHATTYPE_GROUP) {
EMGroup group = EMClient.getInstance().groupManager().getGroup(toChatUsername);
if (group == null) {
Toast.makeText(getActivity(), R.string.gorup_not_found, Toast.LENGTH_SHORT).show();
return;
}
startActivityForResult(
(new Intent(getActivity(), GroupDetailsActivity.class).putExtra("groupId", toChatUsername)),
REQUEST_CODE_GROUP_DETAIL);
}else if(chatType == Constant.CHATTYPE_CHATROOM){
startActivityForResult(new Intent(getActivity(), ChatRoomDetailsActivity.class).putExtra("roomId", toChatUsername), REQUEST_CODE_GROUP_DETAIL);
}
}

@Override
public void onAvatarClick(String username) {
//handling when user click avatar
Intent intent = new Intent(getActivity(), UserProfileActivity.class);
intent.putExtra("username", username);
startActivity(intent);
}

@Override
public void onAvatarLongClick(String username) {
inputAtUsername(username);
}


@Override
public boolean onMessageBubbleClick(EMMessage message) {
//消息框点击事件,demo这里不做覆盖,如需覆盖,return true
//red packet code : 拆红包页面
if (message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_RED_PACKET_MESSAGE, false)){
if (RedPacketUtil.isRandomRedPacket(message)){
RedPacketUtil.openRandomPacket(getActivity(),message);
} else {
RedPacketUtil.openRedPacket(getActivity(), chatType, message, toChatUsername, messageList);
}
return true;
} else if (message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_TRANSFER_PACKET_MESSAGE, false)) {
RedPacketUtil.openTransferPacket(getActivity(), message);
return true;
}
//end of red packet code
return false;
}
@Override
public void onCmdMessageReceived(List<EMMessage> messages) {
//red packet code : 处理红包回执透传消息
for (EMMessage message : messages) {
EMCmdMessageBody cmdMsgBody = (EMCmdMessageBody) message.getBody();
String action = cmdMsgBody.action();//获取自定义action
if (action.equals(RPConstant.REFRESH_GROUP_RED_PACKET_ACTION)){
RedPacketUtil.receiveRedPacketAckMessage(message);
messageList.refresh();
}
}
//end of red packet code
super.onCmdMessageReceived(messages);
}

@Override
public void onMessageBubbleLongClick(EMMessage message) {
// no message forward when in chat room
startActivityForResult((new Intent(getActivity(), ContextMenuActivity.class)).putExtra("message",message)
.putExtra("ischatroom", chatType == EaseConstant.CHATTYPE_CHATROOM),
REQUEST_CODE_CONTEXT_MENU);
}

@Override
public boolean onExtendMenuItemClick(int itemId, View view) {
switch (itemId) {
case ITEM_VIDEO:
Intent intent = new Intent(getActivity(), ImageGridActivity.class);
startActivityForResult(intent, REQUEST_CODE_SELECT_VIDEO);
break;
case ITEM_FILE: //file
selectFileFromLocal();
break;
case ITEM_VOICE_CALL:
startVoiceCall();
break;
case ITEM_VIDEO_CALL:
startVideoCall();
break;
//red packet code : 进入发红包页面
case ITEM_RED_PACKET:
if (chatType == Constant.CHATTYPE_SINGLE) {
//单聊红包修改进入红包的方法,可以在小额随机红包和普通单聊红包之间切换
RedPacketUtil.startRandomPacket(new RPRedPacketUtil.RPRandomCallback() {
@Override
public void onSendPacketSuccess(Intent data) {
sendMessage(RedPacketUtil.createRPMessage(getActivity(), data, toChatUsername));
}

@Override
public void switchToNormalPacket() {
RedPacketUtil.startRedPacketActivityForResult(ChatFragment.this, chatType, toChatUsername, REQUEST_CODE_SEND_RED_PACKET);
}
},getActivity(),toChatUsername);
} else {
RedPacketUtil.startRedPacketActivityForResult(this, chatType, toChatUsername, REQUEST_CODE_SEND_RED_PACKET);
}
break;
case ITEM_TRANSFER_PACKET://进入转账页面
RedPacketUtil.startTransferActivityForResult(this, toChatUsername, REQUEST_CODE_SEND_TRANSFER_PACKET);
break;
//end of red packet code
default:
break;
}
//keep exist extend menu
return false;
}

/**
* select file
*/
protected void selectFileFromLocal() {
Intent intent = null;
if (Build.VERSION.SDK_INT < 19) { //api 19 and later, we can't use this way, demo just select from images
intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("*/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);

} else {
intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
}
startActivityForResult(intent, REQUEST_CODE_SELECT_FILE);
}

/**
* make a voice call
*/
protected void startVoiceCall() {
if (!EMClient.getInstance().isConnected()) {
Toast.makeText(getActivity(), R.string.not_connect_to_server, Toast.LENGTH_SHORT).show();
} else {
startActivity(new Intent(getActivity(), VoiceCallActivity.class).putExtra("username", toChatUsername)
.putExtra("isComingCall", false));
// voiceCallBtn.setEnabled(false);
inputMenu.hideExtendMenuContainer();
}
}

/**
* make a video call
*/
protected void startVideoCall() {
if (!EMClient.getInstance().isConnected())
Toast.makeText(getActivity(), R.string.not_connect_to_server, Toast.LENGTH_SHORT).show();
else {
startActivity(new Intent(getActivity(), VideoCallActivity.class).putExtra("username", toChatUsername)
.putExtra("isComingCall", false));
// videoCallBtn.setEnabled(false);
inputMenu.hideExtendMenuContainer();
}
}

/**
* chat row provider
*
*/
private final class CustomChatRowProvider implements EaseCustomChatRowProvider {
@Override
public int getCustomChatRowTypeCount() {
//here the number is the message type in EMMessage::Type
//which is used to count the number of different chat row
return 12;
}

@Override
public int getCustomChatRowType(EMMessage message) {
if(message.getType() == EMMessage.Type.TXT){
//voice call
if (message.getBooleanAttribute(Constant.MESSAGE_ATTR_IS_VOICE_CALL, false)){
return message.direct() == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_VOICE_CALL : MESSAGE_TYPE_SENT_VOICE_CALL;
}else if (message.getBooleanAttribute(Constant.MESSAGE_ATTR_IS_VIDEO_CALL, false)){
//video call
return message.direct() == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_VIDEO_CALL : MESSAGE_TYPE_SENT_VIDEO_CALL;
}
//red packet code : 红包消息、红包回执消息以及转账消息的chatrow type
else if (RedPacketUtil.isRandomRedPacket(message)) {
//小额随机红包
return message.direct() == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_RANDOM : MESSAGE_TYPE_SEND_RANDOM;
} else if (message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_RED_PACKET_MESSAGE, false)) {
//发送红包消息
return message.direct() == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_RED_PACKET : MESSAGE_TYPE_SEND_RED_PACKET;
} else if (message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_RED_PACKET_ACK_MESSAGE, false)) {
//领取红包消息
return message.direct() == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_RED_PACKET_ACK : MESSAGE_TYPE_SEND_RED_PACKET_ACK;
} else if (message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_TRANSFER_PACKET_MESSAGE, false)) {
//转账消息
return message.direct() == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_TRANSFER_PACKET : MESSAGE_TYPE_SEND_TRANSFER_PACKET;
}
//end of red packet code
}
return 0;
}

@Override
public EaseChatRow getCustomChatRow(EMMessage message, int position, BaseAdapter adapter) {
if(message.getType() == EMMessage.Type.TXT){
// voice call or video call
if (message.getBooleanAttribute(Constant.MESSAGE_ATTR_IS_VOICE_CALL, false) ||
message.getBooleanAttribute(Constant.MESSAGE_ATTR_IS_VIDEO_CALL, false)){
return new ChatRowVoiceCall(getActivity(), message, position, adapter);
}
//red packet code : 红包消息、红包回执消息以及转账消息的chat row
else if (RedPacketUtil.isRandomRedPacket(message)) {//小额随机红包
return new ChatRowRandomPacket(getActivity(), message, position, adapter);
} else if (message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_RED_PACKET_MESSAGE, false)) {//红包消息
return new ChatRowRedPacket(getActivity(), message, position, adapter);
} else if (message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_RED_PACKET_ACK_MESSAGE, false)) {//红包回执消息
return new ChatRowRedPacketAck(getActivity(), message, position, adapter);
} else if (message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_TRANSFER_PACKET_MESSAGE, false)) {//转账消息
return new ChatRowTransfer(getActivity(), message, position, adapter);
}
//end of red packet code
}
return null;
}

}

}
判断是不是机器人及添加监听
 
setChatFragmentListener(this);
if (chatType == Constant.CHATTYPE_SINGLE) {
Map<String,RobotUser> robotMap = DemoHelper.getInstance().getRobotList();
if(robotMap!=null && robotMap.containsKey(toChatUsername)){
isRobot = true;
}
}
点击标题返回及群聊@别人的功能​
// set click listener
titleBar.setLeftLayoutClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
if (EasyUtils.isSingleActivity(getActivity())) {
Intent intent = new Intent(getActivity(), MainActivity.class);
startActivity(intent);
}
onBackPressed();
}
});
((EaseEmojiconMenu)inputMenu.getEmojiconMenu()).addEmojiconGroup(EmojiconExampleGroupData.getData());
if(chatType == EaseConstant.CHATTYPE_GROUP){
inputMenu.getPrimaryMenu().getEditText().addTextChangedListener(new TextWatcher() {

@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if(count == 1 && "@".equals(String.valueOf(s.charAt(start)))){
startActivityForResult(new Intent(getActivity(), PickAtUserActivity.class).
putExtra("groupId", toChatUsername), REQUEST_CODE_SELECT_AT_USER);
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {

}
@Override
public void afterTextChanged(Editable s) {

}
});
}
菜单的操作​
 
super.registerExtendMenuItem();
//extend menu items
inputMenu.registerExtendMenuItem(R.string.attach_video, R.drawable.em_chat_video_selector, ITEM_VIDEO, extendMenuItemClickListener);
inputMenu.registerExtendMenuItem(R.string.attach_file, R.drawable.em_chat_file_selector, ITEM_FILE, extendMenuItemClickListener);
if(chatType == Constant.CHATTYPE_SINGLE){
inputMenu.registerExtendMenuItem(R.string.attach_voice_call, R.drawable.em_chat_voice_call_selector, ITEM_VOICE_CALL, extendMenuItemClickListener);
inputMenu.registerExtendMenuItem(R.string.attach_video_call, R.drawable.em_chat_video_call_selector, ITEM_VIDEO_CALL, extendMenuItemClickListener);
}
//聊天室暂时不支持红包功能
//red packet code : 注册红包菜单选项
if (chatType != Constant.CHATTYPE_CHATROOM) {
inputMenu.registerExtendMenuItem(R.string.attach_red_packet, R.drawable.em_chat_red_packet_selector, ITEM_RED_PACKET, extendMenuItemClickListener);
}
//red packet code : 注册转账菜单选项
if (chatType == Constant.CHATTYPE_SINGLE) {
inputMenu.registerExtendMenuItem(R.string.attach_transfer_money, R.drawable.em_chat_transfer_selector, ITEM_TRANSFER_PACKET, extendMenuItemClickListener);
}
//end of red packet code
一些功能操作​
if (requestCode == REQUEST_CODE_CONTEXT_MENU) {
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;

default:
break;
}
}
if(resultCode == Activity.RESULT_OK){
switch (requestCode) {
case REQUEST_CODE_SELECT_VIDEO: //send the video
if (data != null) {
int duration = data.getIntExtra("dur", 0);
String videoPath = data.getStringExtra("path");
File file = new File(PathUtil.getInstance().getImagePath(), "thvideo" + System.currentTimeMillis());
try {
FileOutputStream fos = new FileOutputStream(file);
Bitmap ThumbBitmap = ThumbnailUtils.createVideoThumbnail(videoPath, 3);
ThumbBitmap.compress(CompressFormat.JPEG, 100, fos);
fos.close();
sendVideoMessage(videoPath, file.getAbsolutePath(), duration);
} catch (Exception e) {
e.printStackTrace();
}
}
break;
case REQUEST_CODE_SELECT_FILE: //send the file
if (data != null) {
Uri uri = data.getData();
if (uri != null) {
sendFileByUri(uri);
}
}
break;
case REQUEST_CODE_SELECT_AT_USER:
if(data != null){
String username = data.getStringExtra("username");
inputAtUsername(username, false);
}
break;
//red packet code : 发送红包消息到聊天界面
case REQUEST_CODE_SEND_RED_PACKET:
if (data != null){
sendMessage(RedPacketUtil.createRPMessage(getActivity(), data, toChatUsername));
}
break;
case REQUEST_CODE_SEND_TRANSFER_PACKET://发送转账消息
if (data != null) {
sendMessage(RedPacketUtil.createTRMessage(getActivity(), data, toChatUsername));
}
break;
//end of red packet code
default:
break;
}
}
进入聊天详情​
 
@Override
public void onEnterToChatDetails() {
if (chatType == Constant.CHATTYPE_GROUP) {
EMGroup group = EMClient.getInstance().groupManager().getGroup(toChatUsername);
if (group == null) {
Toast.makeText(getActivity(), R.string.gorup_not_found, Toast.LENGTH_SHORT).show();
return;
}
startActivityForResult(
(new Intent(getActivity(), GroupDetailsActivity.class).putExtra("groupId", toChatUsername)),
REQUEST_CODE_GROUP_DETAIL);
}else if(chatType == Constant.CHATTYPE_CHATROOM){
startActivityForResult(new Intent(getActivity(), ChatRoomDetailsActivity.class).putExtra("roomId", toChatUsername), REQUEST_CODE_GROUP_DETAIL);
}
}
点击头像​
@Override
public void onAvatarClick(String username) {
//handling when user click avatar
Intent intent = new Intent(getActivity(), UserProfileActivity.class);
intent.putExtra("username", username);
startActivity(intent);
}
消息框点击事件、拆红包
@Override
public boolean onMessageBubbleClick(EMMessage message) {
//消息框点击事件,demo这里不做覆盖,如需覆盖,return true
//red packet code : 拆红包页面
if (message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_RED_PACKET_MESSAGE, false)){
if (RedPacketUtil.isRandomRedPacket(message)){
RedPacketUtil.openRandomPacket(getActivity(),message);
} else {
RedPacketUtil.openRedPacket(getActivity(), chatType, message, toChatUsername, messageList);
}
return true;
} else if (message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_TRANSFER_PACKET_MESSAGE, false)) {
RedPacketUtil.openTransferPacket(getActivity(), message);
return true;
}
//end of red packet code
return false;
}
红包回执及消息框长按​
@Override
public void onCmdMessageReceived(List<EMMessage> messages) {
//red packet code : 处理红包回执透传消息
for (EMMessage message : messages) {
EMCmdMessageBody cmdMsgBody = (EMCmdMessageBody) message.getBody();
String action = cmdMsgBody.action();//获取自定义action
if (action.equals(RPConstant.REFRESH_GROUP_RED_PACKET_ACTION)){
RedPacketUtil.receiveRedPacketAckMessage(message);
messageList.refresh();
}
}
//end of red packet code
super.onCmdMessageReceived(messages);
}

@Override
public void onMessageBubbleLongClick(EMMessage message) {
// no message forward when in chat room
startActivityForResult((new Intent(getActivity(), ContextMenuActivity.class)).putExtra("message",message)
.putExtra("ischatroom", chatType == EaseConstant.CHATTYPE_CHATROOM),
REQUEST_CODE_CONTEXT_MENU);
}
扩展按钮​
@Override
public boolean onExtendMenuItemClick(int itemId, View view) {
switch (itemId) {
case ITEM_VIDEO:
Intent intent = new Intent(getActivity(), ImageGridActivity.class);
startActivityForResult(intent, REQUEST_CODE_SELECT_VIDEO);
break;
case ITEM_FILE: //file
selectFileFromLocal();
break;
case ITEM_VOICE_CALL:
startVoiceCall();
break;
case ITEM_VIDEO_CALL:
startVideoCall();
break;
//red packet code : 进入发红包页面
case ITEM_RED_PACKET:
if (chatType == Constant.CHATTYPE_SINGLE) {
//单聊红包修改进入红包的方法,可以在小额随机红包和普通单聊红包之间切换
RedPacketUtil.startRandomPacket(new RPRedPacketUtil.RPRandomCallback() {
@Override
public void onSendPacketSuccess(Intent data) {
sendMessage(RedPacketUtil.createRPMessage(getActivity(), data, toChatUsername));
}

@Override
public void switchToNormalPacket() {
RedPacketUtil.startRedPacketActivityForResult(ChatFragment.this, chatType, toChatUsername, REQUEST_CODE_SEND_RED_PACKET);
}
},getActivity(),toChatUsername);
} else {
RedPacketUtil.startRedPacketActivityForResult(this, chatType, toChatUsername, REQUEST_CODE_SEND_RED_PACKET);
}
break;
case ITEM_TRANSFER_PACKET://进入转账页面
RedPacketUtil.startTransferActivityForResult(this, toChatUsername, REQUEST_CODE_SEND_TRANSFER_PACKET);
break;
//end of red packet code
default:
break;
}
//keep exist extend menu
return false;
}
本地文件选择、语音通话、视频通话、及自定义chatrow类型​
 
/**
* select file
*/
protected void selectFileFromLocal() {
Intent intent = null;
if (Build.VERSION.SDK_INT < 19) { //api 19 and later, we can't use this way, demo just select from images
intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("*/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);

} else {
intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
}
startActivityForResult(intent, REQUEST_CODE_SELECT_FILE);
}

/**
* make a voice call
*/
protected void startVoiceCall() {
if (!EMClient.getInstance().isConnected()) {
Toast.makeText(getActivity(), R.string.not_connect_to_server, Toast.LENGTH_SHORT).show();
} else {
startActivity(new Intent(getActivity(), VoiceCallActivity.class).putExtra("username", toChatUsername)
.putExtra("isComingCall", false));
// voiceCallBtn.setEnabled(false);
inputMenu.hideExtendMenuContainer();
}
}

/**
* make a video call
*/
protected void startVideoCall() {
if (!EMClient.getInstance().isConnected())
Toast.makeText(getActivity(), R.string.not_connect_to_server, Toast.LENGTH_SHORT).show();
else {
startActivity(new Intent(getActivity(), VideoCallActivity.class).putExtra("username", toChatUsername)
.putExtra("isComingCall", false));
// videoCallBtn.setEnabled(false);
inputMenu.hideExtendMenuContainer();
}
}

/**
* chat row provider
*
*/
private final class CustomChatRowProvider implements EaseCustomChatRowProvider {
@Override
public int getCustomChatRowTypeCount() {
//here the number is the message type in EMMessage::Type
//which is used to count the number of different chat row
return 12;
}

@Override
public int getCustomChatRowType(EMMessage message) {
if(message.getType() == EMMessage.Type.TXT){
//voice call
if (message.getBooleanAttribute(Constant.MESSAGE_ATTR_IS_VOICE_CALL, false)){
return message.direct() == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_VOICE_CALL : MESSAGE_TYPE_SENT_VOICE_CALL;
}else if (message.getBooleanAttribute(Constant.MESSAGE_ATTR_IS_VIDEO_CALL, false)){
//video call
return message.direct() == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_VIDEO_CALL : MESSAGE_TYPE_SENT_VIDEO_CALL;
}
//red packet code : 红包消息、红包回执消息以及转账消息的chatrow type
else if (RedPacketUtil.isRandomRedPacket(message)) {
//小额随机红包
return message.direct() == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_RANDOM : MESSAGE_TYPE_SEND_RANDOM;
} else if (message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_RED_PACKET_MESSAGE, false)) {
//发送红包消息
return message.direct() == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_RED_PACKET : MESSAGE_TYPE_SEND_RED_PACKET;
} else if (message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_RED_PACKET_ACK_MESSAGE, false)) {
//领取红包消息
return message.direct() == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_RED_PACKET_ACK : MESSAGE_TYPE_SEND_RED_PACKET_ACK;
} else if (message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_TRANSFER_PACKET_MESSAGE, false)) {
//转账消息
return message.direct() == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_TRANSFER_PACKET : MESSAGE_TYPE_SEND_TRANSFER_PACKET;
}
//end of red packet code
}
return 0;
}

@Override
public EaseChatRow getCustomChatRow(EMMessage message, int position, BaseAdapter adapter) {
if(message.getType() == EMMessage.Type.TXT){
// voice call or video call
if (message.getBooleanAttribute(Constant.MESSAGE_ATTR_IS_VOICE_CALL, false) ||
message.getBooleanAttribute(Constant.MESSAGE_ATTR_IS_VIDEO_CALL, false)){
return new ChatRowVoiceCall(getActivity(), message, position, adapter);
}
//red packet code : 红包消息、红包回执消息以及转账消息的chat row
else if (RedPacketUtil.isRandomRedPacket(message)) {//小额随机红包
return new ChatRowRandomPacket(getActivity(), message, position, adapter);
} else if (message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_RED_PACKET_MESSAGE, false)) {//红包消息
return new ChatRowRedPacket(getActivity(), message, position, adapter);
} else if (message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_RED_PACKET_ACK_MESSAGE, false)) {//红包回执消息
return new ChatRowRedPacketAck(getActivity(), message, position, adapter);
} else if (message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_TRANSFER_PACKET_MESSAGE, false)) {//转账消息
return new ChatRowTransfer(getActivity(), message, position, adapter);
}
//end of red packet code
}
return null;
}

}
Redpacketlibrary

由于业务未涉及,暂不作分析。
 
总结及其他

其实正常集成,按照于海同学所说也就半天时间,这是因为的确环信的SDK使用起来比较方便。

通过大致的阅读代码,环信的Demo代码写的还是很不错的,功能齐全,注释完整。值得学习和研究。

写在最后

多学习,多积累,多输出。!
 
附:最近两天实际工作采用环信SDK的开发详案

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

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

随缘 发表了文章 • 245 次浏览 • 2017-02-21 16:11 • 来自相关话题

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

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

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

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

环信官方Demo源码分析及SDK简单应用-主界面的三个fragment-通讯录界面
 
环信官方Demo源码分析及SDK简单应用-主界面的三个fragment-设置界面
 
环信官方Demo源码分析及SDK简单应用-EaseUI
 
环信官方Demo源码分析及SDK简单应用-IM集成开发详案及具体代码实现
 
设置界面

我们来贴代码

跟我们平常写的什么我的界面是大同小异的。主要有这些,其大多设置与demoModel有关

零钱RedPacketUtil.startChangeActivity(getActivity());接受新消息通知settingsModel.setSettingMsgNotification(false);
PreferenceManager.getInstance().setSettingMsgNotification(paramBoolean);
valueCache.put(Key.VibrateAndPlayToneOn, paramBoolean);声音​settingsModel.setSettingMsgSound(false);震动​settingsModel.setSettingMsgVibrate(false);消息推送设置

使用扬声器播放语音settingsModel.setSettingMsgSpeaker(false);自定义AppKey​settingsModel.enableCustomAppkey(false);自定义server​settingsModel.enableCustomServer(false); settingsModel.enableCustomServer(false);个人资料​startActivity(new Intent(getActivity(), UserProfileActivity.class).putExtra("setting", true)
.putExtra("username", EMClient.getInstance().getCurrentUser()));通讯录黑名单​startActivity(new Intent(getActivity(), BlacklistActivity.class));诊断​startActivity(new Intent(getActivity(), DiagnoseActivity.class));IOS离线推送昵称​startActivity(new Intent(getActivity(), OfflinePushNickActivity.class));通话设置​startActivity(new Intent(getActivity(), CallOptionActivity.class));允许聊天室群主离开​settingsModel.allowChatroomOwnerLeave(false);
chatOptions.allowChatroomOwnerLeave(false);退出群组时删除聊天数据​settingsModel.setDeleteMessagesAsExitGroup(false);
chatOptions.setDeleteMessagesAsExitGroup(false);自动同意群组加群邀请settingsModel.setAutoAcceptGroupInvitation(false);
chatOptions.setAutoAcceptGroupInvitation(false);视频自适应编码​settingsModel.setAdaptiveVideoEncode(false);
EMClient.getInstance().callManager().getCallOptions().enableFixedVideoResolution(true);退出登录​DemoHelper.getInstance().logout(false,new EMCallBack() {

@Override
public void onSuccess() {
getActivity().runOnUiThread(new Runnable() {
public void run() {
pd.dismiss();
// show login screen
((MainActivity) getActivity()).finish();
startActivity(new Intent(getActivity(), LoginActivity.class));

}
});
}

@Override
public void onProgress(int progress, String status) {

}

@Override
public void onError(int code, String message) {
getActivity().runOnUiThread(new Runnable() {

@Override
public void run() {
// TODO Auto-generated method stub
pd.dismiss();
Toast.makeText(getActivity(), "unbind devicetokens failed", Toast.LENGTH_SHORT).show();
}
});
}
});到这里主界面的三个fragment就都讲完了,我们来看重头戏。
 
环信官方Demo源码分析及SDK简单应用-EaseUI 查看全部
环信官方Demo源码分析及SDK简单应用

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

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

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

环信官方Demo源码分析及SDK简单应用-主界面的三个fragment-通讯录界面
 
环信官方Demo源码分析及SDK简单应用-主界面的三个fragment-设置界面
 
环信官方Demo源码分析及SDK简单应用-EaseUI
 
环信官方Demo源码分析及SDK简单应用-IM集成开发详案及具体代码实现
 
设置界面

我们来贴代码

跟我们平常写的什么我的界面是大同小异的。主要有这些,其大多设置与demoModel有关

零钱
RedPacketUtil.startChangeActivity(getActivity());
接受新消息通知
settingsModel.setSettingMsgNotification(false);
 
PreferenceManager.getInstance().setSettingMsgNotification(paramBoolean);
valueCache.put(Key.VibrateAndPlayToneOn, paramBoolean);
声音​
settingsModel.setSettingMsgSound(false);
震动​
settingsModel.setSettingMsgVibrate(false);
消息推送设置

使用扬声器播放语音
settingsModel.setSettingMsgSpeaker(false);
自定义AppKey​
settingsModel.enableCustomAppkey(false);
自定义server​
settingsModel.enableCustomServer(false);	settingsModel.enableCustomServer(false);
个人资料​
startActivity(new Intent(getActivity(), UserProfileActivity.class).putExtra("setting", true)
.putExtra("username", EMClient.getInstance().getCurrentUser()));
通讯录黑名单​
startActivity(new Intent(getActivity(), BlacklistActivity.class));
诊断​
startActivity(new Intent(getActivity(), DiagnoseActivity.class));
IOS离线推送昵称​
startActivity(new Intent(getActivity(), OfflinePushNickActivity.class));
通话设置​
startActivity(new Intent(getActivity(), CallOptionActivity.class));
允许聊天室群主离开​
settingsModel.allowChatroomOwnerLeave(false);
chatOptions.allowChatroomOwnerLeave(false);
退出群组时删除聊天数据​
settingsModel.setDeleteMessagesAsExitGroup(false);
chatOptions.setDeleteMessagesAsExitGroup(false);
自动同意群组加群邀请
settingsModel.setAutoAcceptGroupInvitation(false);
chatOptions.setAutoAcceptGroupInvitation(false);
视频自适应编码​
settingsModel.setAdaptiveVideoEncode(false);
EMClient.getInstance().callManager().getCallOptions().enableFixedVideoResolution(true);
退出登录​
DemoHelper.getInstance().logout(false,new EMCallBack() {

@Override
public void onSuccess() {
getActivity().runOnUiThread(new Runnable() {
public void run() {
pd.dismiss();
// show login screen
((MainActivity) getActivity()).finish();
startActivity(new Intent(getActivity(), LoginActivity.class));

}
});
}

@Override
public void onProgress(int progress, String status) {

}

@Override
public void onError(int code, String message) {
getActivity().runOnUiThread(new Runnable() {

@Override
public void run() {
// TODO Auto-generated method stub
pd.dismiss();
Toast.makeText(getActivity(), "unbind devicetokens failed", Toast.LENGTH_SHORT).show();
}
});
}
});
到这里主界面的三个fragment就都讲完了,我们来看重头戏。
 
环信官方Demo源码分析及SDK简单应用-EaseUI
0
评论

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

随缘 发表了文章 • 404 次浏览 • 2017-02-21 15:37 • 来自相关话题

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

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

环信官方Demo源码分析及SDK简单应用-LoginActivity
 
环信官方Demo源码分析及SDK简单应用-主界面的三个fragment-会话界面
 
环信官方Demo源码分析及SDK简单应用-主界面的三个fragment-通讯录界面
 
环信官方Demo源码分析及SDK简单应用-主界面的三个fragment-设置界面
 
环信官方Demo源码分析及SDK简单应用-EaseUI
 
环信官方Demo源码分析及SDK简单应用-IM集成开发详案及具体代码实现
 
现在来看具体的主界面的三个Fragment
主界面的三个fragment
会话界面

​ 我们来看会话界面的代码
package com.hyphenate.chatuidemo.ui;

import android.content.Intent;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.easemob.redpacketsdk.constant.RPConstant;
import com.hyphenate.chat.EMClient;
import com.hyphenate.chat.EMConversation;
import com.hyphenate.chat.EMConversation.EMConversationType;
import com.hyphenate.chat.EMMessage;
import com.hyphenate.chatuidemo.Constant;
import com.hyphenate.chatuidemo.R;
import com.hyphenate.chatuidemo.db.InviteMessgeDao;
import com.hyphenate.easeui.model.EaseAtMessageHelper;
import com.hyphenate.easeui.ui.EaseConversationListFragment;
import com.hyphenate.easeui.widget.EaseConversationList.EaseConversationListHelper;
import com.hyphenate.util.NetUtils;

public class ConversationListFragment extends EaseConversationListFragment{

private TextView errorText;

@Override
protected void initView() {
super.initView();
View errorView = (LinearLayout) View.inflate(getActivity(),R.layout.em_chat_neterror_item, null);
errorItemContainer.addView(errorView);
errorText = (TextView) errorView.findViewById(R.id.tv_connect_errormsg);
}

@Override
protected void setUpView() {
super.setUpView();
// register context menu
registerForContextMenu(conversationListView);
conversationListView.setOnItemClickListener(new OnItemClickListener() {

@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
EMConversation conversation = conversationListView.getItem(position);
String username = conversation.conversationId();
if (username.equals(EMClient.getInstance().getCurrentUser()))
Toast.makeText(getActivity(), R.string.Cant_chat_with_yourself, Toast.LENGTH_SHORT).show();
else {
// start chat acitivity
Intent intent = new Intent(getActivity(), ChatActivity.class);
if(conversation.isGroup()){
if(conversation.getType() == EMConversationType.ChatRoom){
// it's group chat
intent.putExtra(Constant.EXTRA_CHAT_TYPE, Constant.CHATTYPE_CHATROOM);
}else{
intent.putExtra(Constant.EXTRA_CHAT_TYPE, Constant.CHATTYPE_GROUP);
}

}
// it's single chat
intent.putExtra(Constant.EXTRA_USER_ID, username);
startActivity(intent);
}
}
});
//red packet code : 红包回执消息在会话列表最后一条消息的展示
conversationListView.setConversationListHelper(new EaseConversationListHelper() {
@Override
public String onSetItemSecondaryText(EMMessage lastMessage) {
if (lastMessage.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_RED_PACKET_ACK_MESSAGE, false)) {
String sendNick = lastMessage.getStringAttribute(RPConstant.EXTRA_RED_PACKET_SENDER_NAME, "");
String receiveNick = lastMessage.getStringAttribute(RPConstant.EXTRA_RED_PACKET_RECEIVER_NAME, "");
String msg;
if (lastMessage.direct() == EMMessage.Direct.RECEIVE) {
msg = String.format(getResources().getString(R.string.msg_someone_take_red_packet), receiveNick);
} else {
if (sendNick.equals(receiveNick)) {
msg = getResources().getString(R.string.msg_take_red_packet);
} else {
msg = String.format(getResources().getString(R.string.msg_take_someone_red_packet), sendNick);
}
}
return msg;
} else if (lastMessage.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_TRANSFER_PACKET_MESSAGE, false)) {
String transferAmount = lastMessage.getStringAttribute(RPConstant.EXTRA_TRANSFER_AMOUNT, "");
String msg;
if (lastMessage.direct() == EMMessage.Direct.RECEIVE) {
msg = String.format(getResources().getString(R.string.msg_transfer_to_you), transferAmount);
} else {
msg = String.format(getResources().getString(R.string.msg_transfer_from_you),transferAmount);
}
return msg;
}
return null;
}
});
super.setUpView();
//end of red packet code
}

@Override
protected void onConnectionDisconnected() {
super.onConnectionDisconnected();
if (NetUtils.hasNetwork(getActivity())){
errorText.setText(R.string.can_not_connect_chat_server_connection);
} else {
errorText.setText(R.string.the_current_network);
}
}


@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
getActivity().getMenuInflater().inflate(R.menu.em_delete_message, menu);
}

@Override
public boolean onContextItemSelected(MenuItem item) {
boolean deleteMessage = false;
if (item.getItemId() == R.id.delete_message) {
deleteMessage = true;
} else if (item.getItemId() == R.id.delete_conversation) {
deleteMessage = false;
}
EMConversation tobeDeleteCons = conversationListView.getItem(((AdapterContextMenuInfo) item.getMenuInfo()).position);
if (tobeDeleteCons == null) {
return true;
}
if(tobeDeleteCons.getType() == EMConversationType.GroupChat){
EaseAtMessageHelper.get().removeAtMeGroup(tobeDeleteCons.conversationId());
}
try {
// delete conversation
EMClient.getInstance().chatManager().deleteConversation(tobeDeleteCons.conversationId(), deleteMessage);
InviteMessgeDao inviteMessgeDao = new InviteMessgeDao(getActivity());
inviteMessgeDao.deleteMessage(tobeDeleteCons.conversationId());
} catch (Exception e) {
e.printStackTrace();
}
refresh();

// update unread count
((MainActivity) getActivity()).updateUnreadLabel();
return true;
}

}我们还是挨个来读代码public class ConversationListFragment extends EaseConversationListFragment来,我们还是得先去找他爹算账。public class EaseConversationListFragment extends EaseBaseFragment哎呀,我们再去找他爷爷。 public abstract class EaseBaseFragment extends Fragment爷爷终于正常点是从Android系统类继承下来的了,我们看具体的代码

EaseBaseFragmentpackage com.hyphenate.easeui.ui;

import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;

import com.hyphenate.easeui.R;
import com.hyphenate.easeui.widget.EaseTitleBar;

public abstract class EaseBaseFragment extends Fragment{
protected EaseTitleBar titleBar;
protected InputMethodManager inputMethodManager;

@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
inputMethodManager = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
//noinspection ConstantConditions
titleBar = (EaseTitleBar) getView().findViewById(R.id.title_bar);

initView();
setUpView();
}

public void showTitleBar(){
if(titleBar != null){
titleBar.setVisibility(View.VISIBLE);
}
}

public void hideTitleBar(){
if(titleBar != null){
titleBar.setVisibility(View.GONE);
}
}

protected void hideSoftKeyboard() {
if (getActivity().getWindow().getAttributes().softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN) {
if (getActivity().getCurrentFocus() != null)
inputMethodManager.hideSoftInputFromWindow(getActivity().getCurrentFocus().getWindowToken(),
InputMethodManager.HIDE_NOT_ALWAYS);
}
}

protected abstract void initView();

protected abstract void setUpView();


}我们还是挨个来看代码,研究他的功能。@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
inputMethodManager = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
//noinspection ConstantConditions
titleBar = (EaseTitleBar) getView().findViewById(R.id.title_bar);

initView();
setUpView();
}隐藏输入法

看到inputmethdManager要干嘛啊,隐藏键盘。果不其然。protected void hideSoftKeyboard() {
if (getActivity().getWindow().getAttributes().softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN) {
if (getActivity().getCurrentFocus() != null)
inputMethodManager.hideSoftInputFromWindow(getActivity().getCurrentFocus().getWindowToken(),
InputMethodManager.HIDE_NOT_ALWAYS);
}
}然后呢?

初始化标题头​ //noinspection ConstantConditions
titleBar = (EaseTitleBar) getView().findViewById(R.id.title_bar);最后初始化标题头,并且让子孙们去实现抽象方法initView和setUpView().

隐藏和显示标题头

其中还提供了两个方法,隐藏和显示标题头public void showTitleBar(){
if(titleBar != null){
titleBar.setVisibility(View.VISIBLE);
}
}

public void hideTitleBar(){
if(titleBar != null){
titleBar.setVisibility(View.GONE);
}
}好了,爷爷的帐算完了,我们来找他儿子。

EaseConversationListFragment

我们来看代码package com.hyphenate.easeui.ui;

import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Pair;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageButton;

import com.hyphenate.EMConnectionListener;
import com.hyphenate.EMConversationListener;
import com.hyphenate.EMError;
import com.hyphenate.chat.EMClient;
import com.hyphenate.chat.EMConversation;
import com.hyphenate.easeui.R;
import com.hyphenate.easeui.widget.EaseConversationList;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

/**
* conversation list fragment
*
*/
public class EaseConversationListFragment extends EaseBaseFragment{
private final static int MSG_REFRESH = 2;
protected EditText query;
protected ImageButton clearSearch;
protected boolean hidden;
protected List<EMConversation> conversationList = new ArrayList<EMConversation>();
protected EaseConversationList conversationListView;
protected FrameLayout errorItemContainer;

protected boolean isConflict;

protected EMConversationListener convListener = new EMConversationListener(){

@Override
public void onCoversationUpdate() {
refresh();
}

};

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.ease_fragment_conversation_list, container, false);
}

@Override
public void onActivityCreated(Bundle savedInstanceState) {
if(savedInstanceState != null && savedInstanceState.getBoolean("isConflict", false))
return;
super.onActivityCreated(savedInstanceState);
}

@Override
protected void initView() {
inputMethodManager = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
conversationListView = (EaseConversationList) getView().findViewById(R.id.list);
query = (EditText) getView().findViewById(R.id.query);
// button to clear content in search bar
clearSearch = (ImageButton) getView().findViewById(R.id.search_clear);
errorItemContainer = (FrameLayout) getView().findViewById(R.id.fl_error_item);
}

@Override
protected void setUpView() {
conversationList.addAll(loadConversationList());
conversationListView.init(conversationList);

if(listItemClickListener != null){
conversationListView.setOnItemClickListener(new OnItemClickListener() {

@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
EMConversation conversation = conversationListView.getItem(position);
listItemClickListener.onListItemClicked(conversation);
}
});
}

EMClient.getInstance().addConnectionListener(connectionListener);

query.addTextChangedListener(new TextWatcher() {
public void onTextChanged(CharSequence s, int start, int before, int count) {
conversationListView.filter(s);
if (s.length() > 0) {
clearSearch.setVisibility(View.VISIBLE);
} else {
clearSearch.setVisibility(View.INVISIBLE);
}
}

public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}

public void afterTextChanged(Editable s) {
}
});
clearSearch.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
query.getText().clear();
hideSoftKeyboard();
}
});

conversationListView.setOnTouchListener(new OnTouchListener() {

@Override
public boolean onTouch(View v, MotionEvent event) {
hideSoftKeyboard();
return false;
}
});
}


protected EMConnectionListener connectionListener = new EMConnectionListener() {

@Override
public void onDisconnected(int error) {
if (error == EMError.USER_REMOVED || error == EMError.USER_LOGIN_ANOTHER_DEVICE || error == EMError.SERVER_SERVICE_RESTRICTED) {
isConflict = true;
} else {
handler.sendEmptyMessage(0);
}
}

@Override
public void onConnected() {
handler.sendEmptyMessage(1);
}
};
private EaseConversationListItemClickListener listItemClickListener;

protected Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case 0:
onConnectionDisconnected();
break;
case 1:
onConnectionConnected();
break;

case MSG_REFRESH:
{
conversationList.clear();
conversationList.addAll(loadConversationList());
conversationListView.refresh();
break;
}
default:
break;
}
}
};

/**
* connected to server
*/
protected void onConnectionConnected(){
errorItemContainer.setVisibility(View.GONE);
}

/**
* disconnected with server
*/
protected void onConnectionDisconnected(){
errorItemContainer.setVisibility(View.VISIBLE);
}


/**
* refresh ui
*/
public void refresh() {
if(!handler.hasMessages(MSG_REFRESH)){
handler.sendEmptyMessage(MSG_REFRESH);
}
}

/**
* load conversation list
*
* @return
+ */
protected List<EMConversation> loadConversationList(){
// get all conversations
Map<String, EMConversation> conversations = EMClient.getInstance().chatManager().getAllConversations();
List<Pair<Long, EMConversation>> sortList = new ArrayList<Pair<Long, EMConversation>>();
/**
* lastMsgTime will change if there is new message during sorting
* so use synchronized to make sure timestamp of last message won't change.
*/
synchronized (conversations) {
for (EMConversation conversation : conversations.values()) {
if (conversation.getAllMessages().size() != 0) {
sortList.add(new Pair<Long, EMConversation>(conversation.getLastMessage().getMsgTime(), conversation));
}
}
}