├── .gitignore
├── EaseChat.pptx
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── net
│ │ └── melove
│ │ └── demo
│ │ └── easechat
│ │ ├── ECApplication.java
│ │ ├── ECChatActivity.java
│ │ ├── ECLoginActivity.java
│ │ └── ECMainActivity.java
│ └── res
│ ├── layout
│ ├── activity_chat.xml
│ ├── activity_login.xml
│ └── activity_main.xml
│ ├── mipmap-hdpi
│ └── ic_launcher.png
│ ├── mipmap-mdpi
│ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxxhdpi
│ └── ic_launcher.png
│ └── values
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── ppt.md
├── screenshot
├── ec-demo.gif
└── ec-easeui-demo.gif
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 |
5 | .idea
6 | /.idea/workspace.xml
7 | /.idea/libraries
8 | /.idea/misc.xml
9 | .DS_Store
10 | /build
11 | /captures
12 | /easeui
13 |
--------------------------------------------------------------------------------
/EaseChat.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lzan13/EaseChat/c017369e4302488a9c09ab02e8a789f8ddfa4675/EaseChat.pptx
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | EaseChat
2 | ========
3 |
4 | ### 前言
5 | 环信已经发部了`SDK3.x`版本,`SDK3.x`相对于`SDK2.x`来说是整个进行了重写,`API`变化还是比较大的,已经熟悉`SDK2.x`的开发者在使用新的`SDK3.x`还是会遇到不少问题的,不过还好官方给出了`SDK2.x`升级`SDK3.x`指南,已经熟悉`SDK2.x`开发者可以根据文档了解`SDK3.x`的变化,新集成的开发者可以直接参考`SDK3.x`进行集成;
6 | 这里简单的实现了sdk的初始化以及注册登录和收发消息,不过ui上没有没有去做很好的处理
7 |
8 | > 如果你还是用的`Eclipse`,可以下载`AndroidStudio`尝试下,如果你上不了`Android`官网,不懂怎么翻墙可以找下国内开发提供的一些地址
9 |
10 | ### 先看效果图
11 | 
12 |
13 | ### 开发环境
14 | 这里并不是一定要按照我的配置来,只是说下当前项目开发运行的环境,如果你的开发环境不同可能需要自己修改下项目配置`build.gradle`文件
15 |
16 | >系统 Mac
17 | AndroidStudio 3.0.0
18 | Gradle 4.1(跟随AndroidStudio 一起更新)
19 | Android compileSdkVersion 27
20 | Android buildToolsVersion 27.0.3
21 | Android Support 最新
22 | 环信 SDK 3.4.0
23 |
24 | ### 地址整理
25 | >项目地址
26 | [lzan13](https://github.com/lzan13) / [EaseChat](https://github.com/lzan13/EaseChat)
27 |
28 | >AndroidStudio下载
29 | [Android官方下载](http://tools.android.com/download/studio/builds/2-0)
30 | [国内提供 AndroidDevTools](http://androiddevtools.cn/)
31 |
32 | >模拟器 Genymotion下载
33 | [Genymotion 官网](http://genymotion.com/)
34 |
35 | >环信官方文档
36 | [SDK3.x 文档](http://docs.easemob.com/im/start)
37 | [SDK3.x API 文档](http://www.easemob.com/apidoc/android/chat3.0/annotated.html)
38 | [SDK2.x 升级 SDK3.x 文档](http://docs.easemob.com/im/200androidcleintintegration/140upgradetov30)
39 |
40 | >[关于环信3.xSDK日志简单分析](https://www.jianshu.com/p/a194fa19bd6a)
41 | >[使用环信3.xSDK集成小米推送实现消息以及通话时的离线通知](https://www.jianshu.com/p/df892008ca5e)
42 | >[使用第三方库出现找不到so库UnsatisfiedLinkError错误的原因以及解决方案](https://www.jianshu.com/p/b9a524f24b7e)
43 |
44 | >项目详细介绍
45 | [项目文章详细介绍](http://www.jianshu.com/p/3e732f45d376)
46 |
47 | ### 延伸项目
48 | 这里还有一个针对音视频的项目,集成了1V1以及多人音视频的项目,还算比较完整,有兴趣的可以看看
49 | > 音视频项目:[VMChatDemoCall](https://github.com/lzan13/VMChatDemoCall)
50 |
51 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /libs
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 27
5 | buildToolsVersion "27.0.3"
6 |
7 | defaultConfig {
8 | applicationId "net.melove.demo.easechat"
9 | minSdkVersion 15
10 | targetSdkVersion 22
11 | versionCode 1
12 | versionName "1.0"
13 | ndk {
14 | abiFilters('armeabi-v7a', 'x86')
15 | }
16 | }
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 | sourceSets {
24 | main {
25 | // 设置Jni so文件路径 如果有jniLibs目录就不需要设置,好像手动创建的 jniLibs目录无效
26 | jniLibs.srcDirs = ['libs']
27 | }
28 | }
29 | }
30 |
31 | dependencies {
32 | compile fileTree(include: ['*.jar'], dir: 'libs')
33 | compile 'com.android.support:appcompat-v7:27.1.1'
34 |
35 | compile 'com.hyphenate:hyphenate-sdk:3.4.0'
36 | }
37 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in D:\develop\android\android_sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
75 |
76 |
77 |
78 |
79 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/app/src/main/java/net/melove/demo/easechat/ECApplication.java:
--------------------------------------------------------------------------------
1 | package net.melove.demo.easechat;
2 |
3 | import android.app.ActivityManager;
4 | import android.app.Application;
5 | import android.content.Context;
6 | import android.content.pm.PackageManager;
7 |
8 | import com.hyphenate.chat.EMClient;
9 | import com.hyphenate.chat.EMOptions;
10 |
11 | import java.util.Iterator;
12 | import java.util.List;
13 |
14 | /**
15 | * Created by lz on 2016/4/16.
16 | * 项目的 Application类,做一些项目的初始化操作,比如sdk的初始化等
17 | */
18 | public class ECApplication extends Application {
19 |
20 | // 上下文菜单
21 | private Context mContext;
22 |
23 | // 记录是否已经初始化
24 | private boolean isInit = false;
25 |
26 | @Override public void onCreate() {
27 | super.onCreate();
28 | mContext = this;
29 |
30 | // 初始化环信SDK
31 | initEasemob();
32 | }
33 |
34 | /**
35 | *
36 | */
37 | private void initEasemob() {
38 | // 获取当前进程 id 并取得进程名
39 | int pid = android.os.Process.myPid();
40 | String processAppName = getAppName(pid);
41 | /**
42 | * 如果app启用了远程的service,此application:onCreate会被调用2次
43 | * 为了防止环信SDK被初始化2次,加此判断会保证SDK被初始化1次
44 | * 默认的app会在以包名为默认的process name下运行,如果查到的process name不是app的process name就立即返回
45 | */
46 | if (processAppName == null || !processAppName.equalsIgnoreCase(mContext.getPackageName())) {
47 | // 则此application的onCreate 是被service 调用的,直接返回
48 | return;
49 | }
50 | if (isInit) {
51 | return;
52 | }
53 |
54 | // 调用初始化方法初始化sdk
55 | EMClient.getInstance().init(mContext, initOptions());
56 | // 设置开启debug模式
57 | EMClient.getInstance().setDebugMode(true);
58 |
59 | // 设置初始化已经完成
60 | isInit = true;
61 | }
62 |
63 | /**
64 | * SDK初始化的一些配置
65 | * 关于 EMOptions 可以参考官方的 API 文档
66 | * http://www.easemob.com/apidoc/android/chat3.0/classcom_1_1hyphenate_1_1chat_1_1_e_m_options.html
67 | */
68 | private EMOptions initOptions() {
69 |
70 | EMOptions options = new EMOptions();
71 | // 设置Appkey,如果配置文件已经配置,这里可以不用设置
72 | // options.setAppKey("lzan13#hxsdkdemo");
73 | // 设置自动登录
74 | options.setAutoLogin(true);
75 | // 设置是否需要发送已读回执
76 | options.setRequireAck(true);
77 | // 设置是否需要发送回执,
78 | options.setRequireDeliveryAck(true);
79 | // 设置是否根据服务器时间排序,默认是true
80 | options.setSortMessageByServerTime(false);
81 | // 收到好友申请是否自动同意,如果是自动同意就不会收到好友请求的回调,因为sdk会自动处理,默认为true
82 | options.setAcceptInvitationAlways(false);
83 | // 设置是否自动接收加群邀请,如果设置了当收到群邀请会自动同意加入
84 | options.setAutoAcceptGroupInvitation(false);
85 | // 设置(主动或被动)退出群组时,是否删除群聊聊天记录
86 | options.setDeleteMessagesAsExitGroup(false);
87 | // 设置是否允许聊天室的Owner 离开并删除聊天室的会话
88 | options.allowChatroomOwnerLeave(true);
89 | // 设置google GCM推送id,国内可以不用设置
90 | // options.setGCMNumber(MLConstants.ML_GCM_NUMBER);
91 | // 设置集成小米推送的appid和appkey
92 | // options.setMipushConfig(MLConstants.ML_MI_APP_ID, MLConstants.ML_MI_APP_KEY);
93 |
94 | return options;
95 | }
96 |
97 | /**
98 | * 根据Pid获取当前进程的名字,一般就是当前app的包名
99 | *
100 | * @param pid 进程的id
101 | * @return 返回进程的名字
102 | */
103 | private String getAppName(int pid) {
104 | String processName = null;
105 | ActivityManager activityManager =
106 | (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
107 | List list = activityManager.getRunningAppProcesses();
108 | Iterator i = list.iterator();
109 | while (i.hasNext()) {
110 | ActivityManager.RunningAppProcessInfo info =
111 | (ActivityManager.RunningAppProcessInfo) (i.next());
112 | try {
113 | if (info.pid == pid) {
114 | // 根据进程的信息获取当前进程的名字
115 | processName = info.processName;
116 | // 返回当前进程名
117 | return processName;
118 | }
119 | } catch (Exception e) {
120 | e.printStackTrace();
121 | }
122 | }
123 | // 没有匹配的项,返回为null
124 | return null;
125 | }
126 | }
--------------------------------------------------------------------------------
/app/src/main/java/net/melove/demo/easechat/ECChatActivity.java:
--------------------------------------------------------------------------------
1 | package net.melove.demo.easechat;
2 |
3 | import android.os.Handler;
4 | import android.os.Message;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.os.Bundle;
7 | import android.text.TextUtils;
8 | import android.text.method.ScrollingMovementMethod;
9 | import android.util.Log;
10 | import android.view.View;
11 | import android.widget.Button;
12 | import android.widget.EditText;
13 | import android.widget.TextView;
14 |
15 | import com.hyphenate.EMCallBack;
16 | import com.hyphenate.EMMessageListener;
17 | import com.hyphenate.chat.EMClient;
18 | import com.hyphenate.chat.EMCmdMessageBody;
19 | import com.hyphenate.chat.EMConversation;
20 | import com.hyphenate.chat.EMMessage;
21 | import com.hyphenate.chat.EMTextMessageBody;
22 |
23 | import java.util.List;
24 |
25 | public class ECChatActivity extends AppCompatActivity implements EMMessageListener {
26 |
27 | // 聊天信息输入框
28 | private EditText mInputEdit;
29 | // 发送按钮
30 | private Button mSendBtn;
31 |
32 | // 显示内容的 TextView
33 | private TextView mContentText;
34 |
35 | // 消息监听器
36 | private EMMessageListener mMessageListener;
37 | // 当前聊天的 ID
38 | private String mChatId;
39 | // 当前会话对象
40 | private EMConversation mConversation;
41 |
42 | @Override protected void onCreate(Bundle savedInstanceState) {
43 | super.onCreate(savedInstanceState);
44 | setContentView(R.layout.activity_chat);
45 |
46 | // 获取当前会话的username(如果是群聊就是群id)
47 | mChatId = getIntent().getStringExtra("ec_chat_id");
48 | mMessageListener = this;
49 |
50 | initView();
51 | initConversation();
52 | }
53 |
54 | /**
55 | * 初始化界面
56 | */
57 | private void initView() {
58 | mInputEdit = (EditText) findViewById(R.id.ec_edit_message_input);
59 | mSendBtn = (Button) findViewById(R.id.ec_btn_send);
60 | mContentText = (TextView) findViewById(R.id.ec_text_content);
61 | // 设置textview可滚动,需配合xml布局设置
62 | mContentText.setMovementMethod(new ScrollingMovementMethod());
63 |
64 | // 设置发送按钮的点击事件
65 | mSendBtn.setOnClickListener(new View.OnClickListener() {
66 | @Override public void onClick(View v) {
67 | String content = mInputEdit.getText().toString().trim();
68 | if (!TextUtils.isEmpty(content)) {
69 | mInputEdit.setText("");
70 | // 创建一条新消息,第一个参数为消息内容,第二个为接受者username
71 | EMMessage message = EMMessage.createTxtSendMessage(content, mChatId);
72 | // 将新的消息内容和时间加入到下边
73 | mContentText.setText(mContentText.getText()
74 | + "\n发送:"
75 | + content
76 | + " - time: "
77 | + message.getMsgTime());
78 | // 调用发送消息的方法
79 | EMClient.getInstance().chatManager().sendMessage(message);
80 | // 为消息设置回调
81 | message.setMessageStatusCallback(new EMCallBack() {
82 | @Override public void onSuccess() {
83 | // 消息发送成功,打印下日志,正常操作应该去刷新ui
84 | Log.i("lzan13", "send message on success");
85 | }
86 |
87 | @Override public void onError(int i, String s) {
88 | // 消息发送失败,打印下失败的信息,正常操作应该去刷新ui
89 | Log.i("lzan13", "send message on error " + i + " - " + s);
90 | }
91 |
92 | @Override public void onProgress(int i, String s) {
93 | // 消息发送进度,一般只有在发送图片和文件等消息才会有回调,txt不回调
94 | }
95 | });
96 | }
97 | }
98 | });
99 | }
100 |
101 | /**
102 | * 初始化会话对象,并且根据需要加载更多消息
103 | */
104 | private void initConversation() {
105 |
106 | /**
107 | * 初始化会话对象,这里有三个参数么,
108 | * 第一个表示会话的当前聊天的 useranme 或者 groupid
109 | * 第二个是绘画类型可以为空
110 | * 第三个表示如果会话不存在是否创建
111 | */
112 | mConversation = EMClient.getInstance().chatManager().getConversation(mChatId, null, true);
113 | // 设置当前会话未读数为 0
114 | mConversation.markAllMessagesAsRead();
115 | int count = mConversation.getAllMessages().size();
116 | if (count < mConversation.getAllMsgCount() && count < 20) {
117 | // 获取已经在列表中的最上边的一条消息id
118 | String msgId = mConversation.getAllMessages().get(0).getMsgId();
119 | // 分页加载更多消息,需要传递已经加载的消息的最上边一条消息的id,以及需要加载的消息的条数
120 | mConversation.loadMoreMsgFromDB(msgId, 20 - count);
121 | }
122 | // 打开聊天界面获取最后一条消息内容并显示
123 | if (mConversation.getAllMessages().size() > 0) {
124 | EMMessage messge = mConversation.getLastMessage();
125 | EMTextMessageBody body = (EMTextMessageBody) messge.getBody();
126 | // 将消息内容和时间显示出来
127 | mContentText.setText(
128 | "聊天记录:" + body.getMessage() + " - time: " + mConversation.getLastMessage()
129 | .getMsgTime());
130 | }
131 | }
132 |
133 | /**
134 | * 自定义实现Handler,主要用于刷新UI操作
135 | */
136 | Handler mHandler = new Handler() {
137 | @Override public void handleMessage(Message msg) {
138 | switch (msg.what) {
139 | case 0:
140 | EMMessage message = (EMMessage) msg.obj;
141 | // 这里只是简单的demo,也只是测试文字消息的收发,所以直接将body转为EMTextMessageBody去获取内容
142 | EMTextMessageBody body = (EMTextMessageBody) message.getBody();
143 | // 将新的消息内容和时间加入到下边
144 | mContentText.setText(mContentText.getText()
145 | + "\n接收:"
146 | + body.getMessage()
147 | + " - time: "
148 | + message.getMsgTime());
149 | break;
150 | }
151 | }
152 | };
153 |
154 | @Override protected void onResume() {
155 | super.onResume();
156 | // 添加消息监听
157 | EMClient.getInstance().chatManager().addMessageListener(mMessageListener);
158 | }
159 |
160 | @Override protected void onStop() {
161 | super.onStop();
162 | // 移除消息监听
163 | EMClient.getInstance().chatManager().removeMessageListener(mMessageListener);
164 | }
165 | /**
166 | * --------------------------------- Message Listener -------------------------------------
167 | * 环信消息监听主要方法
168 | */
169 | /**
170 | * 收到新消息
171 | *
172 | * @param list 收到的新消息集合
173 | */
174 | @Override public void onMessageReceived(List list) {
175 | // 循环遍历当前收到的消息
176 | for (EMMessage message : list) {
177 | Log.i("lzan13", "收到新消息:" + message);
178 | if (message.getFrom().equals(mChatId)) {
179 | // 设置消息为已读
180 | mConversation.markMessageAsRead(message.getMsgId());
181 |
182 | // 因为消息监听回调这里是非ui线程,所以要用handler去更新ui
183 | Message msg = mHandler.obtainMessage();
184 | msg.what = 0;
185 | msg.obj = message;
186 | mHandler.sendMessage(msg);
187 | } else {
188 | // TODO 如果消息不是当前会话的消息发送通知栏通知
189 | }
190 | }
191 | }
192 |
193 | /**
194 | * 收到新的 CMD 消息
195 | */
196 | @Override public void onCmdMessageReceived(List list) {
197 | for (int i = 0; i < list.size(); i++) {
198 | // 透传消息
199 | EMMessage cmdMessage = list.get(i);
200 | EMCmdMessageBody body = (EMCmdMessageBody) cmdMessage.getBody();
201 | Log.i("lzan13", "收到 CMD 透传消息" + body.action());
202 | }
203 | }
204 |
205 | /**
206 | * 收到新的已读回执
207 | *
208 | * @param list 收到消息已读回执
209 | */
210 | @Override public void onMessageRead(List list) {}
211 |
212 | /**
213 | * 收到新的发送回执
214 | * TODO 无效 暂时有bug
215 | *
216 | * @param list 收到发送回执的消息集合
217 | */
218 | @Override public void onMessageDelivered(List list) {}
219 |
220 | /**
221 | * 消息撤回回调
222 | *
223 | * @param list 撤回的消息列表
224 | */
225 | @Override public void onMessageRecalled(List list) {}
226 |
227 | /**
228 | * 消息的状态改变
229 | *
230 | * @param message 发生改变的消息
231 | * @param object 包含改变的消息
232 | */
233 | @Override public void onMessageChanged(EMMessage message, Object object) {}
234 | }
235 |
--------------------------------------------------------------------------------
/app/src/main/java/net/melove/demo/easechat/ECLoginActivity.java:
--------------------------------------------------------------------------------
1 | package net.melove.demo.easechat;
2 |
3 | import android.app.ProgressDialog;
4 | import android.content.Intent;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.os.Bundle;
7 | import android.text.TextUtils;
8 | import android.util.Log;
9 | import android.view.View;
10 | import android.widget.Button;
11 | import android.widget.EditText;
12 | import android.widget.Toast;
13 |
14 | import com.hyphenate.EMCallBack;
15 | import com.hyphenate.EMError;
16 | import com.hyphenate.chat.EMClient;
17 | import com.hyphenate.exceptions.HyphenateException;
18 |
19 | public class ECLoginActivity extends AppCompatActivity {
20 | // 弹出框
21 | private ProgressDialog mDialog;
22 | // username 输入框
23 | private EditText mUsernameEdit;
24 | // 密码输入框
25 | private EditText mPasswordEdit;
26 | // 注册按钮
27 | private Button mSignUpBtn;
28 | // 登录按钮
29 | private Button mSignInBtn;
30 |
31 | @Override protected void onCreate(Bundle savedInstanceState) {
32 | super.onCreate(savedInstanceState);
33 | setContentView(R.layout.activity_login);
34 |
35 | initView();
36 | }
37 |
38 | /**
39 | * 初始化界面控件
40 | */
41 | private void initView() {
42 | mUsernameEdit = findViewById(R.id.ec_edit_username);
43 | mPasswordEdit = findViewById(R.id.ec_edit_password);
44 |
45 | mSignUpBtn = findViewById(R.id.ec_btn_sign_up);
46 | mSignUpBtn.setOnClickListener(new View.OnClickListener() {
47 | @Override public void onClick(View v) {
48 | signUp();
49 | }
50 | });
51 |
52 | mSignInBtn = findViewById(R.id.ec_btn_sign_in);
53 | mSignInBtn.setOnClickListener(new View.OnClickListener() {
54 | @Override public void onClick(View v) {
55 | signIn();
56 | }
57 | });
58 | }
59 |
60 | /**
61 | * 注册方法
62 | */
63 | private void signUp() {
64 | // 注册是耗时过程,所以要显示一个dialog来提示下用户
65 | mDialog = new ProgressDialog(this);
66 | mDialog.setMessage("注册中,请稍后...");
67 | mDialog.show();
68 |
69 | new Thread(new Runnable() {
70 | @Override public void run() {
71 | try {
72 | String username = mUsernameEdit.getText().toString().trim();
73 | String password = mPasswordEdit.getText().toString().trim();
74 | EMClient.getInstance().createAccount(username, password);
75 | runOnUiThread(new Runnable() {
76 | @Override public void run() {
77 | if (!ECLoginActivity.this.isFinishing()) {
78 | mDialog.dismiss();
79 | }
80 | Toast.makeText(ECLoginActivity.this, "注册成功", Toast.LENGTH_LONG).show();
81 | }
82 | });
83 | } catch (final HyphenateException e) {
84 | e.printStackTrace();
85 | runOnUiThread(new Runnable() {
86 | @Override public void run() {
87 | if (!ECLoginActivity.this.isFinishing()) {
88 | mDialog.dismiss();
89 | }
90 | /**
91 | * 关于错误码可以参考官方api详细说明
92 | * http://www.easemob.com/apidoc/android/chat3.0/classcom_1_1hyphenate_1_1_e_m_error.html
93 | */
94 | int errorCode = e.getErrorCode();
95 | String message = e.getMessage();
96 | Log.d("lzan13",
97 | String.format("sign up - errorCode:%d, errorMsg:%s", errorCode,
98 | e.getMessage()));
99 | switch (errorCode) {
100 | // 网络错误
101 | case EMError.NETWORK_ERROR:
102 | Toast.makeText(ECLoginActivity.this,
103 | "网络错误 code: " + errorCode + ", message:" + message,
104 | Toast.LENGTH_LONG).show();
105 | break;
106 | // 用户已存在
107 | case EMError.USER_ALREADY_EXIST:
108 | Toast.makeText(ECLoginActivity.this,
109 | "用户已存在 code: " + errorCode + ", message:" + message,
110 | Toast.LENGTH_LONG).show();
111 | break;
112 | // 参数不合法,一般情况是username 使用了uuid导致,不能使用uuid注册
113 | case EMError.USER_ILLEGAL_ARGUMENT:
114 | Toast.makeText(ECLoginActivity.this,
115 | "参数不合法,一般情况是username 使用了uuid导致,不能使用uuid注册 code: "
116 | + errorCode
117 | + ", message:"
118 | + message, Toast.LENGTH_LONG).show();
119 | break;
120 | // 服务器未知错误
121 | case EMError.SERVER_UNKNOWN_ERROR:
122 | Toast.makeText(ECLoginActivity.this,
123 | "服务器未知错误 code: " + errorCode + ", message:" + message,
124 | Toast.LENGTH_LONG).show();
125 | break;
126 | case EMError.USER_REG_FAILED:
127 | Toast.makeText(ECLoginActivity.this,
128 | "账户注册失败 code: " + errorCode + ", message:" + message,
129 | Toast.LENGTH_LONG).show();
130 | break;
131 | default:
132 | Toast.makeText(ECLoginActivity.this,
133 | "ml_sign_up_failed code: " + errorCode + ", message:" + message,
134 | Toast.LENGTH_LONG).show();
135 | break;
136 | }
137 | }
138 | });
139 | } catch (Exception e) {
140 | e.printStackTrace();
141 | }
142 | }
143 | }).start();
144 | }
145 |
146 | /**
147 | * 登录方法
148 | */
149 | private void signIn() {
150 | mDialog = new ProgressDialog(this);
151 | mDialog.setMessage("正在登陆,请稍后...");
152 | mDialog.show();
153 | String username = mUsernameEdit.getText().toString().trim();
154 | String password = mPasswordEdit.getText().toString().trim();
155 | if (TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) {
156 | Toast.makeText(ECLoginActivity.this, "用户名和密码不能为空", Toast.LENGTH_LONG).show();
157 | return;
158 | }
159 | EMClient.getInstance().login(username, password, new EMCallBack() {
160 | /**
161 | * 登陆成功的回调
162 | */
163 | @Override public void onSuccess() {
164 | runOnUiThread(new Runnable() {
165 | @Override public void run() {
166 | mDialog.dismiss();
167 | // 加载所有会话到内存
168 | EMClient.getInstance().chatManager().loadAllConversations();
169 | // 加载所有群组到内存,如果使用了群组的话
170 | // EMClient.getInstance().groupManager().loadAllGroups();
171 | // 登录成功跳转界面
172 | Intent intent = new Intent(ECLoginActivity.this, ECMainActivity.class);
173 | startActivity(intent);
174 | finish();
175 | }
176 | });
177 | }
178 |
179 | /**
180 | * 登陆错误的回调
181 | * @param i
182 | * @param s
183 | */
184 | @Override public void onError(final int i, final String s) {
185 | runOnUiThread(new Runnable() {
186 | @Override public void run() {
187 | mDialog.dismiss();
188 | Log.d("lzan13", "登录失败 Error code:" + i + ", message:" + s);
189 | /**
190 | * 关于错误码可以参考官方api详细说明
191 | * http://www.easemob.com/apidoc/android/chat3.0/classcom_1_1hyphenate_1_1_e_m_error.html
192 | */
193 | switch (i) {
194 | // 网络异常 2
195 | case EMError.NETWORK_ERROR:
196 | Toast.makeText(ECLoginActivity.this,
197 | "网络错误 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
198 | break;
199 | // 无效的用户名 101
200 | case EMError.INVALID_USER_NAME:
201 | Toast.makeText(ECLoginActivity.this,
202 | "无效的用户名 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
203 | break;
204 | // 无效的密码 102
205 | case EMError.INVALID_PASSWORD:
206 | Toast.makeText(ECLoginActivity.this,
207 | "无效的密码 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
208 | break;
209 | // 用户认证失败,用户名或密码错误 202
210 | case EMError.USER_AUTHENTICATION_FAILED:
211 | Toast.makeText(ECLoginActivity.this,
212 | "用户认证失败,用户名或密码错误 code: " + i + ", message:" + s, Toast.LENGTH_LONG)
213 | .show();
214 | break;
215 | // 用户不存在 204
216 | case EMError.USER_NOT_FOUND:
217 | Toast.makeText(ECLoginActivity.this,
218 | "用户不存在 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
219 | break;
220 | // 无法访问到服务器 300
221 | case EMError.SERVER_NOT_REACHABLE:
222 | Toast.makeText(ECLoginActivity.this,
223 | "无法访问到服务器 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
224 | break;
225 | // 等待服务器响应超时 301
226 | case EMError.SERVER_TIMEOUT:
227 | Toast.makeText(ECLoginActivity.this,
228 | "等待服务器响应超时 code: " + i + ", message:" + s, Toast.LENGTH_LONG)
229 | .show();
230 | break;
231 | // 服务器繁忙 302
232 | case EMError.SERVER_BUSY:
233 | Toast.makeText(ECLoginActivity.this,
234 | "服务器繁忙 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
235 | break;
236 | // 未知 Server 异常 303 一般断网会出现这个错误
237 | case EMError.SERVER_UNKNOWN_ERROR:
238 | Toast.makeText(ECLoginActivity.this,
239 | "未知的服务器异常 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
240 | break;
241 | default:
242 | Toast.makeText(ECLoginActivity.this,
243 | "ml_sign_in_failed code: " + i + ", message:" + s,
244 | Toast.LENGTH_LONG).show();
245 | break;
246 | }
247 | }
248 | });
249 | }
250 |
251 | @Override public void onProgress(int i, String s) {
252 |
253 | }
254 | });
255 | }
256 | }
257 |
--------------------------------------------------------------------------------
/app/src/main/java/net/melove/demo/easechat/ECMainActivity.java:
--------------------------------------------------------------------------------
1 | package net.melove.demo.easechat;
2 |
3 | import android.content.Intent;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.os.Bundle;
6 | import android.text.TextUtils;
7 | import android.util.Log;
8 | import android.view.View;
9 | import android.widget.Button;
10 | import android.widget.EditText;
11 | import android.widget.Toast;
12 |
13 | import com.hyphenate.EMCallBack;
14 | import com.hyphenate.chat.EMClient;
15 |
16 | public class ECMainActivity extends AppCompatActivity {
17 |
18 | // 发起聊天 username 输入框
19 | private EditText mChatIdEdit;
20 | // 发起聊天
21 | private Button mStartChatBtn;
22 | // 退出登录
23 | private Button mSignOutBtn;
24 |
25 | @Override protected void onCreate(Bundle savedInstanceState) {
26 | super.onCreate(savedInstanceState);
27 |
28 | // 判断sdk是否登录成功过,并没有退出和被踢,否则跳转到登陆界面
29 | if (!EMClient.getInstance().isLoggedInBefore()) {
30 | Intent intent = new Intent(ECMainActivity.this, ECLoginActivity.class);
31 | startActivity(intent);
32 | finish();
33 | return;
34 | }
35 |
36 | setContentView(R.layout.activity_main);
37 |
38 | initView();
39 | }
40 |
41 | /**
42 | * 初始化界面
43 | */
44 | private void initView() {
45 |
46 | mChatIdEdit = findViewById(R.id.ec_edit_chat_id);
47 |
48 | mStartChatBtn = findViewById(R.id.ec_btn_start_chat);
49 | mStartChatBtn.setOnClickListener(new View.OnClickListener() {
50 | @Override public void onClick(View v) {
51 | // 获取我们发起聊天的者的username
52 | String chatId = mChatIdEdit.getText().toString().trim();
53 | if (!TextUtils.isEmpty(chatId)) {
54 | // 获取当前登录用户的 username
55 | String currUsername = EMClient.getInstance().getCurrentUser();
56 | if (chatId.equals(currUsername)) {
57 | Toast.makeText(ECMainActivity.this, "不能和自己聊天", Toast.LENGTH_SHORT).show();
58 | return;
59 | }
60 | // 跳转到聊天界面,开始聊天
61 | Intent intent = new Intent(ECMainActivity.this, ECChatActivity.class);
62 | intent.putExtra("ec_chat_id", chatId);
63 | startActivity(intent);
64 | } else {
65 | Toast.makeText(ECMainActivity.this, "Username 不能为空", Toast.LENGTH_LONG).show();
66 | }
67 | }
68 | });
69 |
70 | mSignOutBtn = findViewById(R.id.ec_btn_sign_out);
71 | mSignOutBtn.setOnClickListener(new View.OnClickListener() {
72 | @Override public void onClick(View v) {
73 | signOut();
74 | }
75 | });
76 | }
77 |
78 | /**
79 | * 退出登录
80 | */
81 | private void signOut() {
82 | // 调用sdk的退出登录方法,第一个参数表示是否解绑推送的token,没有使用推送或者被踢都要传false
83 | EMClient.getInstance().logout(false, new EMCallBack() {
84 | @Override public void onSuccess() {
85 | Log.i("lzan13", "logout success");
86 | // 调用退出成功,结束app
87 | finish();
88 | }
89 |
90 | @Override public void onError(int i, String s) {
91 | Log.i("lzan13", "logout error " + i + " - " + s);
92 | }
93 |
94 | @Override public void onProgress(int i, String s) {
95 |
96 | }
97 | });
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_chat.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
14 |
19 |
20 |
26 |
27 |
33 |
34 |
35 |
36 |
43 |
44 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_login.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
19 |
20 |
27 |
28 |
35 |
36 |
42 |
43 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
17 |
18 |
23 |
24 |
29 |
30 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lzan13/EaseChat/c017369e4302488a9c09ab02e8a789f8ddfa4675/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lzan13/EaseChat/c017369e4302488a9c09ab02e8a789f8ddfa4675/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lzan13/EaseChat/c017369e4302488a9c09ab02e8a789f8ddfa4675/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lzan13/EaseChat/c017369e4302488a9c09ab02e8a789f8ddfa4675/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lzan13/EaseChat/c017369e4302488a9c09ab02e8a789f8ddfa4675/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | EaseChat
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | google()
7 | }
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:3.0.0'
10 |
11 | // NOTE: Do not place your application dependencies here; they belong
12 | // in the individual module build.gradle files
13 | }
14 | }
15 |
16 | allprojects {
17 | repositories {
18 | jcenter()
19 | google()
20 | maven {
21 | url "https://raw.githubusercontent.com/HyphenateInc/Hyphenate-SDK-Android/master/repository"
22 | }
23 | }
24 | }
25 |
26 | task clean(type: Delete) {
27 | delete rootProject.buildDir
28 | }
29 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lzan13/EaseChat/c017369e4302488a9c09ab02e8a789f8ddfa4675/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Oct 17 16:17:03 CST 2016
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/ppt.md:
--------------------------------------------------------------------------------
1 | 使用环信EaseUI集成聊天
2 | ---------------------------
3 |
4 | * 作者:
5 | * lzan13
6 | * 开发环境:
7 | * Windows 10
8 | * AndroidStudio 2.1.2
9 |
10 |
11 | ### 集成准备
12 | * SDK的下载
13 | * 注册成为开发者
14 | [注册地址](https://console.easemob.com/index_register.html)
15 | [注册介绍](http://docs.easemob.com/im/000quickstart/10register)
16 | [了解appkey](http://docs.easemob.com/im/000quickstart/10register#创建应用)
17 |
18 |
19 | ### 项目配置
20 |
21 | * 加入SDK配置使用
22 | 将SDK加入`AndroidStudio`的项目,配置方面主要是`appkey`以及`SDK`的服务(不过呢这一步大家经常会忘)
23 |
24 | * 初始化
25 | 项目配置完成我们就可以在我们项目入口的地方进行初始化环信`SDK`(一般在`Application`里初始化,也可以放在`MainActivity`),
26 | 需要注意的是`SDK`的初始化一定要放在调用`SDK`的其他方法调用之前,不然会出现错误;还有就是要注意`SDK`不能进行多次初始化,这点在官方文档也有详细说明;
27 | 调用SDK的初始化方法:
28 | `EMClient.getInstance().init(context, options);`
29 | 其中Options这个参数主要是SDK初始化的一些配置,这里可以配置一些初始化sdk的定义,例如:
30 | ```java
31 | // 设置自动登录
32 | options.setAutoLogin(true);
33 | // 设置(主动或被动)退出群组时,是否删除群聊聊天记录
34 | options.setDeleteMessagesAsExitGroup(false);
35 | ```
36 | [详细可查看api文档](http://www.easemob.com/apidoc/android/chat3.0/classcom_1_1hyphenate_1_1chat_1_1_e_m_options.html)
37 |
38 |
39 | ### 注册与登录
40 | ```java
41 | // 注册方法,同步,需要自己异步执行,根据执行情况判断是否注册成功
42 | EMClient.getInstance().createAccount(psername, password);
43 | // 登录方法,异步,可在回调中监听登录状态
44 | EMClient.getInstance().login(username, password, callback);
45 | // 退出登录方法,第一个参数表示是否解绑推送的token,没有使用推送或者被踢都要传false
46 | EMClient.getInstance().logout(false, callback);
47 | ```
48 | [一些常见错误码可以参考API文档详细介绍](http://www.easemob.com/apidoc/android/chat3.0/classcom_1_1hyphenate_1_1_e_m_error.html)
49 |
50 |
51 | ### 集成聊天
52 | 集成聊天就需要实现消息的收发,消息的发送比较简单,直接创建消息,然后调用发送方法就行了,有时我们还需要设置下消息的回调,来监听消息发送状态,
53 | 然后消息的接收稍微麻烦一些,我们需要实现`EMMessageListener`接口,兵实现里边的几个方法,当有新消息的时候就会回调这些方法,我们可以在这里边进行处理
54 | ```java
55 | // 新消息的回调
56 | onMessageReceived(List list);
57 | // 新的透传消息回调
58 | onCmdMessageReceived(List list);
59 | // 消息已读回调
60 | onMessageReadAckReceived(List list);
61 | // 消息已发送回调
62 | onMessageDeliveryAckReceived(List list);
63 | // 消息状态改变回调
64 | onMessageChanged(EMMessage message, Object object);
65 | ```
66 |
67 |
68 | ### 结束
69 |
70 | OK!
71 | PPT已经看完了,后边创建一个新的项目带领大家过一下,最后会把项目的一些细节也都写上然后放在`github`上,大家可以去`fork`以及`clone`看下
72 | [项目代码地址](https://github.com/lzan13/EaseChat)
73 | 有两个分支:`master`分支是这次的代码; `easeui_dev`分支是使用了`EaseUI`方式集成的代码
74 |
75 | **一些其他地址**
76 | [SDK的下载](http://www.easemob.com/download/im)
77 | [开发者后台](http://console.easemob.com)
78 | [开发者文档](http://docs.easemob.com/im/start)
79 | [API文档](http://www.easemob.com/apidoc/android/chat3.0)
--------------------------------------------------------------------------------
/screenshot/ec-demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lzan13/EaseChat/c017369e4302488a9c09ab02e8a789f8ddfa4675/screenshot/ec-demo.gif
--------------------------------------------------------------------------------
/screenshot/ec-easeui-demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lzan13/EaseChat/c017369e4302488a9c09ab02e8a789f8ddfa4675/screenshot/ec-easeui-demo.gif
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------