老版本的環信,實現訊息撤回功能。
阿新 • • 發佈:2019-01-01
最近公司很久之前的一個整合過環信聊天功能的app,要增加訊息撤回的功能,由於這個app是很久之前的。。所以,那個時候的easeUi裡面並沒有這個功能,於是就參照有這個功能的sdk,把程式碼搬了過來。。
Ps:當前的sdk版本是2.2.4
首先找到聊天頁面ChatActivity頁面,裡面是一個EaseUi裡面的EaseChatFragment,然後找到訊息框點選事件,messageList有個item點選事件,(EaseChatMessageList.MessageListItemClickListener),有一系列事件,你可以寫在訊息框點選事件裡(onBubbleClick),也可以寫在訊息框長按事件裡面(onBubbleLongClick),我是選擇了後者。
原理圖:
下面是效果圖:
下面貼程式碼:
messageList.setItemClickListener(new EaseChatMessageList.MessageListItemClickListener() {
@Override
public void onUserAvatarClick(String username) {
if (chatFragmentListener != null) {
chatFragmentListener.onAvatarClick(username);
}
}
@Override
public void onResendClick(final EMMessage message) {
new EaseAlertDialog(getActivity(), R.string.resend, R.string.confirm_resend, null, new AlertDialogUser() {
@Override
public void onResult(boolean confirmed, Bundle bundle) {
if (!confirmed) {
return;
}
resendMessage(message);
}
}, true).show();
}
@Override
public void onBubbleLongClick(EMMessage message) {
contextMenuMessage = message;
//這個是用來控制撤回這個選單是否顯示
boolean isvisibilty = false;
/**
* callback:這個用來判斷是否已經是撤回的訊息,
* 在訊息撤回以後,會發送一條新的訊息,內容是XX
* 撤回了一條訊息(我暫且叫CallMessage)
* 我在這個CallMessage中加了個擴充套件欄位callback,來標識該條訊息已經是
* 撤回的訊息了。即再長按該條訊息不需要再顯示撤回這個選單了
*
*/
boolean callback = false;
try {
callback = message.getBooleanAttribute("callback");//如果已經是撤回的訊息了,就不要顯示撤回這個選項了
} catch (EaseMobException e) {
e.printStackTrace();
}
/**
* 這裡需要判斷下該條訊息必須是自己發的,並且不是CallMessage
* 才能顯示撤回選單
*/
if (message.getFrom().equals(user.getHuanxinId()) && !callback) {
isvisibilty = true;
} else {
isvisibilty = false;
}
//長按以後彈出一個選擇框(包括複製訊息,刪除訊息,和撤回訊息)
startActivityForResult((new Intent(getActivity(), ContextMenuActivity.class))
.putExtra("message", message)
.putExtra("ischatroom", chatType == EaseConstant.CHATTYPE_CHATROOM)
.putExtra("isvisibilty", isvisibilty),
REQUEST_CODE_CONTEXT_MENU);
}
@Override
public boolean onBubbleClick(EMMessage message) {
if (chatFragmentListener != null) {
return chatFragmentListener.onMessageBubbleClick(message);
}
return false;
}
});
選擇框的樣式:
下面是ContextMenuActivity.class(選擇框)
import android.content.Intent;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
import com.easemob.chat.EMMessage;
import com.easemob.easeui.R;
import com.easemob.easeui.utils.Constant;
//import com.easemob.redpacketsdk.constant.RPConstant;
//import com.hyphenate.chat.EMMessage;
//import com.hyphenate.chatuidemo.Constant;
//import com.hyphenate.chatuidemo.R;
/**
* 選擇框頁面,根據不同的type,來配置不同的佈局,因為有的不需要有複製訊息這個選單
*/
public class ContextMenuActivity extends EaseBaseActivity {
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 RESULT_CODE_RECALL = 4;
private EMMessage message;
Intent intent;
boolean isVisibilty;//撤回是否可見
private TextView tvReCall;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
intent=getIntent();
message = intent.getParcelableExtra("message");
isVisibilty=intent.getBooleanExtra("isvisibilty",false);
// boolean isChatroom = getIntent().getBooleanExtra("ischatroom", false);
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)){
// //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{
setContentView(R.layout.em_context_menu_for_text);
}
} 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);
}
tvReCall= (TextView) findViewById(R.id.recall);
if(isVisibilty){
tvReCall.setVisibility(View.VISIBLE);
}else{
tvReCall.setVisibility(View.GONE);
}
// if (isChatroom
// //red packet code : 遮蔽紅包訊息的撤回功能
// || message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_RED_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 recall(View view){
setResult(RESULT_CODE_RECALL);
finish();
}
}
下面是佈局:em_context_menu_for_image:
<?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"
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"
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:id="@+id/forward"
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:visibility="gone"
android:textColor="@android:color/black"
android:textSize="20sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/recall"
android:background="@drawable/em_context_menu_item_bg"
android:clickable="true"
android:gravity="center_vertical"
android:onClick="recall"
android:padding="10dp"
android:text="@string/recall"
android:textColor="@android:color/black"
android:textSize="20sp" />
</LinearLayout>
其他的佈局大同小異,就不一一貼出來了。
然後選擇好是哪個選單以後,選擇框消失,回到聊天介面,
即EaseChatFragment的onActivityResult裡面。。
if (requestCode == REQUEST_CODE_CONTEXT_MENU) {
switch (resultCode) {
case ContextMenuActivity.RESULT_CODE_COPY: // copy
// MessageBody body = contextMenuMessage.getBody();
EMMessage.Type type = contextMenuMessage.getType();
if (type.equals(EMMessage.Type.TXT)) {
TextMessageBody txtBody = (TextMessageBody) contextMenuMessage.getBody();
clipboard.setText(txtBody.getMessage());
}
break;
case ContextMenuActivity.RESULT_CODE_DELETE: // delete
conversation.removeMessage(contextMenuMessage.getMsgId());
messageList.refresh();
break;
case ContextMenuActivity.RESULT_CODE_FORWARD: // forward
break;
case ContextMenuActivity.RESULT_CODE_RECALL://撤回
recall();
break;
default:
break;
}
}
重點看下recall()方法,這個就是撤回功能。
/**
* 撤回
* 原理:
* A使用者傳送訊息。
* A使用者需要撤回某條訊息,將訊息id通過擴充套件訊息傳送到使用者B。
* B使用者收到擴充套件訊息,解析其中的messageid,從資料庫刪除對應訊息。
*/
private void recall() {
// sendTextMessage();
EMMessage cmdMsg = EMMessage.createSendMessage(EMMessage.Type.CMD);
// 如果是群聊,設定chattype,預設是單聊
if (chatType == EaseConstant.CHATTYPE_GROUP) {
cmdMsg.setChatType(ChatType.GroupChat);
} else if (chatType == EaseConstant.CHATTYPE_CHATROOM) {
cmdMsg.setChatType(ChatType.ChatRoom);
}
String action = "REVOKE_FLAG";
CmdMessageBody cmdBody = new CmdMessageBody(action);
// 設定訊息body
cmdMsg.addBody(cmdBody);
// 設定要發給誰,使用者username或者群聊groupid
cmdMsg.setReceipt(toChatUsername);
// 通過擴充套件欄位新增要撤回訊息的id
cmdMsg.setAttribute("msgId", contextMenuMessage.getMsgId());
cmdMsg.setAttribute("name", user.getName());
EMChatManager.getInstance().sendMessage(cmdMsg, new EMCallBack() {
@Override
public void onSuccess() {
// Log.e("zmm","傳送成功-->");
/**
* 注意這裡一定要把需要撤回的訊息刪除,這裡如果不刪除,會出現,
* 自己這邊的聊天列表裡面該條訊息還在。
*/
//傳送成功以後再這裡把該撤回的訊息刪除,這裡是為了重新整理自己的聊天頁面
conversation.removeMessage(contextMenuMessage.getMsgId());
// Log.e("zmm", "刪除訊息-->" + contextMenuMessage.getMsgId());
/**2018-05-11更新:可以在傳送透傳訊息成功以後,就傳送一個“xx撤回一個訊息”的普通訊息,更好的處理。應該是。接收方接收到透傳。並且刪除掉你要撤回的訊息以後。那個時候。傳送一個透傳訊息。告訴你。你再來發送“xx撤回一個訊息”這樣的一條訊息比較合理。**/
EMMessage message = EMMessage.createTxtSendMessage(user.getName() + "回撤一條訊息",
toChatUsername);
message.setAttribute("callback", true);
sendMessage(message, user.getName());
}
@Override
public void onProgress(int progress, String status) {
}
@Override
public void onError(int code, String error) {
Log.e("zmm", "傳送失敗-->" + code + "-->" + error);
}
});
messageList.refreshSelectLast();
}
protected void sendMessage(EMMessage message, String name) {
if (chatFragmentListener != null) {
//設定擴充套件屬性
chatFragmentListener.onSetMessageAttributes(message);
}
// 如果是群聊,設定chattype,預設是單聊
if (chatType == EaseConstant.CHATTYPE_GROUP) {
message.setChatType(ChatType.GroupChat);
} else if (chatType == EaseConstant.CHATTYPE_CHATROOM) {
message.setChatType(ChatType.ChatRoom);
}
message.setAttribute("name", name);//這裡放入傳送人的名字,在需要拿到傳送者名字的時候再取出來。(XX)
//傳送訊息
EMChatManager.getInstance().sendMessage(message, null);
//重新整理ui
messageList.refreshSelectLast();
}
然後就是在接收到該條CallMessage的地方處理下就可以了。
還是再EaseChatFragment這個頁面,實現了EMEventListener這個介面,裡有接收到新訊息的回撥:
{
EMMessage message = (EMMessage) event.getData();
// Log.e("zmm","onEvent-->"+event.getEvent());
switch (event.getEvent()) {
case EventNewMessage:
// 獲取到message
String username = null;
// 群組訊息
if (message.getChatType() == ChatType.GroupChat || message.getChatType() == ChatType.ChatRoom) {
username = message.getTo();
} else {
// 單聊訊息
username = message.getFrom();
}
// 如果是當前會話的訊息,重新整理聊天頁面
if (username.equals(toChatUsername)) {
messageList.refreshSelectLast();
// 聲音和震動提示有新訊息
EaseUI.getInstance().getNotifier().viberateAndPlayTone(message);
} else {
// 如果訊息不是和當前聊天ID的訊息
EaseUI.getInstance().getNotifier().onNewMsg(message);
}
break;
case EventDeliveryAck:
case EventReadAck:
// 獲取到message
messageList.refresh();
break;
case EventOfflineMessage:
// a list of offline messages
// List<EMMessage> offlineMessages = (List<EMMessage>)
// event.getData();
messageList.refresh();
break;
case EventNewCMDMessage: // CMD訊息(這個就是CallMessage返回的type)
// Log.e("zmm", "接收到回撥訊息-->");
CmdMessageBody cmdMsgBody = (CmdMessageBody) message.getBody();
String action = cmdMsgBody.action;//獲取自定義action
String name = null;
try {
name = message.getStringAttribute("name");
} catch (EaseMobException e) {
Log.e("zmm", "-->" + e.toString() + "-->" + e.getMessage());
e.printStackTrace();
}
if (action.equals("REVOKE_FLAG")) {
try {
String msgId = message.getStringAttribute("msgId");
EMConversation conversation = EMChatManager.getInstance().getConversation(message.getFrom());
// --刪除訊息來表示撤回--
// Log.e("zmm",)
conversation.removeMessage(msgId);
// Log.e("zmm", "刪除訊息-->" + msgId);
//2018 -05-11更新:不應該再這裡傳送“XX撤回了一條訊息。”。
//刪除訊息以後增加一條XX撤回了一條訊息。
//傳送一個透傳訊息給撤回方
addcallbackmessage();
} catch (EaseMobException e) {
// TODO Auto-generated catch block
Log.e("zmm", "1111-->" + e.toString() + "-->" + e.getMessage());
e.printStackTrace();
}
}else if ("RECALLBACK_FLAG".equals(action)) {
//這裡是撤回方接收到。對方已經刪除了我需要撤回的訊息了。這個時候。我要傳送一個“xx撤回了一條訊息”這樣一個普通訊息。
EMMessage newmessage = EMMessage.createTxtSendMessage(user.getName() + "回撤一條訊息",
toChatUsername);
newmessage.setAttribute("callback", true);
sendMessage(newmessage, name);
}
break;
default:
break;
}
}
/**
* 發透傳訊息告訴需要撤回訊息的那位。告訴他我已經成功刪除訊息了
* 你可以再發送一條普通訊息,內容是“XX撤回了一條訊息”
*/
private void addcallbackmessage() {
EMMessage cmdMsg = EMMessage.createSendMessage(EMMessage.Type.CMD);
// 如果是群聊,設定chattype,預設是單聊
if (chatType == EaseConstant.CHATTYPE_GROUP) {
cmdMsg.setChatType(ChatType.GroupChat);
} else if (chatType == EaseConstant.CHATTYPE_CHATROOM) {
cmdMsg.setChatType(ChatType.ChatRoom);
}
CmdMessageBody cmdBody = new CmdMessageBody("RECALLBACK_FLAG");
// 設定訊息body
cmdMsg.addBody(cmdBody);
// 設定要發給誰,使用者username或者群聊groupid
cmdMsg.setReceipt(toChatUsername);
// 通過擴充套件欄位新增要撤回訊息的id
// cmdMsg.setAttribute("msgId", contextMenuMessage.getMsgId());
cmdMsg.setAttribute("name", user.getName());
EMChatManager.getInstance().sendMessage(cmdMsg, null);
}
以上就完成了訊息撤回功能。。特此記錄。望對大家有幫助。
最後獻上一句最近喜歡的話:
最怕一生碌碌無為,還說難得平凡可貴。。。。
加油!!!