wildfirechat / android-chat

即时通讯,聊天,野火IMAndroid客户端,支持Android 4.x —— 最新
http://docs.wildfirechat.cn
Other
2.54k stars 889 forks source link

android 端Im如何自定义消息 #655

Closed jianghu9527 closed 2 years ago

jianghu9527 commented 2 years ago

package cn.wildfirechat.message.custom;

import static cn.wildfirechat.message.core.MessageContentType.ContentType_Text;

import android.os.Parcel;
import android.util.Log;

import org.json.JSONException;
import org.json.JSONObject;

import cn.wildfirechat.message.Message;
import cn.wildfirechat.message.TextMessageContent;
import cn.wildfirechat.message.core.ContentTag;
import cn.wildfirechat.message.core.MessagePayload;
import cn.wildfirechat.message.core.PersistFlag;
import cn.wildfirechat.model.QuoteInfo;
import  cn.wildfirechat.message.MessageContent;
import static cn.wildfirechat.message.core.MessageContentType.ContentType_Notice_Txt;

@ContentTag(type = ContentType_Notice_Txt, flag = PersistFlag.Persist)
public class CustomTextMessageNoticeContent extends  MessageContent   { //TextMessageContent   MessageContent

    public   ExtraBean extra;
    public String  content;
//    public String pushContent;
//    // 引用信息
    private QuoteInfo quoteInfo;
    public CustomTextMessageNoticeContent() {
    }
    public class  ExtraBean{
        public  String toId;
        public  String businessId;
        public  String msgId;
        public int noticeType;
        public  Long time;
        public  String fromId;
        public  String pushContent;
        public String  content;
        public String quoteInfo;

    };

    public CustomTextMessageNoticeContent(String content) {
        this.content = content;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public QuoteInfo getQuoteInfo() {
        return quoteInfo;
    }

    public void setQuoteInfo(QuoteInfo quoteInfo) {
        this.quoteInfo = quoteInfo;
    }

    @Override
    public String toString() {
        return super.toString();
    }
    @Override
    public MessagePayload encode() {
        MessagePayload payload = super.encode();
        payload.searchableContent = content;
        payload.mentionedType = mentionedType;
        payload.mentionedTargets = mentionedTargets;
        if (quoteInfo != null) {
            JSONObject object = new JSONObject();
            try {
                object.put("quote", quoteInfo.encode());
                payload.binaryContent = object.toString().getBytes();
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
        Log.i("-----------------------------","-----------TextMessageContent-------MessagePayload-------payload-----通知-----------"+payload.toString());
        return payload;
    }

    @Override
    public void decode(MessagePayload payload) {
        content = payload.searchableContent;
        mentionedType = payload.mentionedType;
        mentionedTargets = payload.mentionedTargets;
        if (payload.binaryContent != null && payload.binaryContent.length > 0) {
            try {
                JSONObject object = new JSONObject(new String(payload.binaryContent));
                quoteInfo = new QuoteInfo();
                quoteInfo.decode(object.optJSONObject("quote"));

                Log.i("-----------------------------","-------TextMessageContent-----------MessagePayload-------payload-----decode-----------"+quoteInfo.toString());

            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    }

//    @Override
//    public void decode(MessagePayload payload) {
//        content = payload.searchableContent;
//        mentionedType = payload.mentionedType;
////        mentionedTargets = payload.mentionedTargets;
//        if (payload.binaryContent != null && payload.binaryContent.length > 0) {
//            try {
//                JSONObject object = new JSONObject(new String(payload.binaryContent));
//                Log.d("---------------------------","--------------------CustomTextMessageNoticeContent----MessagePayload---decode:"+object);
//                quoteInfo = new QuoteInfo();
//                quoteInfo.decode(object.optJSONObject("quote"));
//            } catch (JSONException e) {
//                e.printStackTrace();
//            }
//        }
//    }

    @Override
    public String digest(Message message) {
        return content;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        super.writeToParcel(dest, flags);
        dest.writeString(this.content);
        dest.writeParcelable(this.quoteInfo, flags);
    }

    protected CustomTextMessageNoticeContent(Parcel in) {
        super(in);
        this.content = in.readString();
        this.quoteInfo = in.readParcelable(QuoteInfo.class.getClassLoader());
    }

    public static final Creator<CustomTextMessageNoticeContent> CREATOR = new Creator<CustomTextMessageNoticeContent>() {
        @Override
        public CustomTextMessageNoticeContent createFromParcel(Parcel source) {
            return new CustomTextMessageNoticeContent(source);
        }

        @Override
        public CustomTextMessageNoticeContent[] newArray(int size) {
            return new CustomTextMessageNoticeContent[size];
        }
    };

}
jianghu9527 commented 2 years ago

1.没有收到这个消息体(怎么验证是否收到了) 2.收到了消息也计入了未读消息总数(CustomTextMessageNoticeContent 没有生效)

jianghu9527 commented 2 years ago

注册绑定CustomTextMessageNoticeContent 的流程?

jianghu9527 commented 2 years ago

如何封装自定义消息对象

imndx commented 2 years ago

自定义消息涉及到一下几几方面:

  1. 自定消息type,也就是上面的ContentType_Notice_Txt,这个值需要保证是1000以上,且不会和其他的重复,参考:MessageContentType
  2. flag,是一个枚举类型,可选值参考PersistFlag,需要存储且计入未读数的话,是Persist_And_Count
  3. 继承MessageContent实现自定义消息,如果是通知类的自定义消息(就是显示成小灰条那种消息),继承自NotificationMessageContent更方便。
    1. 一定要包含一个无参构造函数
    2. 一定要正确实现encodedecode方法,可参考TextMessageContent等其他消息
    3. 一定要实现Parcelable接口方法,推荐使用Parcelabe插件生成响应代码。MessageContent类实现了Parcelable接口,但MessageContent里面没有实现所有的方法并没有实现
  4. 在合适的地方调用ChatManager#registerMessageContent注册自定义消息,可以在WfcUIKIT#init里面注册。
  5. 编写自定义消息对应UI代码,只有存储类型为Persist或者Persist_And_Count的自定义消息才有 UI显示,其他的自定义消息没有 UI,请跳过此步
    1. 如果是通知类自定义消息,且展示方式和默认的一致,只需要将该自定义消息添加到SimpleNotificationMessageContentViewHolder的注解中
    2. 如果是通知类自定义消息,且展示方式和默认的不一致,需要继承NotificationMessageContentViewHolder来实现该自定义消息的 UI,具体可以参考SimpleNotificationMessageContentViewHolder
    3. 其他普通消息,需要继承NormalMessageContentViewHolder实现该自定义消息的 UI,具体可以参考TextMessageContentViewHolder
    4. 自定义消息的 UI 和自定义消息本身是通过自定义消息 UI 类上面的MessageContentType注解关联的
  6. 调用MessageViewHolderManager#registerMessageViewHolder注册自定义消息UI,可参考LocationMessageContentViewHolder的注册。
imndx commented 2 years ago

1.没有收到这个消息体(怎么验证是否收到了) 2.收到了消息也计入了未读消息总数(CustomTextMessageNoticeContent 没有生效)

  1. 可以在 ChatManager#onReceiveMessage里面打断点,看否收到,如果自定义消息处理失败,会回落到UnknownMessageContent消息,UnknownMessageContent消息的orignalPayload字段是原始消息相关内容。
jianghu9527 commented 2 years ago

文本消息 {messageId=74, conversation=Conversation{type=Single, target='d931692aa7c845da815a37ce8d5253d5', line=0}, sender='d931692aa7c845da815a37ce8d5253d5', toUsers=null, content=TextMessageContent{mentionedType=0, mentionedTargets=[], extra='', pushContent='null', content='1111', quoteInfo=null}, direction=Receive, status=Unread, messageUid=287333093082136705, serverTime=1651747095563, localExtra=''}

自定义消息(不知道对不对) {messageId=75, conversation=Conversation{type=Single, target='systemUpdateUser', line=0}, sender='systemUpdateUser', toUsers=null, content=TextMessageContent{mentionedType=0, mentionedTargets=[], extra='', pushContent='null', content='{"activeStatus":0,"depName":"开发部1","filid":"c14600e14356421496778150e141e11a", "headImg":"","id":49359,"isaddress":0,"name":"测试开发115","orgName":"运维单位1", "orgPath":"5026021b6f24462c943279be4b93a97a_en1bd1d844ab2e476cb9e875b2f65e8fcc_en788af995ac3e4f49aa9f418f55bd6a12_enf1925fe112f7448490ed0a07d3e832c6_enaaab1e8bf6864fa7a43c33d55606e52c_dep7b0ec4b1b0c146c99478bbce907c6ed8", "pFilid":"dep7b0ec4b1b0c146c99478bbce907c6ed8","phone":"18228018433","postType":"0","sex":"0","shortName":"测试开发115","sort":0,"status":1,"type":"2","updateTime":1651747187515,"userId":"u2d085a4ab2c540a2b8dca706d665ed6c"}', quoteInfo=null}, direction=Receive, status=Unread, messageUid=287333286399705217, serverTime=1651747187744, localExtra=''}

我个人理解是:自定义消息需要定义与TextMessageContent 不同的属性 上面"自定义消息" 不属于自定义消息嘛, 属于扩展字段

“自定义消息” content=’‘ 是字符串 不是 对象属性

jianghu9527 commented 2 years ago

如果添加其他的字段 例如:CustomTextMessageNoticeContent ExtraBean extra;
服务器是不是需要同步修改

imndx commented 2 years ago

如果添加其他的字段 例如:CustomTextMessageNoticeContent ExtraBean extra; 服务器是不是需要同步修改

不需要,只需要修改客户端就行。

最主要的是encodedecode方法,encode时,把你们新增的放到MessagePayload的某个字段,服务端会透传MessagePayloaddecode时,做和encode相反的操作。

jianghu9527 commented 2 years ago

encode 和 decode方法 在什么时候调用?

imndx commented 2 years ago

encode一般是在发送消息等之前调用,以对消息进行编码;decode一般是在收到消息之后进行调用,以对消息进行解码。

具体可以参考ClientService#sendClientService#onReceiveMessage

jianghu9527 commented 2 years ago

/**

// public ExtraBean extra; public String content; // public String pushContent; // // 引用信息 private QuoteInfo quoteInfo; public CustomTextMessageNoticeContent() { }

// public class ExtraBean{ // public String toId; // public String businessId; // public String msgId; // public int noticeType; // public Long time; // public String fromId; // public String pushContent; // public String content; // public String quoteInfo; // // };

/**
 * 跟新  通讯录
 *
 *         {"activeStatus":1,"depName":"开发部1","filid":"13cf82a8c41e483995d046006d104cf0",
 *                 "headImg":"http://www.scgzjg.cn/userimg/01/fe736bf0-6363-449a-893b8c464a01.jpg","id":49357,"isaddress":0,"name":"杨杰04(test16)",
 *                 "orgName":"运维单位1",
 *
 *                 "orgPath":"5026021b6f24462c943279be4b93a97a_en1bd1d844ab2e476cb9e875b2f65e8fcc_en788af995ac3e4f49aa9f418f55bd6a12_enf1925fe112f7448490ed0a07d3e832c6_enaaab1e8bf6864fa7a43c33d55606e52c_dep7b0ec4b1b0c146c99478bbce907c6ed8",
 *                 "pFilid":"dep7b0ec4b1b0c146c99478bbce907c6ed8","phone":"17380509711","postType":"0","sex":"0","shortName":"杨杰04(test16)","sort":0,"status":1,"type":"2","updateTime":1651802590270,
 *                 "userId":"ub4e9c3019b9d4f07b9c8cac973be2be6"}
 *
 */
public class NoticeList{

// int activeStatus ; // String depName; // String filid; // String headImg; // int id; // int isaddress; public Long id=0L;

    public String name="";
    public String shortName="";

    public String filid="";//当前id
    public String pFilid=""; //上一级id

    /**
     *  当 type  2  filid 是loginid    pFilid  是部门id
     *  当 type  1  filid 部门id    pFilid  是上级id
     */
    public String type="";// 012   机构、部门、人员
    public int status=-1;  // 1正常  ,其他是禁用
    public String headImg="";
    public int activeStatus=-1;  //1 已经激活  0 为激活
    public  String  phone  ="";//手机号码
    public  int  sex = 0;//  0男,1女
    public int sort= 0; //排序
    public  int isaddress=0; //是否隐藏手机号码   1 是 0否
    public String  orgPath; //机构路径

    public   String postType;
    public String depName;
    public String orgName;

    public Long updateTime=0L;

    public String userId;

}

public CustomTextMessageNoticeContent(String content) {
    this.content = content;
}

public String getContent() {
    return content;
}

public void setContent(String content) {
    this.content = content;
}

public QuoteInfo getQuoteInfo() {
    return quoteInfo;
}

public void setQuoteInfo(QuoteInfo quoteInfo) {
    this.quoteInfo = quoteInfo;
}

@Override
public String toString() {
    return super.toString();
}
@Override
public MessagePayload encode() {
    NoticeList  notice=new NoticeList();
    JSONObject object_notice = new JSONObject();
    try {
    object_notice.put("activeStatus",notice.activeStatus);
    object_notice.put("depName",notice.depName);
    object_notice.put("filid",notice.filid);
    object_notice.put("headImg",notice.headImg);
    object_notice.put("id",notice.id);
    object_notice.put("isaddress",notice.isaddress);
    object_notice.put("name",notice.name);
    object_notice.put("orgName",notice.orgName);
    object_notice.put("orgPath",notice.orgPath);
    object_notice.put("pFilid",notice.pFilid);
    object_notice.put("phone",notice.phone);
    object_notice.put("postType",notice.postType);
    object_notice.put("sex",notice.sex);
    object_notice.put("shortName",notice.shortName);
    object_notice.put("sort",notice.sort);
    object_notice.put("status",notice.status);
    object_notice.put("type",notice.type);
    object_notice.put("updateTime",notice.updateTime);
    object_notice.put("userId",notice.userId);

    } catch (JSONException e) {
        e.printStackTrace();
    }

    MessagePayload payload = super.encode();

// payload.searchableContent = content; payload.searchableContent =""+object_notice; payload.content =""+object_notice; payload.mentionedType = mentionedType; payload.mentionedTargets = mentionedTargets; if (quoteInfo != null) { JSONObject object = new JSONObject(); try { object.put("quote", quoteInfo.encode()); payload.binaryContent = object.toString().getBytes(); } catch (JSONException e) { e.printStackTrace(); } } Log.i("-----------------------------","-----------CustomTextMessageNoticeContent-------MessagePayload-------payload-----通知-----------"+payload.toString()); return payload; }

@Override
public void decode(MessagePayload payload) {

// content = payload.searchableContent; content = ""+ GsonUtil.GsonString(NoticeList.class); mentionedType = payload.mentionedType; mentionedTargets = payload.mentionedTargets; if (payload.binaryContent != null && payload.binaryContent.length > 0) { try { JSONObject object = new JSONObject(new String(payload.binaryContent)); quoteInfo = new QuoteInfo(); quoteInfo.decode(object.optJSONObject("quote"));

            Log.i("-----------------------------","-------CustomTextMessageNoticeContent-----------MessagePayload-------payload-----decode-----------"+quoteInfo.toString());

        } catch (JSONException e) {
            e.printStackTrace();
        }
    }
}

@Override
public String digest(Message message) {
    return content;
}

@Override
public int describeContents() {
    return 0;
}

@Override
public void writeToParcel(Parcel dest, int flags) {
    super.writeToParcel(dest, flags);
    dest.writeString(this.content);
    dest.writeParcelable(this.quoteInfo, flags);
}

protected CustomTextMessageNoticeContent(Parcel in) {
    super(in);
    this.content = in.readString();
    this.quoteInfo = in.readParcelable(QuoteInfo.class.getClassLoader());
}

public static final Creator<CustomTextMessageNoticeContent> CREATOR = new Creator<CustomTextMessageNoticeContent>() {
    @Override
    public CustomTextMessageNoticeContent createFromParcel(Parcel source) {
        return new CustomTextMessageNoticeContent(source);
    }

    @Override
    public CustomTextMessageNoticeContent[] newArray(int size) {
        return new CustomTextMessageNoticeContent[size];
    }
};

}

这个是接受到的消息 {messageId=99, conversation=Conversation{type=Single, target='systemUpdateUser', line=0}, sender='systemUpdateUser', toUsers=null, content=TextMessageContent{mentionedType=0, mentionedTargets=[], extra='', pushContent='null', content='{"activeStatus":1,"depName":"开发部1","filid":"13cf82a8c41e483995d046006d104cf0","headImg":"http://www.scgzjg.cn/userimg/01/fe736bf0-449a-8983-893b8c464a01.jpg","id":49357,"isaddress":0,"name":"杨1(test16)","orgName":"单位21","orgPath":"5026021b6f24462c943279be4b93a97a_en1bd1d844ab2e476cb9e875b2f65e8fcc_en788af995ac3e4f49aa9f418f55bd6a12_enf1925fe112f7448490ed0a07d3e832c6_enaaab1e8bf6864fa7a43c33d55606e52c_dep7b0ec4b1b0c146c99478bbce907c6ed8","pFilid":"dep7b0ec4b1b0c146c99478bbce907c6ed8","phone":"17380509711","postType":"0","sex":"0","shortName":"杨204(test16)","sort":0,"status":1,"type":"2","updateTime":1651807642684,"userId":"ub4e9c3019b9d4f07b9c8cac973be2be6"}', quoteInfo=null}, direction=Receive, status=Unread, messageUid=287460070350913665, serverTime=1651807643043, localExtra=''}

这样写还是不对?

jianghu9527 commented 2 years ago

我那里写的不对?

imndx commented 2 years ago

你说的不对是说 Android 端发送的是自定义的CustomTextMessageNoticeContent,而 web/pc 端收到之后,转成了TextMessageContent吗?

针对你上面的自定义消息,再说几点:

  1. quoteInfo 是文本消息里面的字段,是表示引用的内容,这儿可以不要。
  2. MessageContentType.ContentType_Notice_Txt 这个值是多少?不能和原来的ContentType_Text重复
  3. encode方法,没必要把你的object_notice放到payload的所有字段去,放到binaryContent字段即可。但这儿问题不大。
  4. decode方法不对
    JSONObject object = new JSONObject(new String(payload.binaryContent));
    // 你得从object 对象里面取值,并赋值到自定义消息去,比如:
    this. type = object.optString(type);
    ...
  5. 下面这两个方法不对,要把所有的field都写上,建议用工具生成。
    
    @Override
    public void writeToParcel(Parcel dest, int flags) {
    super.writeToParcel(dest, flags);
    dest.writeString(this.content);
    dest.writeParcelable(this.quoteInfo, flags);
    }

protected CustomTextMessageNoticeContent(Parcel in) { super(in); this.content = in.readString(); this.quoteInfo = in.readParcelable(QuoteInfo.class.getClassLoader()); }