package com.netease.nim.uikit.session.module.list;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SimpleItemAnimator;
import android.text.TextUtils;
import android.util.Pair;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;
import com.netease.nim.uikit.NimUIKit;
import com.netease.nim.uikit.R;
import com.netease.nim.uikit.UserPreferences;
import com.netease.nim.uikit.common.ui.dialog.CustomAlertDialog;
import com.netease.nim.uikit.common.ui.dialog.EasyAlertDialog;
import com.netease.nim.uikit.common.ui.dialog.EasyAlertDialogHelper;
import com.netease.nim.uikit.common.ui.recyclerview.adapter.BaseFetchLoadAdapter;
import com.netease.nim.uikit.common.ui.recyclerview.loadmore.MsgListFetchLoadMoreView;
import com.netease.nim.uikit.common.util.media.BitmapDecoder;
import com.netease.nim.uikit.common.util.sys.ClipboardUtil;
import com.netease.nim.uikit.common.util.sys.NetworkUtil;
import com.netease.nim.uikit.common.util.sys.ScreenUtil;
import com.netease.nim.uikit.contact_selector.activity.ContactSelectActivity;
import com.netease.nim.uikit.session.activity.VoiceTrans;
import com.netease.nim.uikit.session.audio.MessageAudioControl;
import com.netease.nim.uikit.session.helper.MessageHelper;
import com.netease.nim.uikit.session.helper.MessageListPanelHelper;
import com.netease.nim.uikit.session.module.Container;
import com.netease.nim.uikit.uinfo.UserInfoHelper;
import com.netease.nim.uikit.uinfo.UserInfoObservable;
import com.netease.nimlib.sdk.NIMClient;
import com.netease.nimlib.sdk.Observer;
import com.netease.nimlib.sdk.RequestCallback;
import com.netease.nimlib.sdk.RequestCallbackWrapper;
import com.netease.nimlib.sdk.ResponseCode;
import com.netease.nimlib.sdk.avchat.model.AVChatAttachment;
import com.netease.nimlib.sdk.msg.MessageBuilder;
import com.netease.nimlib.sdk.msg.MsgService;
import com.netease.nimlib.sdk.msg.MsgServiceObserve;
import com.netease.nimlib.sdk.msg.attachment.AudioAttachment;
import com.netease.nimlib.sdk.msg.attachment.FileAttachment;
import com.netease.nimlib.sdk.msg.constant.AttachStatusEnum;
import com.netease.nimlib.sdk.msg.constant.MsgDirectionEnum;
import com.netease.nimlib.sdk.msg.constant.MsgStatusEnum;
import com.netease.nimlib.sdk.msg.constant.MsgTypeEnum;
import com.netease.nimlib.sdk.msg.constant.SessionTypeEnum;
import com.netease.nimlib.sdk.msg.model.AttachmentProgress;
import com.netease.nimlib.sdk.msg.model.IMMessage;
import com.netease.nimlib.sdk.msg.model.QueryDirectionEnum;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* 基于RecyclerView的消息收发模块
* Created by huangjun on 2016/12/27.
*/
public class MessageListPanelEx {
private static final int REQUEST_CODE_FORWARD_PERSON = 0x01;
private static final int REQUEST_CODE_FORWARD_TEAM = 0x02;
// container
private Container container;
private View rootView;
// message list view
private RecyclerView messageListView;
private List<IMMessage> items;
private MsgAdapter adapter;
private ImageView listviewBk;
// 新消息到达提醒
private IncomingMsgPrompt incomingMsgPrompt;
private Handler uiHandler;
// 仅显示消息记录,不接收和发送消息
private boolean recordOnly;
// 从服务器拉取消息记录
private boolean remote;
// 语音转文字
private VoiceTrans voiceTrans;
// 待转发消息
private IMMessage forwardMessage;
// 背景图片缓存
private static Pair<String, Bitmap> background;
public MessageListPanelEx(Container container, View rootView, boolean recordOnly, boolean remote) {
this(container, rootView, null, recordOnly, remote);
}
public MessageListPanelEx(Container container, View rootView, IMMessage anchor, boolean recordOnly, boolean remote) {
this.container = container;
this.rootView = rootView;
this.recordOnly = recordOnly;
this.remote = remote;
init(anchor);
}
public void onResume() {
setEarPhoneMode(UserPreferences.isEarPhoneModeEnable());
}
public void onPause() {
MessageAudioControl.getInstance(container.activity).stopAudio();
}
public void onDestroy() {
registerObservers(false);
}
public boolean onBackPressed() {
uiHandler.removeCallbacks(null);
MessageAudioControl.getInstance(container.activity).stopAudio(); // 界面返回,停止语音播放
if (voiceTrans != null && voiceTrans.isShow()) {
voiceTrans.hide();
return true;
}
return false;
}
public void reload(Container container, IMMessage anchor) {
this.container = container;
if (adapter != null) {
adapter.clearData();
}
initFetchLoadListener(anchor);
}
private void init(IMMessage anchor) {
initListView(anchor);
this.uiHandler = new Handler();
if (!recordOnly) {
incomingMsgPrompt = new IncomingMsgPrompt(container.activity, rootView, messageListView, adapter, uiHandler);
}
registerObservers(true);
}
private void initListView(IMMessage anchor) {
listviewBk = (ImageView) rootView.findViewById(R.id.message_activity_background);
// RecyclerView
messageListView = (RecyclerView) rootView.findViewById(R.id.messageListView);
messageListView.setLayoutManager(new LinearLayoutManager(container.activity));
messageListView.requestDisallowInterceptTouchEvent(true);
messageListView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState != RecyclerView.SCROLL_STATE_IDLE) {
container.proxy.shouldCollapseInputPanel();
}
}
});
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
messageListView.setOverScrollMode(View.OVER_SCROLL_NEVER);
}
// adapter
items = new ArrayList<>();
adapter = new MsgAdapter(messageListView, items);
adapter.setFetchMoreView(new MsgListFetchLoadMoreView());
adapter.setLoadMoreView(new MsgListFetchLoadMoreView());
adapter.setEventListener(new MsgItemEventListener());
initFetchLoadListener(anchor);
messageListView.setAdapter(adapter);
}
private void initFetchLoadListener(IMMessage anchor) {
MessageLoader loader = new MessageLoader(anchor, remote);
if (recordOnly && !remote) {
// 双向Load
adapter.setOnFetchMoreListener(loader);
adapter.setOnLoadMoreListener(loader);
} else {
// 只下来加载old数据
adapter.setOnFetchMoreListener(loader);
}
}
// 刷新消息列表
public void refreshMessageList() {
container.activity.runOnUiThread(new Runnable() {
@Override
public void run() {
adapter.notifyDataSetChanged();
}
});
}
public void scrollToBottom() {
uiHandler.postDelayed(new Runnable() {
@Override
public void run() {
doScrollToBottom();
}
}, 200);
}
private void doScrollToBottom() {
messageListView.scrollToPosition(adapter.getBottomDataPosition());
}
public void onIncomingMessage(List<IMMessage> messages) {
boolean needScrollToBottom = isLastMessageVisible();
boolean needRefresh = false;
List<IMMessage> addedListItems = new ArrayList<>(messages.size());
for (IMMessage message : messages) {
if (isMyMessage(message)) {
items.add(message);
addedListItems.add(message);
needRefresh = true;
}
}
if (needRefresh) {
sortMessages(items);
adapter.notifyDataSetChanged();
}
adapter.updateShowTimeItem(addedListItems, false, true);
// incoming messages tip
IMMessage lastMsg = messages.get(messages.size() - 1);
if (isMyMessage(lastMsg)) {
if (needScrollToBottom) {
doScrollToBottom();
} else if (incomingMsgPrompt != null && lastMsg.getSessionType() != SessionTypeEnum.ChatRoom) {
incomingMsgPrompt.show(lastMsg);
}
}
}
private boolean isLastMessageVisible() {
LinearLayoutManager layoutManager = (LinearLayoutManager) messageListView.getLayoutManager();
int lastVisiblePosition = layoutManager.findLastCompletelyVisibleItemPosition();
return lastVisiblePosition >= adapter.getBottomDataPosition();
}
// 发送消息后,更新本地消息列表
public void onMsgSend(IMMessage message) {
List<IMMessage> addedListItems = new ArrayList<>(1);
addedListItems.add(message);
adapter.updateShowTimeItem(addedListItems, false, true);
adapter.appendData(message);
doScrollToBottom();
}
/**
* **************************** 排序 ***********************************
*/
private void sortMessages(List<IMMessage> list) {
if (list.size() == 0) {
return;
}
Collections.sort(list, comp);
}
private static Comparator<IMMessage> comp = new Comparator<IMMessage>() {
@Override
public int compare(IMMessage o1, IMMessage o2) {
long time = o1.getTime() - o2.getTime();
return time == 0 ? 0 : (time < 0 ? -1 : 1);
}
};
/**
* ************************* 观察者 ********************************
*/
private void registerObservers(boolean register) {
MsgServiceObserve service = NIMClient.getService(MsgServiceObserve.class);
service.observeMsgStatus(messageStatusObserver, register);
service.observeAttachmentProgress(attachmentProgressObserver, register);
service.observeRevokeMessage(revokeMessageObserver, register);
if (register) {
registerUserInfoObserver();
} else {
unregisterUserInfoObserver();
}
MessageListPanelHelper.getInstance().registerObserver(incomingLocalMessageObserver, register);
}
/**
* 消息状态变化观察者
*/
Observer<IMMessage> messageStatusObserver = new Observer<IMMessage>() {
@Override
public void onEvent(IMMessage message) {
if (isMyMessage(message)) {
onMessageStatusChange(message);
}
}
};
/**
* 消息附件上传/下载进度观察者
*/
Observer<AttachmentProgress> attachmentProgressObserver = new Observer<AttachmentProgress>() {
@Override
public void onEvent(AttachmentProgress progress) {
onAttachmentProgressChange(progress);
}
};
/**
* 本地消息接收观察者
*/
MessageListPanelHelper.LocalMessageObserver incomingLocalMessageObserver = new MessageListPanelHelper.LocalMessageObserver() {
@Override
public void onAddMessage(IMMessage message) {
if (message == null || !container.account.equals(message.getSessionId())) {
return;
}
onMsgSend(message);
}
@Override
public void onClearMessages(String account) {
items.clear();
refreshMessageList();
}
};
/**
* 消息撤回观察者
*/
Observer<IMMessage> revokeMessageObserver = new Observer<IMMessage>() {
@Override
public void onEvent(IMMessage message) {
if (message == null || !container.account.equals(message.getSessionId())) {
return;
}
deleteItem(message, false);
}
};
private void onMessageStatusChange(IMMessage message) {
int index = getItemIndex(message.getUuid());
if (index >= 0 && index < items.size()) {
IMMessage item = items.get(index);
item.setStatus(message.getStatus());
item.setAttachStatus(message.getAttachStatus());
if (item.getAttachment() instanceof AVChatAttachment
|| item.getAttachment() instanceof AudioAttachment) {
item.setAttachment(message.getAttachment());
}
// resend的的情况,可能时间已经变化了,这里要重新检查是否要显示时间
List<IMMessage> msgList = new ArrayList<>(1);
msgList.add(message);
adapter.updateShowTimeItem(msgList, false, true);
refreshViewHolderByIndex(index);
}
}
private void onAttachmentProgressChange(AttachmentProgress progress) {
int index = getItemIndex(progress.getUuid());
if (index >= 0 && index < items.size()) {
IMMessage item = items.get(index);
float value = (float) progress.getTransferred() / (float) progress.getTotal();
adapter.putProgress(item, value);
refreshViewHolderByIndex(index);
}
}
public boolean isMyMessage(IMMessage message) {
return message.getSessionType() == container.sessionType
&& message.getSessionId() != null
&& message.getSessionId().equals(container.account);
}
/**
* 刷新单条消息
*/
private void refreshViewHolderByIndex(final int index) {
container.activity.runOnUiThread(new Runnable() {
@Override
public void run() {
if (index < 0) {
return;
}
adapter.notifyDataItemChanged(index);
}
});
}
private int getItemIndex(String uuid) {
for (int i = 0; i < items.size(); i++) {
IMMessage message = items.get(i);
if (TextUtils.equals(message.getUuid(), uuid)) {
return i;
}
}
return -1;
}
public void setChattingBackground(String uriString, int color) {
if (uriString != null) {
Uri uri = Uri.parse(uriString);
if (uri.getScheme().equalsIgnoreCase("file") && uri.getPath() != null) {
listviewBk.setImageBitmap(getBackground(uri.getPath()));
} else if (uri.getScheme().equalsIgnoreCase("android.resource")) {
List<String> paths = uri.getPathSegments();
if (paths == null || paths.size() != 2) {
return;
}
String type = paths.get(0);
String name = paths.get(1);
String pkg = uri.getHost();
int resId = container.activity.getResources().getIdentifier(name, type, pkg);
if (resId != 0) {
listviewBk.setBackgroundResource(resId);
}
}
} else if (color != 0) {
listviewBk.setBackgroundColor(color);
}
}
/**
* ***************************************** 数据加载 *********************************************
*/
private class MessageLoader implements BaseFetchLoadAdapter.RequestLoadMoreListener, BaseFetchLoadAdapter.RequestFetchMoreListener {
private static final int LOAD_MESSAGE_COUNT = 20;
private QueryDirectionEnum direction = null;
private IMMessage anchor;
private boolean remote;
private boolean firstLoad = true;
public MessageLoader(IMMessage anchor, boolean remote) {
this.anchor = anchor;
this.remote = remote;
if (remote) {
loadFromRemote();
} else {
if (anchor == null) {
loadFromLocal(QueryDirectionEnum.QUERY_OLD);
} else {
loadAnchorContext(); // 加载指定anchor的上下文
}
}
}
private RequestCallback<List<IMMessage>> callback = new RequestCallbackWrapper<List<IMMessage>>() {
@Override
public void onResult(int code, List<IMMessage> messages, Throwable exception) {
if (code != ResponseCode.RES_SUCCESS || exception != null) {
if (direction == QueryDirectionEnum.QUERY_OLD) {
adapter.fetchMoreFailed();
} else if (direction == QueryDirectionEnum.QUERY_NEW) {
adapter.loadMoreFail();
}
return;
}
if (messages != null) {
onMessageLoaded(messages);
}
}
};
private void loadAnchorContext() {
// query new, auto load old
direction = QueryDirectionEnum.QUERY_NEW;
NIMClient.getService(MsgService.class).queryMessageListEx(anchor(), direction, LOAD_MESSAGE_COUNT, true)
.setCallback(new RequestCallbackWrapper<List<IMMessage>>() {
@Override
public void onResult(int code, List<IMMessage> messages, Throwable exception) {
if (code != ResponseCode.RES_SUCCESS || exception != null) {
return;
}
onAnchorContextMessageLoaded(messages);
}
});
}
private void loadFromLocal(QueryDirectionEnum direction) {
this.direction = direction;
NIMClient.getService(MsgService.class).queryMessageListEx(anchor(), direction, LOAD_MESSAGE_COUNT, true)
.setCallback(callback);
}
private void loadFromRemote() {
this.direction = QueryDirectionEnum.QUERY_OLD;
NIMClient.getService(MsgService.class).pullMessageHistory(anchor(), LOAD_MESSAGE_COUNT, true)
.setCallback(callback);
}
private IMMessage anchor() {
if (items.size() == 0) {
return anchor == null ? MessageBuilder.createEmptyMessage(container.account, container.sessionType, 0) : anchor;
} else {
int index = (direction == QueryDirectionEnum.QUERY_NEW ? items.size() - 1 : 0);
return items.get(index);
}
}
private void onMessageLoaded(final List<IMMessage> messages) {
if (messages == null) {
return;
}
if (remote) {
Collections.reverse(messages);
}
// 在第一次加载的过程中又收到了新消息,做一下去重
if (firstLoad && items.size() > 0) {
for (IMMessage message : messages) {
int removeIndex = 0;
for (IMMessage item : items) {
if (item.isTheSame(message)) {
adapter.remove(removeIndex);
break;
}
removeIndex++;
}
}
}
// 加入anchor
if (firstLoad && anchor != null) {
messages.add(anchor);
}
// 在更新前,先确定一些标记
List<IMMessage> total = new ArrayList<>();
total.addAll(items);
boolean isBottomLoad = direction == QueryDirectionEnum.QUERY_NEW;
if (isBottomLoad) {
total.addAll(messages);
} else {
total.addAll(0, messages);
}
adapter.updateShowTimeItem(total, true, firstLoad); // 更新要显示时间的消息
updateReceipt(total); // 更新已读回执标签
// 加载状态修改,刷新界面
int count = messages.size();
if (isBottomLoad) {
// 底部加载
if (count <= 0) {
adapter.loadMoreEnd(true);
} else {
adapter.loadMoreComplete(messages);
}
} else {
// 顶部加载
if (count <= 0) {
adapter.fetchMoreEnd(true);
} else {
adapter.fetchMoreComplete(messageListView, messages);
}
}
// 如果是第一次加载,updateShowTimeItem返回的就是lastShowTimeItem
if (firstLoad) {
doScrollToBottom();
sendReceipt(); // 发送已读回执
}
firstLoad = false;
}
private void onAnchorContextMessageLoaded(final List<IMMessage> messages) {
if (messages == null) {
return;
}
if (remote) {
Collections.reverse(messages);
}
int loadCount = messages.size();
if (firstLoad && anchor != null) {
messages.add(0, anchor);
}
// 在更新前,先确定一些标记
adapter.updateShowTimeItem(messages, true, firstLoad); // 更新要显示时间的消息
updateReceipt(messages); // 更新已读回执标签
// new data
adapter.appendData(messages);
if (loadCount < LOAD_MESSAGE_COUNT) {
adapter.loadMoreEnd(true);
}
firstLoad = false;
}
@Override
public void onFetchMoreRequested() {
// 顶部加载历史数据
if (remote) {
loadFromRemote();
} else {
loadFromLocal(QueryDirectionEnum.QUERY_OLD);
}
}
@Override
public void onLoadMoreRequested() {
// 底部加载新数据
if (!remote) {
loadFromLocal(QueryDirectionEnum.QUERY_NEW);
}
}
}
private class MsgItemEventListener implements MsgAdapter.ViewHolderEventListener {
@Override
public void onFailedBtnClick(IMMessage message) {
if (message.getDirect() == MsgDirectionEnum.Out) {
// 发出的消息,如果是发送失败,直接重发,否则有可能是漫游到的多媒体消息,但文件下载
if (message.getStatus() == MsgStatusEnum.fail) {
resendMessage(message); // 重发
} else {
if (message.getAttachment() instanceof FileAttachment) {
FileAttachment attachment = (FileAttachment) message.getAttachment();
if (TextUtils.isEmpty(attachment.getPath())
&& TextUtils.isEmpty(attachment.getThumbPath())) {
showReDownloadConfirmDlg(message);
}
} else {
resendMessage(message);
}
}
} else {
showReDownloadConfirmDlg(message);
}
}
@Override
public boolean onViewHolderLongClick(View clickView, View viewHolderView, IMMessage item) {
if (container.proxy.isLongClickEnabled()) {
showLongClickAction(item);
}
return true;
}
// 重新下载(对话框提示)
private void showReDownloadConfirmDlg(final IMMessage message) {
EasyAlertDialogHelper.OnDialogActionListener listener = new EasyAlertDialogHelper.OnDialogActionListener() {
@Override
public void doCancelAction() {
}
@Override
public void doOkAction() {
// 正常情况收到消息后附件会自动下载。如果下载失败,可调用该接口重新下载
if (message.getAttachment() != null && message.getAttachment() instanceof FileAttachment)
NIMClient.getService(MsgService.class).downloadAttachment(message, true);
}
};
final EasyAlertDialog dialog = EasyAlertDialogHelper.createOkCancelDiolag(container.activity, null,
container.activity.getString(R.string.repeat_download_message), true, listener);
dialog.show();
}
// 重发消息到服务器
private void resendMessage(IMMessage message) {
// 重置状态为unsent
int index = getItemIndex(message.getUuid());
if (index >= 0 && index < items.size()) {
IMMessage item = items.get(index);
item.setStatus(MsgStatusEnum.sending);
deleteItem(item, true);
onMsgSend(item);
}
NIMClient.getService(MsgService.class).sendMessage(message, true);
}
/**
* ****************************** 长按菜单 ********************************
*/
// 长按消息Item后弹出菜单控制
private void showLongClickAction(IMMessage selectedItem) {
onNormalLongClick(selectedItem);
}
/**
* 长按菜单操作
*
* @param item
*/
private void onNormalLongClick(IMMessage item) {
CustomAlertDialog alertDialog = new CustomAlertDialog(container.activity);
alertDialog.setCancelable(true);
alertDialog.setCanceledOnTouchOutside(true);
prepareDialogItems(item, alertDialog);
alertDialog.show();
}
// 长按消息item的菜单项准备。如果消息item的MsgViewHolder处理长按事件(MsgViewHolderBase#onItemLongClick),且返回为true,
// 则对应项的长按事件不会调用到此处
private void prepareDialogItems(final IMMessage selectedItem, CustomAlertDialog alertDialog) {
MsgTypeEnum msgType = selectedItem.getMsgType();
MessageAudioControl.getInstance(container.activity).stopAudio();
// 0 EarPhoneMode
longClickItemEarPhoneMode(alertDialog, msgType);
// 1 resend
longClickItemResend(selectedItem, alertDialog);
// 2 copy
longClickItemCopy(selectedItem, alertDialog, msgType);
// 3 revoke
if (selectedItem.getDirect() == MsgDirectionEnum.Out && selectedItem.getStatus() == MsgStatusEnum.success
&& !NimUIKit.getMsgRevokeFilter().shouldIgnore(selectedItem) && !recordOnly) {
longClickRevokeMsg(selectedItem, alertDialog);
}
// 4 delete
longClickItemDelete(selectedItem, alertDialog);
// 5 trans
longClickItemVoidToText(selectedItem, alertDialog, msgType);
if (!NimUIKit.getMsgForwardFilter().shouldIgnore(selectedItem) && !recordOnly) {
// 6 forward to person
longClickItemForwardToPerson(selectedItem, alertDialog);
// 7 forward to team
longClickItemForwardToTeam(selectedItem, alertDialog);
}
}
// 长按菜单项--重发
private void longClickItemResend(final IMMessage item, CustomAlertDialog alertDialog) {
if (item.getStatus() != MsgStatusEnum.fail) {
return;
}
alertDialog.addItem(container.activity.getString(R.string.repeat_send_has_blank), new CustomAlertDialog.onSeparateItemClickListener() {
@Override
public void onClick() {
onResendMessageItem(item);
}
});
}
private void onResendMessageItem(IMMessage message) {
int index = getItemIndex(message.getUuid());
if (index >= 0) {
showResendConfirm(message, index); // 重发确认
}
}
private void showResendConfirm(final IMMessage message, final int index) {
EasyAlertDialogHelper.OnDialogActionListener listener = new EasyAlertDialogHelper.OnDialogActionListener() {
@Override
public void doCancelAction() {
}
@Override
public void doOkAction() {
resendMessage(message);
}
};
final EasyAlertDialog dialog = EasyAlertDialogHelper.createOkCancelDiolag(container.activity, null,
container.activity.getString(R.string.repeat_send_message), true, listener);
dialog.show();
}
// 长按菜单项--复制
private void longClickItemCopy(final IMMessage item, CustomAlertDialog alertDialog, MsgTypeEnum msgType) {
if (msgType != MsgTypeEnum.text) {
return;
}
alertDialog.addItem(container.activity.getString(R.string.copy_has_blank), new CustomAlertDialog.onSeparateItemClickListener() {
@Override
public void onClick() {
onCopyMessageItem(item);
}
});
}
private void onCopyMessageItem(IMMessage item) {
ClipboardUtil.clipboardCopyText(container.activity, item.getContent());
}
// 长按菜单项--删除
private void longClickItemDelete(final IMMessage selectedItem, CustomAlertDialog alertDialog) {
if (recordOnly) {
return;
}
alertDialog.addItem(container.activity.getString(R.string.delete_has_blank), new CustomAlertDialog.onSeparateItemClickListener() {
@Override
public void onClick() {
deleteItem(selectedItem, true);
}
});
}
// 长按菜单项 -- 音频转文字
private void longClickItemVoidToText(final IMMessage item, CustomAlertDialog alertDialog, MsgTypeEnum msgType) {
if (msgType != MsgTypeEnum.audio) return;
if (item.getDirect() == MsgDirectionEnum.In
&& item.getAttachStatus() != AttachStatusEnum.transferred)
return;
if (item.getDirect() == MsgDirectionEnum.Out
&& item.getAttachStatus() != AttachStatusEnum.transferred)
return;
alertDialog.addItem(container.activity.getString(R.string.voice_to_text), new CustomAlertDialog.onSeparateItemClickListener() {
@Override
public void onClick() {
onVoiceToText(item);
}
});
}
// 语音转文字
private void onVoiceToText(IMMessage item) {
if (voiceTrans == null)
voiceTrans = new VoiceTrans(container.activity);
voiceTrans.voiceToText(item);
if (item.getDirect() == MsgDirectionEnum.In && item.getStatus() != MsgStatusEnum.read) {
item.setStatus(MsgStatusEnum.read);
NIMClient.getService(MsgService.class).updateIMMessageStatus(item);
adapter.notifyDataSetChanged();
}
}
// 长按菜单项 -- 听筒扬声器切换
private void longClickItemEarPhoneMode(CustomAlertDialog alertDialog, MsgTypeEnum msgType) {
if (msgType != MsgTypeEnum.audio) return;
String content = UserPreferences.isEarPhoneModeEnable() ? "切换成扬声器播放" : "切换成听筒播放";
final String finalContent = content;
alertDialog.addItem(content, new CustomAlertDialog.onSeparateItemClickListener() {
@Override
public void onClick() {
Toast.makeText(container.activity, finalContent, Toast.LENGTH_SHORT).show();
setEarPhoneMode(!UserPreferences.isEarPhoneModeEnable());
}
});
}
// 长按菜单项 -- 转发到个人
private void longClickItemForwardToPerson(final IMMessage item, CustomAlertDialog alertDialog) {
alertDialog.addItem(container.activity.getString(R.string.forward_to_person), new CustomAlertDialog.onSeparateItemClickListener() {
@Override
public void onClick() {
forwardMessage = item;
ContactSelectActivity.Option option = new ContactSelectActivity.Option();
option.title = "选择转发的人";
option.type = ContactSelectActivity.ContactSelectType.BUDDY;
option.multi = false;
option.maxSelectNum = 1;
NimUIKit.startContactSelect(container.activity, option, REQUEST_CODE_FORWARD_PERSON);
}
});
}
// 长按菜单项 -- 转发到群组
private void longClickItemForwardToTeam(final IMMessage item, CustomAlertDialog alertDialog) {
alertDialog.addItem(container.activity.getString(R.string.forward_to_team), new CustomAlertDialog.onSeparateItemClickListener() {
@Override
public void onClick() {
forwardMessage = item;
ContactSelectActivity.Option option = new ContactSelectActivity.Option();
option.title = "选择转发的群";
option.type = ContactSelectActivity.ContactSelectType.TEAM;
option.multi = false;
option.maxSelectNum = 1;
NimUIKit.startContactSelect(container.activity, option, REQUEST_CODE_FORWARD_TEAM);
}
});
}
// 长按菜单项 -- 撤回消息
private void longClickRevokeMsg(final IMMessage item, CustomAlertDialog alertDialog) {
alertDialog.addItem(container.activity.getString(R.string.withdrawn_msg), new CustomAlertDialog.onSeparateItemClickListener() {
@Override
public void onClick() {
if (!NetworkUtil.isNetAvailable(container.activity)) {
Toast.makeText(container.activity, R.string.network_is_not_available, Toast.LENGTH_SHORT).show();
return;
}
NIMClient.getService(MsgService.class).revokeMessage(item).setCallback(new RequestCallback<Void>() {
@Override
public void onSuccess(Void param) {
deleteItem(item, false);
MessageHelper.getInstance().onRevokeMessage(item);
}
@Override
public void onFailed(int code) {
if (code == ResponseCode.RES_OVERDUE) {
Toast.makeText(container.activity, R.string.revoke_failed, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(container.activity, "revoke msg failed, code:" + code, Toast.LENGTH_SHORT).show();
}
}
@Override
public void onException(Throwable exception) {
}
});
}
});
}
}
private void setEarPhoneMode(boolean earPhoneMode) {
UserPreferences.setEarPhoneModeEnable(earPhoneMode);
MessageAudioControl.getInstance(container.activity).setEarPhoneModeEnable(earPhoneMode);
}
private Bitmap getBackground(String path) {
if (background != null && path.equals(background.first) && background.second != null) {
return background.second;
}
if (background != null && background.second != null) {
background.second.recycle();
}
Bitmap bitmap = null;
if (path.startsWith("/android_asset")) {
String asset = path.substring(path.indexOf("/", 1) + 1);
try {
InputStream ais = container.activity.getAssets().open(asset);
bitmap = BitmapDecoder.decodeSampled(ais, ScreenUtil.screenWidth, ScreenUtil.screenHeight);
ais.close();
} catch (IOException e) {
e.printStackTrace();
}
} else {
bitmap = BitmapDecoder.decodeSampled(path, ScreenUtil.screenWidth, ScreenUtil.screenHeight);
}
background = new Pair<>(path, bitmap);
return bitmap;
}
private UserInfoObservable.UserInfoObserver uinfoObserver;
private void registerUserInfoObserver() {
if (uinfoObserver == null) {
uinfoObserver = new UserInfoObservable.UserInfoObserver() {
@Override
public void onUserInfoChanged(List<String> accounts) {
if (container.sessionType == SessionTypeEnum.P2P) {
if (accounts.contains(container.account) || accounts.contains(NimUIKit.getAccount())) {
adapter.notifyDataSetChanged();
}
} else { // 群的,简单的全部重刷
adapter.notifyDataSetChanged();
}
}
};
}
UserInfoHelper.registerObserver(uinfoObserver);
}
private void unregisterUserInfoObserver() {
if (uinfoObserver != null) {
UserInfoHelper.unregisterObserver(uinfoObserver);
}
}
/**
* 收到已读回执(更新VH的已读label)
*/
public void receiveReceipt() {
updateReceipt(items);
refreshMessageList();
}
public void updateReceipt(final List<IMMessage> messages) {
for (int i = messages.size() - 1; i >= 0; i--) {
if (receiveReceiptCheck(messages.get(i))) {
adapter.setUuid(messages.get(i).getUuid());
break;
}
}
}
private boolean receiveReceiptCheck(final IMMessage msg) {
if (msg != null && msg.getSessionType() == SessionTypeEnum.P2P
&& msg.getDirect() == MsgDirectionEnum.Out
&& msg.getMsgType() != MsgTypeEnum.tip
&& msg.getMsgType() != MsgTypeEnum.notification
&& msg.isRemoteRead()) {
return true;
}
return false;
}
/**
* 发送已读回执(需要过滤)
*/
public void sendReceipt() {
if (container.account == null || container.sessionType != SessionTypeEnum.P2P) {
return;
}
IMMessage message = getLastReceivedMessage();
if (!sendReceiptCheck(message)) {
return;
}
NIMClient.getService(MsgService.class).sendMessageReceipt(container.account, message);
}
private IMMessage getLastReceivedMessage() {
IMMessage lastMessage = null;
for (int i = items.size() - 1; i >= 0; i--) {
if (sendReceiptCheck(items.get(i))) {
lastMessage = items.get(i);
break;
}
}
return lastMessage;
}
private boolean sendReceiptCheck(final IMMessage msg) {
if (msg == null || msg.getDirect() != MsgDirectionEnum.In ||
msg.getMsgType() == MsgTypeEnum.tip || msg.getMsgType() == MsgTypeEnum.notification) {
return false; // 非收到的消息,Tip消息和通知类消息,不要发已读回执
}
return true;
}
// 删除消息
private void deleteItem(IMMessage messageItem, boolean isRelocateTime) {
NIMClient.getService(MsgService.class).deleteChattingHistory(messageItem);
List<IMMessage> messages = new ArrayList<>();
for (IMMessage message : items) {
if (message.getUuid().equals(messageItem.getUuid())) {
continue;
}
messages.add(message);
}
updateReceipt(messages);
adapter.deleteItem(messageItem, isRelocateTime);
}
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != Activity.RESULT_OK) {
return;
}
final ArrayList<String> selected = data.getStringArrayListExtra(ContactSelectActivity.RESULT_DATA);
if (selected != null && !selected.isEmpty()) {
switch (requestCode) {
case REQUEST_CODE_FORWARD_TEAM:
doForwardMessage(selected.get(0), SessionTypeEnum.Team);
break;
case REQUEST_CODE_FORWARD_PERSON:
doForwardMessage(selected.get(0), SessionTypeEnum.P2P);
break;
}
}
}
// 转发消息
private void doForwardMessage(final String sessionId, SessionTypeEnum sessionTypeEnum) {
IMMessage message = MessageBuilder.createForwardMessage(forwardMessage, sessionId, sessionTypeEnum);
if (message == null) {
Toast.makeText(container.activity, "该类型不支持转发", Toast.LENGTH_SHORT).show();
return;
}
NIMClient.getService(MsgService.class).sendMessage(message, false);
if (container.account.equals(sessionId)) {
onMsgSend(message);
}
}
}