目录

IM Android SDK 指南

1、Android SDK导入

1.1、集成前准备

在云之讯官方网站注册开发者帐号,可参考新手指引

1.2、下载SDK

前往云之讯官网下载云之讯IMSDK(下载地址),SDK包括2个jar包和1个lib库

1.3、导入SDK

1) 将SDK包中提供的yunzhixun_IM_SDK_ver_x.x.x_release.jar、 yunzhixun_TCP_SDK_ver_x.x.x_release.jar包考贝到android工程的libs目录下,将libpack.so拷贝到android工程的libs目录下的armeabi目录下,放置位置如下图所示:

2) Jar包加入到Build Path中,如下图

3) 把导入包勾选 如果执行了Add to Build Path 仍然无法使用jar包,请指向自己的工程,点击鼠标右键,选择Properties目录—>选择Java BuildPath目录—>Order and Export把导入的包勾选—>按下OK

1.4、添加SDK权限和IM核心服务

将下列权限配置添加到Android工程中的AndroidManifest文件中

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yzx.im_demo"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="9"
android:targetSdkVersion="15" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.VIBRATE" />
 
<application
android:name=".MainApplication"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@android:style/Theme.NoTitleBar" >
 
//YzxIMCoreService是云之讯IM的核心服务,必须添加,通过Application调用启动服务
<service android:name="com.yzxtcp.service.YzxIMCoreService" ></service>
//AlarmReceiver和MsgBackReceiver是维持TCP长连接心跳的广播
<receiver android:name="com.yzxtcp.tools.tcp.receiver.AlarmReceiver" >
    </receiver>
<receiver android:name="com.yzxtcp.tools.tcp.receiver.MsgBackReceiver" >
    </receiver>
</application>

2、连接平台和注销


2.1、初始化IM服务

方法1: 直接继承IMApplication类,IMApplication类中已经包含了核心服务的初始化,代码如下:

public class MainApplication extends IMApplication {
    @Override
    public void onCreate() {
        super.onCreate();
    }
}

方法2: 如果您的Application已经继承了其他第三方提供的Application,可以在Application中添加以下代码

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        UCSManager.init(this);//初始化核心服务
        IMManager.getInstance(this);//必须要加上
    }
}

2.2、连接平台

1) token方式登入(所有关于云之讯IM的操作都必须在登入成功后才能使用) token 称为用户令牌,App Key 是您的 App 的唯一标识,token 则是您 App 上的每一个用户的身份授权象征,使用这个 token 作为该用户的唯一身份凭证与其他用户进行通信。 token是从AS服务器(用户服务器,您必须拥有自己的服务器)获取,token由AS服务器按照一定规则生成。

获取token后用下面方法进行登入

UCSManager.connect(token, new ILoginListener()) {
    @Override
    public void onLogin(UcsReason reason) {
        if(reason.getReason() == UcsErrorCode.NET_ERROR_CONNECTOK){
        //登入成功
        例如登入成功后跳转到会话界面,显示获取的所有会话列表
        }else{
        //登入失败
        例如登入失败后提示用户登入失败
        }
    }
});
2) 账户密码方式登入(仅限老平台用户继续使用,新接入者无需关心这种登入方式)
UCSManager.connent(String sid, String sidPwd,String clientId,String clientPwd,new ILoginListener() {
    @Override
    public void onLogin(UcsReason reason) {
        if(reason.getReason() == UcsErrorCode.NET_ERROR_CONNECTOK){
        //登入成功
        //例如登入成功后跳转到会话界面,显示获取的所有会话列表
        }else{
        //登入失败
        //例如登入失败后提示用户登入失败
        }
    }
});

2.3、注销

要断开SDK与云平台连接(比如说切换账号时),可以调用注销接口。如果需要发送消息或者接收消息等操作,必须重新调用UCSManager.content登入。 注销函数如下:

UCSManager.disconnect();//注销函数

3、基本功能集成

3.1、获取IMManager类实例

IM各种功能实现方法,都可以通过imManager调用,比如获取所有会话列表、创建讨论组得到所有未读消息等, 因此需要先获取imManager,获取方式如下:

IMManager imManager = IMManager.getInstance(this); //获得IMManager类实例

3.2、会话操作

接下里将介绍关于会话的一些基本操作,与某人聊天,一个讨论组或者群组都称之为一个会话

1) 设置会话回调监听器 首先Activity需要实现IConversationListener,代码示例如下:

public class ConversationFragment extends Fragment implements IConversationListener

会话界面启动时在onCreate函数中就要首先添加会话回调监听器,以便时收到创建会话、删除会话和更新会话的回调接口。

IMManager.getInstance(this).setConversationListener(IConversationListener);

2) 会话回调接口 当第一次与某人聊天时或者被加入到某个讨论组时,只要收到一条消息或者发送一条消息,都会创建一个新的会话, 会收到创建会话接口的回调

void onCreateConversation(ConversationInfo cinfo){
    //把cinfo添加到会话列表中,更新界面
}

当你退出某个讨论组时,会删除这个讨论组会话,同时收到删除这个会话的回调

//删除会话回调
void onDeleteConversation(ConversationInfo cinfo){
    //把cinfo从会话列表中移除,更新界面
}

当某个会话中收到消息时,同时收到这个会收到更新这个会话的回调,比如这个消息的未读消息数, 首条消息内容的变化等。

//更新会话回调
void onUpdateConversation(ConversationInfo cinfoSrc) {
    //通过比对会话的targetId(单聊时是对方账号,讨论组或者群组时是讨论组或者群组账号)是否相等找到
    //要被更新的会话cinfoDest,然后从会话列表中移除cinfoDest,根据收到的cinfoSrc配置cinfoDest中的属性
    //然后重新加入到会话列表中,可以参考下面的函数:
    ConversationInfocinfoDest = null;
    for(ConversationInfo conversation : clists){
        if(conversation.getTargetId().equals(cinfoSrc.getTargetId())){
            cinfoDest = conversation;
        }
    }
    updataCinfo(cinfoSrc, cinfoDest);
}
 
private void updataCinfo(ConversationInfo cinfoSrc,ConversationInfo cinfoDest){
    conversationLists.remove(cinfoDest);
    cinfoDest.setDraftMsg(cinfoSrc.getDraftMsg());
    cinfoDest.setLastTime(cinfoSrc.getLastTime());
    if(cinfoSrc.getCategoryId()!=CategoryId.GROUP){
        cinfoDest.setConversationTitle(cinfoSrc.getConversationTitle());
    }
    if(cinfoDest.getIsTop()){
        conversationLists.add(0, cinfoDest);
    }else{
        conversationLists.add(topNum, cinfoDest);
    }
    //更新会话列表
}

3) 获取所有会话列表 进入会话界面,如果需要显示所有会话列表,可以调用下面API函数获取

List<ConversationInfo> conversationLists = new ArrayList<ConversationInfo>();//定义会话列表
conversationLists=IMManager.getInstance(mContext).getConversationList();//获取会话列表

4) 会话类在消息界面的应用 当从会话界面跳转到消息界面时,可以通过intent把这个会话的类conversationinfo传递进来,示例代码如下:

Intent intent = new Intent(getActivity(), IMMessageActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("conversation", conversationinfo);
startActivity(intent);

在消息界面中可以通过conversationinfo类中的方法进行下列操作:

//需要获取最近的20条聊天记录
List<ChatMessage>currentMsgList = new ArrayList<ChatMessage>();
currentMsgList = conversationinfo.getLastestMessages(0, 20);
//清除会话未读消息
conversationinfo.clearMessagesUnreadStatus();
//清除会话聊天记录
conversationinfo.clearMessages();
//设置会话是否置顶
conversationinfo.setIsTop(Boolean isTop);

3.3、消息操作

1) 设置消息回调监听器 首先Activity需要实现MessageListener,代码示例如下:

public class IMMessageActivity extends FragmentActivity implements MessageListener

消息界面启动时在onCreate函数中就要首先添加消息回调监听器,这样才可以及时接受到消息接口的回调

IMManager.getInstance(this).setSendMsgListener(MessageListener);

2) 消息回调接口 发送消息后会收到发送消息的回调,回调接口中收到的消息对象就是你之前发送的消息对象

void onSendMsgRespone(ChatMessage message){
    //因为消息状态会改变,因此需要更新界面
}

收到一条或者多条消息后,消息界面收到接收消息的回调。

void onReceiveMessage(List messages){
    //messages 保存的对象就是List <ChatMessage>,可以强转到List <ChatMessage>
    //把接收到的消息添加到消息列表,更新界面
}

调用IMManager中的方法downloadAttached(方法使用见SDK手册)后会收到下载回调,回调接口接下。

Void onDownloadAttachedProgress(StringmsgId,String filePaht,int sizeProgrss,int currentProgress){
    //获得文件下载进度,更新界面
}

3) 创建消息对象 消息对象包括单聊、群组和讨论组,他们都继承消息对象ChatMessage,下面是分别创建单聊、群组和讨论组的方法

ChatMessage msg = null;
msg = new SingleChat();//创建单聊消息
msg = new GroupChat();//创建群组消息
msg = new DiscussionChat();//创建讨论组消息

4) 发送文本消息

ChatMessage msg = null;
msg = new SingleChat();//创建单聊消息
msg.setTargetId(对方账号)
msg.setSenderId(自己账号);
msg.setMsgType(MSGTYPE.MSG_DATA_TEXT);//设置消息类型为文本
msg.setContent(text);//设置消息内容
if(IMManager.getInstance(mContext).sendmessage(msg)){//发送消息成功返回true
    //发送成功后把消息添加到消息列表中,收到消息发送回调后刷新界面
    currentMsgList.add(msg);
}

5) 发送图片消息 发送的图片最好小于100K,发送图片之前请先进行压缩处理

ChatMessage msg = null;
//压缩图片, bitmap想压缩的图片,quality压缩后的质量(0-100), path 压缩后存储的路径
IMManager.getInstance(context).compressBitmap(bitmap, quality, path);
msg = new GroupChat ();//创建群聊消息
msg.setTargetId(对方账号);
msg.setSenderId(自己账号);
msg.setMsgType(MSGTYPE.MSG_DATA_IMAGE); //设置消息类型为图片
msg.setContent(path); //设置缩量图片路径
msg.setPath(path);//设置图片路径
if (IMManager.getInstance(mContext).sendmessage(msg)){//发送消息成功返回true
    //发送成功后把消息添加到消息列表中,收到消息发送回调后刷新界面
    currentMsgList.add(msg);
}

6) 发送语音消息

ChatMessage msg = null;
msg = new DiscussionChat ();//创建讨论组消息
msg.setTargetId(对方账号);
msg.setSenderId(自己账号);
msg.setMsgType(MSGTYPE.MSG_DATA_VOICE); //设置消息类型为语音
msg.setPath(path);//设置语音路径
msg.setContent(time);//设置语音时间
if (IMManager.getInstance(mContext).sendmessage(msg)){//发送消息成功返回true
    //发送成功后把消息添加到消息列表中,收到消息发送回调后刷新界面
    currentMsgList.add(msg);
}

7) 录音 云之讯SDK有提供专门的录音和播放接口(具体操作见SDK接口文档),这里介绍录音接口, 开发者可以选择使用我们提供的接口,下面是录音接口的使用方法:

IMManager.getInstance(mContext).startVoiceRecord(path, new RecordListener() {
    @Override
    public void onFinishedRecordingVoice(int time) {
        if(time<1){
            //提示用户时间小于1秒,无法发送,小于1秒请不要发送消息
            return;
        }
        发送语音消息
    }
    @Override
    public void onFinishedPlayingVoice() {
    }
}
});

8) 发送地图定位消息
发送地图定位消息前需要先构造地图定位消息对象 LocationMapMsg locationMsg

下面以单聊消息为例展示如何发送地图定位消息

/**
String coordinate = "default"; // 坐标类型 可选 SDK不解析只透传用户可随意传字符串
double latitude // 经度  
double longitude // 维度  
String detailAddr // 详细地址信息 
String thumbnailPath // 地图缩略图保存的绝对路径 缩略图不要超过30k否则会发送失败
*/
LocationMapMsg locationMsg = new LocationMapMsg(latitude, longitude, detailAddr , thumbnailPath); 
 
ChatMessage msg = null; 
msg = new SingleChat(); //创建单聊消息 
msg.setTargetId(对方账号) 
 .setNickName(对方昵称)
 .setSenderId(自己账号)
 .setMsgType(MSGTYPE.MSG_DATA_LOCALMAP)//设置消息类型
 .setLocationMapMsg(locationMsg) //设置地图对象
 .setFromMyself(false);
 
if (IMManager.getInstance(mContext).sendmessage(msg)){//发送消息成功返回true 
        // 发送成功后把消息添加到消息列表中,收到消息发送回调后刷新界面 
        currentMsgList.add(msg);
}

3.4、讨论组操作

1) 进入对讨论组操作的界面时(比如讨论组信息界面,创建讨论组界面),在onCreate时首先要添加讨论组监听接口,调用下面方法实现:

IMManager.getInstance(this).setDiscussionGroup (mContext);

2) 讨论组回调接口

Void onCreateDiscussion(UcsReason reason, DiscussionInfo dInfo){
    if(reason.getReason()==0){
    //创建成功
    ConversationInfo info = IMManager.getInstance(this).getConversation(dInfo.getDiscussionId());
    if(null != info){
        intent.putExtra("conversation", info);
        startActivity(intent);
        finish();
    }else{
        CustomLog.e("获得讨论组会话为空");
    }
    }else{
    //创建失败
    }
}
 
void onDiscussionAddMember(UcsReason reason){
    if(reason.getReason()==0){
        //加人成功
    }else{
        //加人失败
    }
}
 
void onDiscussionDelMember(UcsReason reason){
    if(reason.getReason()==0){
        //踢人成功
    }else{
        //踢人失败
    }
}
 
void onQuiteDiscussion(UcsReason reason){
    if(reason.getReason()==0){
        //退出成功
    }else{
        //退出失败
    }
}
 
void onModifyDiscussionName(UcsReason reason){
    if(reason.getReason()==0){
        //修改讨论组名字成功
    }else{
        //修改讨论组名字失败
    }
}

3) 创建讨论组:
参数:
discussionName - 讨论组名字
memberList - 将添加的成员列表(不能包含自己)

IMManager.getInstance(mContext).createDiscussionGroup(java.lang.String discussionName,
java.util.List<java.lang.String> memberList);

4) 添加讨论组成员:
参数:
discussionID - 讨论组ID
memberList - 将添加的成员列表

IMManager.getInstance(mContext).addDiscussionGroupMember(java.lang.String discussionID,
java.util.List<java.lang.String> memberList)

5) 删除讨论组成员:
参数:
discussionID - 讨论组ID
memberList - 将删除的成员列表(成员不能包含自己)

IMManager.getInstance(mContext).delDiscussionGroupMember(java.lang.String discussionID,
java.util.List<java.lang.String> memberList)

6) 退出讨论组:
参数:
discussionID - 讨论组ID

IMManager.getInstance(mContext).quitDiscussionGroup (java.lang.String discussionID)

7) 修改讨论组名字
参数:
discussionID - 讨论组ID
newname–新的讨论组名称

imManager.modifyDiscussionTitle(java.lang.String discussionID, java.lang.Stringnewname);

8) 获取指定讨论组ID的讨论组信息
参数:
discussionID - 讨论组ID号
返回:
讨论组信息对象

imManager.getDiscussionInfo(java.lang.String discussionID);

9) 获取所有讨论组信息
返回:
讨论组信息列表

imManager.getAllDiscussionInfos();

3.5、群组操作

SDK仅仅是提供群组消息的收发功能(SDK提供了讨论组的管理功能),群组的管理功能(创建群组,添加群组成员,删除群组成员等)由用户AS服务器处理。

ChatMessagemsg = new GroupChat()是创建群组消息对象。
void onReceiveMessage(List<ChatMessage> messages)是接收消息回调。
判断message.categoryId==CategoryId.GROUP表示消息是群组消息。

3.6、设置SDK状态监听

设置SDK状态监听,查看是否被踢线

imManager.setISdkStatusListener(this);

回调接口示例代码如下:

public void onSdkStatus(UcsReason reason) {
    if (reason.getReason() == UcsErrorCode.NET_ERROR_KICKOUT) {
        CustomLog.i("收到服务器强制下线通知");
    } else if (reason.getReason() == UcsErrorCode.NET_ERROR_TOKENERROR) {
        CustomLog.i("token超时,请重新登录");
    } else if (reason.getReason() == UcsErrorCode.PUBLIC_ERROR_NETUNCONNECT){
        CustomLog.i("网络断开");
    }else if (reason.getReason() == UcsErrorCode.PUBLIC_ERROR_NETCONNECT){
        CustomLog.i("网络已连接");
    } else if (reason.getReason() == UcsErrorCode.NET_ERROR_TCPCONNECTFAIL){
        CustomLog.i("TCP连接失败");
    } }else if (reason.getReason() == UcsErrorCode.NET_ERROR_TCPCONNECTING){
        CustomLog.i("TCP连接中");
    }else if (reason.getReason() == UcsErrorCode.NET_ERROR_TCPCONNECTOK) {
        CustomLog.i("TCP连接成功");
    }
}

4、对象类属性介绍

4.1、会话类

private String targetId;  //单聊id,群组id,讨论组id
private String conversationTitle; //会话标题
private int categoryId;   //会话分类:1个人,2群组,3讨论组
private String draftMsg="";  //草稿或者是最近的消息
private boolean isTop;   //是否置顶
private long lastTime;   //最后消息时间
private long topTime;   //置顶多久,默认是NULL暂未使用
private  int msgUnRead; //未读消息数

4.2、消息类

private int msgid;  //消息ID
private int targetId; //接收用户名
private int senderId;//发送方用户名
private String nickName; //发送方昵称
private int categoryId;// 消息分类 1单聊,2群聊, 3,讨论组
private boolean isFromMyself;//消息方向发送或者接收
private long sendTime;// 发送时间
private long receiveTime;//接收时间
private int msgType;//消息类型 1,文本,2图片,3语音
private String content;//消息类容:文本代表文字内容图片代表小图路径语音代表录音长度
private int readStatus;// 消息是否已读状态
private int sendStatus;//消息发送状态 1发送中,2,发送成功,3,发送失败
private String path;   //大图片语音视频的存储路径
private String parentID; //群组或讨论组或单聊 ID号
public final static int MSG_STATUS_INPROCESS = 1; //初始状态
public final static int MSG_STATUS_SUCCESS = 2; //发送成功状态
public final static int MSG_STATUS_FAIL = 3; //发送失败状态
public final static int MSG_STATUS_READED = 4; //已读状态
public final static int MSG_STATUS_UNREAD = 5; //未读状态
public final static int MSG_STATUS_RETRY = 6; //重发状态
public final static int MSG_STATUS_NETERROR = 7;    //消息状态网络错误
public final static int MSG_STATUS_TIMEOUT = 8;//发送消息超时

4.3、讨论组信息类

private String discussionId; //讨论组ID
private String discussionName; //讨论组名字
private int categoryId;     //聊天类型 1,单聊,2,群组,3,讨论组
private int memberCount;    //讨论组成员数量
private String ownerId;    //群组ID号
private String discussionMembers; //包含成员id,名字,头像
private String disscussionSettings; //讨论组设置信息,json
private long createTime;   //讨论组创建时间

4.4、群组信息类

private String groupId; //群ID
private String groupName; //群名字
private int categoryId;  //聊天类型 1,单聊,2,群组,3,讨论组
private int updateTime;  //创建时间

4.5、返回值类

private int reason; //返回消息错误码
private String msg; //返回原因

4.6、会话类型枚举类

public enum CategoryId{
NONE,//未知会话类型
PERSONAL, //单聊会话
GROUP, //群聊会话
DISCUSSION;//讨论组会话
}

4.7、消息类型枚举类

public enum MSGTYPE {
MSG_DATA_NONE,//未知消息类型
MSG_DATA_TEXT,//文本消息
MSG_DATA_IMAGE,//图片消息
MSG_DATA_VOICE,//语音消息
MSG_DATA_VIDEO,//视频消息
MSG_DATA_SYSTEM,//系统消息
MSG_DATA_LOCALMAP;//地图定位消息
}

5、集成注意事项

1.创建讨论组失败
答:(常见原因)创建讨论组接口中的员列表不能包含自己;成员列表必须是都注册过的。

2.没有收到回调
答:(常见原因)接收调必须先设置监听器,比如接收消息回调要设置消息回调监听器 IMManager.getInstance(this).setSendMsgListener(mContext);

3.调用SDK接口崩溃或者无反应
答:(常见原因)一定要在Application中添加UCSManager.init(this)或者集成IMApplication才能去调用SDK中的其他方法,因为要先启动SDK核心服务。

4.发送消息失败
答:(常见原因)设置SDK状态监听器,监听是否被踢线,token是否过期,网络是否正常

5.讨论组删除成员失败
答:(常见原因)讨论组和群组只有创建者可以删除,其他人只能退出

6、错误码

300100    连接服务器失败                NET_ERROR_CONNECTFAIL
300101    连接超时                    NET_ERROR_CONNECTTIMEOUT
300102    强制踢线                    NET_ERROR_KICKOUT
300103    无效的token 或与appid不匹配        NET_ERROR_TOKENERROR
300104    用户不存在                NET_ERROR_USERUNKNOWN
300105    密码错误                    NET_ERROR_PASSWORDERROR
300106    重新连接成功                NET_ERROR_RECONNECTOK
300107    连接服务器成功                NET_ERROR_CONNECTOK
300108    TCP 连接成功                NET_ERROR_TCPCONNECTOK
300109    TCP 连接失败                NET_ERROR_TCPCONNECTFAIL
300110    TCP 连接中                NET_ERROR_TCPCONNECTING
300300    无效的消息(null)            IM_ERROR_INVALIDMSG
300301    无效的群组                IM_ERROR_INVALIDGROUP
300302    无效的讨论组                IM_ERROR_INVALIDDISSCUSSION
300303    修改讨论组名失败                IM_ERROR_MODIFYDISFAIL
300304    创建讨论组失败                IM_ERROR_CREATEDISFAIL
300305    用户不在群组内                IM_ERROR_USERNOTINGROUP
300306    用户不在讨论组内                IM_ERROR_USERNOTINDIS
300307    删除成员失败                IM_ERROR_DELUSERFAIL
300308    邀请成员失败                IM_ERROR_ADDUSERFAIL
300309    文件上传失败                IM_ERROR_UPLOADFILEFAIL
300310    文件下载失败                IM_ERROR_DOWNLOADFILEFAIL
300311    录音时间过短                IM_ERROR_RECORDTOOSHORT
300312    文件格式不支持                IM_ERROR_INVALIDFILEFORMAT
300313    消息内容过长                IM_ERROR_MSGTOOLONG
300314    读取本地数据库消息列表失败            IM_ERROR_READDBFAIL
300315    消息写入本地数据库失败            IM_ERROR_WRITEDBFAIL
300316    退出讨论组失败                IM_ERROR_QUITDISFAIL
300600    参数错误                    PUBLIC_ERROR_PARAMETERERR
300601    消息格式错误                PUBLIC_ERROR_MSGFORMATERR
300602    网络未连接                PUBLIC_ERROR_NETUNCONNECT
300603    初始化加载失败(包括数据库加载)        PUBLIC_ERROR_INITFAIL
300604    操作过于频繁                PUBLIC_ERROR_OPERATIONFREQUENT
300605    网络已连接                PUBLIC_ERROR_NETCONNECTED