私聊改造:输入体验优化

This commit is contained in:
huangjian
2023-01-11 19:09:36 +08:00
parent 42aea570b3
commit 69f0d76330
34 changed files with 1455 additions and 1021 deletions

View File

@@ -676,7 +676,6 @@
android:label="单聊界面"
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:theme="@style/AppTheme.NoActionBar"
android:windowSoftInputMode="stateHidden|adjustResize" />
<activity
android:name=".ui.im.avtivity.AddBlackListActivity"
@@ -952,10 +951,12 @@
<!-- 房间内私聊 -->
<activity
android:name=".room_chat.activity.NimRoomP2PMessageActivity"
android:theme="@style/room_message_activity" />
android:theme="@style/room_message_activity"
android:windowSoftInputMode="stateHidden|adjustResize" />
<activity
android:name=".room_chat.activity.RoomMsgActivity"
android:theme="@style/room_message_activity" />
android:theme="@style/room_message_activity"
android:windowSoftInputMode="stateHidden|adjustNothing" />
<activity
android:name=".room_chat.activity.RoomNewbieActivity"
android:theme="@style/room_message_activity" />

View File

@@ -0,0 +1,44 @@
package com.mango.moshen.ui.im;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.effective.android.panel.PanelSwitchHelper;
public class AutoHidePanelRecyclerView extends RecyclerView {
PanelSwitchHelper panelSwitchHelper;
public void setPanelSwitchHelper(PanelSwitchHelper panelSwitchHelper) {
this.panelSwitchHelper = panelSwitchHelper;
}
public AutoHidePanelRecyclerView(Context context) {
this(context, null);
}
public AutoHidePanelRecyclerView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public AutoHidePanelRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setLayoutManager(new LinearLayoutManager(context));
}
@Override
public boolean onTouchEvent(MotionEvent e) {
if (e != null && e.getAction() != MotionEvent.ACTION_CANCEL) {
if (panelSwitchHelper != null) {
panelSwitchHelper.hookSystemBackByPanelSwitcher();
}
}
return super.onTouchEvent(e);
}
}

View File

@@ -7,7 +7,9 @@ import com.netease.nim.uikit.api.model.contact.ContactEventListener;
import com.netease.nim.uikit.api.model.session.SessionCustomization;
import com.netease.nim.uikit.api.model.session.SessionEventListener;
import com.netease.nim.uikit.business.session.actions.BaseAction;
import com.netease.nim.uikit.business.session.actions.CameraAction;
import com.netease.nim.uikit.business.session.actions.ImageAction;
import com.netease.nim.uikit.business.session.actions.PhotoAction;
import com.netease.nimlib.sdk.msg.model.IMMessage;
import com.mango.moshen.community.holder.DynamicSysHolder;
import com.mango.moshen.community.im.WorldDynamicShareViewHolder;
@@ -116,10 +118,11 @@ public class ImInitHelper {
private void initP2PSessionCustomization() {
SessionCustomization sessionCustomization = new SessionCustomization();
ArrayList<BaseAction> actions = new ArrayList<>();
actions.add(new ImageAction());
actions.add(new PhotoAction());
actions.add(new CameraAction());
actions.add(new GiftAction());
sessionCustomization.actions = actions;
sessionCustomization.withSticker = true;
sessionCustomization.withSticker = false;
NimUIKit.setCommonP2PSessionCustomization(sessionCustomization);
// 语音派对

View File

@@ -0,0 +1,878 @@
package com.mango.moshen.ui.im;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.Handler;
import android.os.SystemClock;
import android.text.Editable;
import android.text.InputType;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.Chronometer;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.alibaba.fastjson.JSONObject;
import com.effective.android.panel.PanelSwitchHelper;
import com.effective.android.panel.interfaces.PanelHeightMeasurer;
import com.effective.android.panel.interfaces.listener.OnPanelChangeListener;
import com.effective.android.panel.view.panel.IPanelView;
import com.effective.android.panel.view.panel.PanelView;
import com.mango.moshen.R;
import com.mango.moshen.ui.im.actions.GiftAction;
import com.mango.xchat_android_library.utils.SingleToastUtil;
import com.mango.xchat_android_library.utils.SizeUtils;
import com.netease.nim.uikit.api.NimUIKit;
import com.netease.nim.uikit.api.UIKitOptions;
import com.netease.nim.uikit.api.model.session.SessionCustomization;
import com.netease.nim.uikit.business.ait.AitTextChangeListener;
import com.netease.nim.uikit.business.session.actions.BaseAction;
import com.netease.nim.uikit.business.session.actions.CameraAction;
import com.netease.nim.uikit.business.session.actions.PhotoAction;
import com.netease.nim.uikit.business.session.emoji.EmoticonPickerView;
import com.netease.nim.uikit.business.session.emoji.IEmoticonSelectedListener;
import com.netease.nim.uikit.business.session.emoji.MoonUtil;
import com.netease.nim.uikit.business.session.event.ActiveEvent;
import com.netease.nim.uikit.business.session.module.Container;
import com.netease.nim.uikit.business.session.module.input.ActionsPanel;
import com.netease.nim.uikit.business.session.module.input.NimAudioChatEvent;
import com.netease.nim.uikit.common.antispam.AntiSpamEvent;
import com.netease.nim.uikit.common.ui.dialog.EasyAlertDialogHelper;
import com.netease.nim.uikit.common.util.AntiSpamUtil;
import com.netease.nim.uikit.common.util.log.LogUtil;
import com.netease.nim.uikit.common.util.string.StringUtil;
import com.netease.nim.uikit.common.util.sys.ScreenUtil;
import com.netease.nim.uikit.impl.NimUIKitImpl;
import com.netease.nimlib.sdk.NIMClient;
import com.netease.nimlib.sdk.media.record.AudioRecorder;
import com.netease.nimlib.sdk.media.record.IAudioRecordCallback;
import com.netease.nimlib.sdk.media.record.RecordType;
import com.netease.nimlib.sdk.msg.MessageBuilder;
import com.netease.nimlib.sdk.msg.MsgService;
import com.netease.nimlib.sdk.msg.attachment.MsgAttachment;
import com.netease.nimlib.sdk.msg.constant.SessionTypeEnum;
import com.netease.nimlib.sdk.msg.model.CustomNotification;
import com.netease.nimlib.sdk.msg.model.CustomNotificationConfig;
import com.netease.nimlib.sdk.msg.model.IMMessage;
import org.greenrobot.eventbus.EventBus;
import java.io.File;
import java.util.List;
/**
* 底部文本编辑,语音等模块
* Created by hzxuwen on 2015/6/16.
*/
public class InputPanel implements IEmoticonSelectedListener, IAudioRecordCallback, AitTextChangeListener {
private static final String TAG = "MsgSendLayout";
private static final int SHOW_LAYOUT_DELAY = 0;
protected Container container;
protected View view;
protected Handler uiHandler;
protected EditText messageEditText;// 文本消息编辑框
protected Button audioRecordBtn; // 录音按钮
protected View audioAnimLayout; // 录音动画布局
protected FrameLayout textAudioSwitchLayout; // 切换文本,语音按钮布局
protected View switchToTextButtonInInputBar;// 文本消息选择按钮
protected View switchToAudioButtonInInputBar;// 语音消息选择按钮
protected View sendMessageButtonInInputBar;// 发送消息按钮
protected View emojiButtonInInputBar;// 发送消息按钮
protected View messageInputBar;
// 表情
protected EmoticonPickerView emoticonPickerView; // 贴图表情控件
// 语音
protected AudioRecorder audioMessageHelper;
private AutoHidePanelRecyclerView messageListView;
private SessionCustomization customization;
private Chronometer time;
private TextView timerTip;
private LinearLayout timerTipContainer;
private boolean started = false;
private boolean cancelled = false;
private boolean touched = false; // 是否按着
private boolean isKeyboardShowed = true; // 是否显示键盘
private boolean isTextAudioSwitchShow; // 是否展示左侧语音按钮
// adapter
private List<BaseAction> actions;
// data
private long typingTime = 0;
private TextWatcher aitTextWatcher;
private volatile boolean disable;
private boolean isChat;
private PanelSwitchHelper mHelper;
private final Runnable showTextRunnable = new Runnable() {
@Override
public void run() {
showInputMethod(messageEditText);
}
};
private ImageView ivImage;
private ImageView ivCamera;
private ImageView ivGift;
/**
* ************************* 键盘布局切换 *******************************
*/
private final View.OnClickListener clickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
if (v == switchToTextButtonInInputBar) {
switchToTextLayout(true);// 显示文本发送的布局
} else if (v == sendMessageButtonInInputBar) {
onTextMessageSendButtonPressed();
} else if (v == switchToAudioButtonInInputBar) {
switchToAudioLayout();
} else if (v == ivCamera) {
for (BaseAction action : actions) {
if (action instanceof CameraAction) {
action.onClick();
break;
}
}
mHelper.resetState();
} else if (v == ivGift) {
ivGift.postDelayed(() -> {
for (BaseAction action : actions) {
if (action instanceof GiftAction) {
action.onClick();
break;
}
}
}, !mHelper.isResetState() ? 300 : 0);
mHelper.resetState();
} else if (v == ivImage) {
for (BaseAction action : actions) {
if (action instanceof PhotoAction) {
action.onClick();
break;
}
}
mHelper.resetState();
}
}
};
public InputPanel(Container container, View view, List<BaseAction> actions, boolean isTextAudioSwitchShow) {
this.container = container;
this.view = view;
this.actions = actions;
this.uiHandler = new Handler();
this.isTextAudioSwitchShow = isTextAudioSwitchShow;
init();
}
public InputPanel(Container container, View view, List<BaseAction> actions) {
this(container, view, actions, true);
}
// 上滑取消录音判断
private static boolean isCancelled(View view, MotionEvent event) {
int[] location = new int[2];
view.getLocationOnScreen(location);
return event.getRawX() < location[0] || event.getRawX() > location[0] + view.getWidth()
|| event.getRawY() < location[1] - 40;
}
public void onPause() {
// 停止录音
if (audioMessageHelper != null) {
onEndAudioRecord(true);
}
}
public void onDestroy() {
// release
if (audioMessageHelper != null) {
audioMessageHelper.destroyAudioRecorder();
}
}
public boolean collapse(boolean immediately) {
boolean respond = !mHelper.isResetState();
mHelper.resetState();
return respond;
}
public void addAitTextWatcher(TextWatcher watcher) {
aitTextWatcher = watcher;
}
private void init() {
initViews();
initInputBarListener();
initTextEdit();
initAudioRecordButton();
restoreText(false);
for (int i = 0; i < actions.size(); ++i) {
actions.get(i).setIndex(i);
actions.get(i).setContainer(container);
}
if (disable) {
disableButtons();
}
if (mHelper == null) {
mHelper = new PanelSwitchHelper.Builder(container.activity)
//可选
.addKeyboardStateListener((visible, height) -> {
Log.d(TAG, "系统键盘是否可见 : " + visible + " 高度为:" + height);
})
//可选
.addEditTextFocusChangeListener((view, hasFocus) -> {
Log.d(TAG, "输入框是否获得焦点 : " + hasFocus);
})
//可选
.addViewClickListener(view -> {
if (view == null) return;
if (view.getId() == R.id.emoji_button) {
switchToTextLayout(false);
}
Log.d(TAG, "点击了View : " + view);
})
//可选
.addPanelChangeListener(new OnPanelChangeListener() {
@Override
public void onKeyboard() {
Log.d(TAG, "唤起系统输入法");
container.proxy.onInputPanelExpand();
}
@Override
public void onNone() {
Log.d(TAG, "隐藏所有面板");
}
@Override
public void onPanel(IPanelView view) {
Log.d(TAG, "唤起面板 : " + view);
container.proxy.onInputPanelExpand();
}
@Override
public void onPanelSizeChange(IPanelView panelView, boolean portrait, int oldWidth, int oldHeight, int width, int height) {
}
})
.addPanelHeightMeasurer(new PanelHeightMeasurer() {
@Override
public boolean synchronizeKeyboardHeight() {
return false;
}
@Override
public int getTargetPanelDefaultHeight() {
return ScreenUtil.dip2px(200);
}
@Override
public int getPanelTriggerId() {
return R.id.iv_image;
}
})
.addPanelHeightMeasurer(new PanelHeightMeasurer() {
@Override
public boolean synchronizeKeyboardHeight() {
return false;
}
@Override
public int getTargetPanelDefaultHeight() {
return ScreenUtil.dip2px(200);
}
@Override
public int getPanelTriggerId() {
return R.id.emoji_button;
}
})
.logTrack(true)//output log
.build();
messageListView.setPanelSwitchHelper(mHelper);
}
}
public void setCustomization(SessionCustomization customization) {
this.customization = customization;
if (customization != null) {
emoticonPickerView.setWithSticker(customization.withSticker);
}
}
public void reload(Container container, SessionCustomization customization) {
this.container = container;
setCustomization(customization);
}
private void initViews() {
messageInputBar = view.findViewById(R.id.textMessageLayout);
switchToTextButtonInInputBar = view.findViewById(R.id.buttonTextMessage);
switchToAudioButtonInInputBar = view.findViewById(R.id.buttonAudioMessage);
emojiButtonInInputBar = view.findViewById(R.id.emoji_button);
sendMessageButtonInInputBar = view.findViewById(R.id.buttonSendMessage);
messageEditText = view.findViewById(R.id.editTextMessage);
// 语音
audioRecordBtn = view.findViewById(R.id.audioRecord);
audioAnimLayout = view.findViewById(R.id.layoutPlayAudio);
time = view.findViewById(R.id.timer);
timerTip = view.findViewById(R.id.timer_tip);
timerTipContainer = view.findViewById(R.id.timer_tip_container);
// 表情
emoticonPickerView = view.findViewById(R.id.emoticon_picker_view);
emoticonPickerView.show(this);
// 显示录音按钮
switchToTextButtonInInputBar.setVisibility(View.GONE);
switchToAudioButtonInInputBar.setVisibility(View.VISIBLE);
// 文本录音按钮切换布局
textAudioSwitchLayout = view.findViewById(R.id.switchLayout);
if (isTextAudioSwitchShow) {
textAudioSwitchLayout.setVisibility(View.VISIBLE);
} else {
textAudioSwitchLayout.setVisibility(View.GONE);
}
messageListView = view.findViewById(R.id.messageListView);
ivImage = view.findViewById(R.id.iv_image);
ivCamera = view.findViewById(R.id.iv_camera);
ivGift = view.findViewById(R.id.iv_gift);
ivCamera.setOnClickListener(clickListener);
ivGift.setOnClickListener(clickListener);
}
public void disableButtons() {
if (messageEditText != null && audioRecordBtn != null &&
switchToTextButtonInInputBar != null &&
switchToAudioButtonInInputBar != null &&
emojiButtonInInputBar != null) {
messageEditText.setEnabled(false);
messageEditText.setHint("禁言中,快找管理员解除禁言吧!");
messageEditText.setHintTextColor(Color.parseColor("#999999"));
audioRecordBtn.setEnabled(false);
switchToTextButtonInInputBar.setEnabled(false);
switchToAudioButtonInInputBar.setEnabled(false);
emojiButtonInInputBar.setEnabled(false);
} else {
setDisable(true);
}
}
public void setDisable(boolean disable) {
this.disable = disable;
}
private void initInputBarListener() {
switchToTextButtonInInputBar.setOnClickListener(clickListener);
switchToAudioButtonInInputBar.setOnClickListener(clickListener);
sendMessageButtonInInputBar.setOnClickListener(clickListener);
}
private void initTextEdit() {
messageEditText.setInputType(InputType.TYPE_CLASS_TEXT);
messageEditText.setImeOptions(EditorInfo.IME_ACTION_SEND);
messageEditText.setOnEditorActionListener((v, actionId, event) -> {
//当actionId == XX_SEND 或者 XX_DONE时都触发
//或者event.getKeyCode == ENTER 且 event.getAction == ACTION_DOWN时也触发
//注意这是一定要判断event != null。因为在某些输入法上会返回null。
if (actionId == EditorInfo.IME_ACTION_SEND
|| actionId == EditorInfo.IME_ACTION_DONE
|| (event != null && KeyEvent.KEYCODE_ENTER == event.getKeyCode() && KeyEvent.ACTION_DOWN == event.getAction())) {
//处理事件
onTextMessageSendButtonPressed();
return true;
}
return false;
});
messageEditText.setOnFocusChangeListener((v, hasFocus) -> {
messageEditText.setHint("");
checkSendButtonEnable(messageEditText);
});
messageEditText.addTextChangedListener(new TextWatcher() {
private int start;
private int count;
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
this.start = start;
this.count = count;
if (aitTextWatcher != null) {
aitTextWatcher.onTextChanged(s, start, before, count);
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
if (aitTextWatcher != null) {
aitTextWatcher.beforeTextChanged(s, start, count, after);
}
}
@Override
public void afterTextChanged(Editable s) {
checkSendButtonEnable(messageEditText);
MoonUtil.replaceEmoticons(container.activity, s, start, count);
int editEnd = messageEditText.getSelectionEnd();
messageEditText.removeTextChangedListener(this);
while (StringUtil.counterChars(s.toString()) > NimUIKitImpl.getOptions().maxInputTextLength && editEnd > 0) {
s.delete(editEnd - 1, editEnd);
editEnd--;
}
messageEditText.setSelection(Math.min(s.length(), editEnd));
messageEditText.addTextChangedListener(this);
if (aitTextWatcher != null) {
aitTextWatcher.afterTextChanged(s);
}
sendTypingCommand();
}
});
// 不展示右侧按钮时需要添加edit间距
if (!isTextAudioSwitchShow) {
try {
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) messageEditText.getLayoutParams();
layoutParams.setMarginStart(SizeUtils.dp2px(view.getContext(), 12));
messageEditText.requestLayout();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
/**
* 发送“正在输入”通知
*/
private void sendTypingCommand() {
if (container.account.equals(NimUIKit.getAccount())) {
return;
}
if (container.sessionType == SessionTypeEnum.Team || container.sessionType == SessionTypeEnum.ChatRoom) {
return;
}
if (System.currentTimeMillis() - typingTime > 5000L) {
typingTime = System.currentTimeMillis();
CustomNotification command = new CustomNotification();
command.setSessionId(container.account);
command.setSessionType(container.sessionType);
CustomNotificationConfig config = new CustomNotificationConfig();
config.enablePush = false;
config.enableUnreadCount = false;
command.setConfig(config);
JSONObject json = new JSONObject();
json.put("id", "1");
command.setContent(json.toString());
NIMClient.getService(MsgService.class).sendCustomNotification(command);
}
}
// 点击edittext切换键盘和更多布局
private void switchToTextLayout(boolean needShowInput) {
audioRecordBtn.setVisibility(View.GONE);
messageEditText.setVisibility(View.VISIBLE);
switchToTextButtonInInputBar.setVisibility(View.GONE);
switchToAudioButtonInInputBar.setVisibility(View.VISIBLE);
messageInputBar.setVisibility(View.VISIBLE);
if (needShowInput) {
uiHandler.postDelayed(showTextRunnable, SHOW_LAYOUT_DELAY);
}
}
// 发送文本消息
private void onTextMessageSendButtonPressed() {
String text = messageEditText.getText().toString();
if (TextUtils.isEmpty(text.trim())) return;
EventBus.getDefault().post(new ActiveEvent());
IMMessage textMessage = createTextMessage(text);
if (AntiSpamUtil.checkLocalAntiSpam(textMessage)) {
EventBus.getDefault().post(new AntiSpamEvent());
} else {
if (container.proxy.sendMessage(textMessage)) {
restoreText(true);
}
}
}
protected IMMessage createTextMessage(String text) {
return MessageBuilder.createTextMessage(container.account, container.sessionType, text);
}
// 切换成音频,收起键盘,按钮切换成键盘
private void switchToAudioLayout() {
if (!isChat) {
SingleToastUtil.showToast("等级不够");
return;
}
Log.e(TAG, "switchToAudioLayout: ");
NimAudioChatEvent event = new NimAudioChatEvent();
event.setSuccess(o -> {
messageEditText.setVisibility(View.GONE);
audioRecordBtn.setVisibility(View.VISIBLE);
hideInputMethod();
switchToAudioButtonInInputBar.setVisibility(View.GONE);
switchToTextButtonInInputBar.setVisibility(View.VISIBLE);
mHelper.resetState();
});
Log.e(TAG, "switchToAudioLayout: post event");
EventBus.getDefault().post(event);
}
// 隐藏键盘布局
public void hideInputMethod() {
isKeyboardShowed = false;
uiHandler.removeCallbacks(showTextRunnable);
InputMethodManager imm = (InputMethodManager) container.activity.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(messageEditText.getWindowToken(), 0);
messageEditText.clearFocus();
}
// 显示键盘布局
private void showInputMethod(EditText editTextMessage) {
editTextMessage.requestFocus();
//如果已经显示,则继续操作时不需要把光标定位到最后
if (!isKeyboardShowed) {
editTextMessage.setSelection(editTextMessage.getText().length());
isKeyboardShowed = true;
}
mHelper.toKeyboardState();
container.proxy.onInputPanelExpand();
}
private void restoreText(boolean clearText) {
if (clearText) {
messageEditText.setText("");
}
checkSendButtonEnable(messageEditText);
}
/**
* 显示发送或更多
*
* @param editText
*/
private void checkSendButtonEnable(EditText editText) {
String textMessage = editText.getText().toString();
setEditTextState();
if (!TextUtils.isEmpty(StringUtil.removeBlanks(textMessage)) && editText.hasFocus()) {
sendMessageButtonInInputBar.setVisibility(View.VISIBLE);
} else {
sendMessageButtonInInputBar.setVisibility(View.GONE);
}
}
/**
* *************** IEmojiSelectedListener ***************
*/
@Override
public void onEmojiSelected(String key) {
Editable mEditable = messageEditText.getText();
if (key.equals("/DEL")) {
messageEditText.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
} else {
int start = messageEditText.getSelectionStart();
int end = messageEditText.getSelectionEnd();
start = (start < 0 ? 0 : start);
end = (start < 0 ? 0 : end);
mEditable.replace(start, end, key);
}
}
@Override
public void onStickerSelected(String category, String item) {
Log.i("InputPanel", "onStickerSelected, category =" + category + ", sticker =" + item);
if (customization != null) {
MsgAttachment attachment = customization.createStickerAttachment(category, item);
IMMessage stickerMessage = MessageBuilder.createCustomMessage(container.account, container.sessionType, "贴图消息", attachment);
container.proxy.sendMessage(stickerMessage);
}
}
@Override
public void onTextAdd(String content, int start, int length) {
if (messageEditText.getVisibility() != View.VISIBLE ||
(emoticonPickerView != null && emoticonPickerView.getVisibility() == View.VISIBLE)) {
switchToTextLayout(true);
} else {
uiHandler.postDelayed(showTextRunnable, SHOW_LAYOUT_DELAY);
}
messageEditText.getEditableText().insert(start, content);
}
@Override
public void onTextDelete(int start, int length) {
if (messageEditText.getVisibility() != View.VISIBLE) {
switchToTextLayout(true);
} else {
uiHandler.postDelayed(showTextRunnable, SHOW_LAYOUT_DELAY);
}
int end = start + length - 1;
messageEditText.getEditableText().replace(start, end, "");
}
public int getEditSelectionStart() {
return messageEditText.getSelectionStart();
}
/**
* ****************************** 语音 ***********************************
*/
@SuppressLint("ClickableViewAccessibility")
private void initAudioRecordButton() {
audioRecordBtn.setOnTouchListener((v, event) -> {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
touched = true;
initAudioRecord();
onStartAudioRecord();
} else if (event.getAction() == MotionEvent.ACTION_CANCEL
|| event.getAction() == MotionEvent.ACTION_UP) {
touched = false;
onEndAudioRecord(isCancelled(v, event));
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
touched = true;
cancelAudioRecord(isCancelled(v, event));
}
return false;
});
}
/**
* 初始化AudioRecord
*/
private void initAudioRecord() {
if (audioMessageHelper == null) {
UIKitOptions options = NimUIKitImpl.getOptions();
audioMessageHelper = new AudioRecorder(container.activity, options.audioRecordType, options.audioRecordMaxTime, this);
}
}
/**
* 开始语音录制
*/
private void onStartAudioRecord() {
container.activity.getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
audioMessageHelper.startRecord();
cancelled = false;
}
/**
* 结束语音录制
*
* @param cancel
*/
private void onEndAudioRecord(boolean cancel) {
started = false;
container.activity.getWindow().setFlags(0, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
audioMessageHelper.completeRecord(cancel);
audioRecordBtn.setText(R.string.record_audio);
audioRecordBtn.setBackgroundResource(R.drawable.bg_message_voice);
stopAudioRecordAnim();
if (!cancel) {
EventBus.getDefault().post(new ActiveEvent());
}
}
/**
* 取消语音录制
*
* @param cancel
*/
private void cancelAudioRecord(boolean cancel) {
// reject
if (!started) {
return;
}
// no change
if (cancelled == cancel) {
return;
}
cancelled = cancel;
updateTimerTip(cancel);
}
/**
* 正在进行语音录制和取消语音录制,界面展示
*
* @param cancel
*/
private void updateTimerTip(boolean cancel) {
if (cancel) {
timerTip.setText(R.string.recording_cancel_tip);
timerTipContainer.setBackgroundResource(R.drawable.nim_cancel_record_red_bg);
} else {
timerTip.setText(R.string.recording_cancel);
timerTipContainer.setBackgroundResource(0);
}
}
/**
* 开始语音录制动画
*/
private void playAudioRecordAnim() {
audioAnimLayout.setVisibility(View.VISIBLE);
time.setBase(SystemClock.elapsedRealtime());
time.start();
}
/**
* 结束语音录制动画
*/
private void stopAudioRecordAnim() {
audioAnimLayout.setVisibility(View.GONE);
time.stop();
time.setBase(SystemClock.elapsedRealtime());
}
// 录音状态回调
@Override
public void onRecordReady() {
}
@Override
public void onRecordStart(File audioFile, RecordType recordType) {
started = true;
if (!touched) {
return;
}
audioRecordBtn.setText(R.string.record_audio_end);
audioRecordBtn.setBackgroundResource(R.drawable.bg_message_voice_pressed);
updateTimerTip(false); // 初始化语音动画状态
playAudioRecordAnim();
}
@Override
public void onRecordSuccess(File audioFile, long audioLength, RecordType recordType) {
IMMessage audioMessage = MessageBuilder.createAudioMessage(container.account, container.sessionType, audioFile, audioLength);
container.proxy.sendMessage(audioMessage);
}
@Override
public void onRecordFail() {
if (started) {
SingleToastUtil.showToast(R.string.recording_error);
}
}
@Override
public void onRecordCancel() {
}
@Override
public void onRecordReachedMaxTime(final int maxTime) {
stopAudioRecordAnim();
EasyAlertDialogHelper.createOkCancelDiolag(container.activity, "", container.activity.getString(R.string.recording_max_time), false, new EasyAlertDialogHelper.OnDialogActionListener() {
@Override
public void doCancelAction() {
}
@Override
public void doOkAction() {
audioMessageHelper.handleEndRecord(true, maxTime);
}
}).show();
}
public boolean isRecording() {
return audioMessageHelper != null && audioMessageHelper.isRecording();
}
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != Activity.RESULT_OK) {
return;
}
int index = (requestCode << 16) >> 24;
if (index != 0) {
index--;
if (index < 0 | index >= actions.size()) {
LogUtil.d(TAG, "request code out of actions' range");
return;
}
BaseAction action = actions.get(index);
if (action != null) {
action.onActivityResult(requestCode & 0xff, resultCode, data);
}
}
}
public void reloadActions(List<BaseAction> actions) {
this.actions = actions;
for (int i = 0; i < this.actions.size(); ++i) {
this.actions.get(i).setIndex(i);
this.actions.get(i).setContainer(container);
}
ActionsPanel.init(view, actions);
}
public void setLimitLevel(boolean isChat, String msg) {
this.isChat = isChat;
setEditTextState();
}
private void setEditTextState() {
if (!isChat) {
messageEditText.setHint("暂未达到可发起私聊等级");
messageEditText.setFocusable(false);
messageEditText.setFocusableInTouchMode(false);
messageEditText.setEnabled(false);
sendMessageButtonInInputBar.setEnabled(false);
} else {
messageEditText.setHint("请输入消息");
messageEditText.setFocusable(true);
messageEditText.setFocusableInTouchMode(true);
messageEditText.setEnabled(true);
sendMessageButtonInInputBar.setEnabled(true);
}
}
}

View File

@@ -17,6 +17,8 @@ import androidx.recyclerview.widget.RecyclerView;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.mango.core.room.event.MessageSizeEvent;
import com.mango.xchat_android_library.utils.SingleToastUtil;
import com.netease.nim.uikit.R;
import com.netease.nim.uikit.api.NimUIKit;
import com.netease.nim.uikit.api.model.user.UserInfoObserver;
@@ -65,8 +67,6 @@ import com.netease.nimlib.sdk.robot.model.RobotAttachment;
import com.netease.nimlib.sdk.robot.model.RobotMsgType;
import com.netease.nimlib.sdk.team.constant.TeamMemberType;
import com.netease.nimlib.sdk.team.model.TeamMember;
import com.mango.core.room.event.MessageSizeEvent;
import com.mango.xchat_android_library.utils.SingleToastUtil;
import org.greenrobot.eventbus.EventBus;
@@ -87,7 +87,7 @@ public class MessageListPanelEx {
private static final int REQUEST_CODE_FORWARD_TEAM = 0x02;
// 背景图片缓存
private static Pair<String, Bitmap> background;
private static Comparator<IMMessage> comp = new Comparator<IMMessage>() {
private static final Comparator<IMMessage> comp = new Comparator<IMMessage>() {
@Override
public int compare(IMMessage o1, IMMessage o2) {
@@ -97,7 +97,7 @@ public class MessageListPanelEx {
};
// container
private Container container;
private View rootView;
private final View rootView;
// message list view
private RecyclerView messageListView;
private List<IMMessage> items;
@@ -107,15 +107,15 @@ public class MessageListPanelEx {
private IncomingMsgPrompt incomingMsgPrompt;
private Handler uiHandler;
// 仅显示消息记录,不接收和发送消息
private boolean recordOnly;
private final boolean recordOnly;
// 从服务器拉取消息记录
private boolean remote;
private final boolean remote;
// 语音转文字
private VoiceTrans voiceTrans;
// 待转发消息
private IMMessage forwardMessage;
private CountDownTimer countDownTimer;
private OnItemClickListener listener = new OnItemClickListener() {
private final OnItemClickListener listener = new OnItemClickListener() {
@Override
public void onItemClick(IRecyclerView adapter, View view, int position) {
@@ -142,7 +142,7 @@ public class MessageListPanelEx {
container.activity.startActivity(intent);
} catch (ActivityNotFoundException e) {
// Toast.makeText(container.activity, "路径错误", Toast.LENGTH_SHORT).show();
SingleToastUtil.showToastShort("路径错误");
SingleToastUtil.showToast("路径错误");
}
} else if (LinkElement.TYPE_BLOCK.equals(element.getType())) {
@@ -162,7 +162,7 @@ public class MessageListPanelEx {
/**
* 消息状态变化观察者
*/
private Observer<IMMessage> messageStatusObserver = new Observer<IMMessage>() {
private final Observer<IMMessage> messageStatusObserver = new Observer<IMMessage>() {
@Override
public void onEvent(IMMessage message) {
if (isMyMessage(message)) {
@@ -173,7 +173,7 @@ public class MessageListPanelEx {
/**
* 消息附件上传/下载进度观察者
*/
private Observer<AttachmentProgress> attachmentProgressObserver = new Observer<AttachmentProgress>() {
private final Observer<AttachmentProgress> attachmentProgressObserver = new Observer<AttachmentProgress>() {
@Override
public void onEvent(AttachmentProgress progress) {
onAttachmentProgressChange(progress);
@@ -182,7 +182,7 @@ public class MessageListPanelEx {
/**
* 本地消息接收观察者
*/
private MessageListPanelHelper.LocalMessageObserver incomingLocalMessageObserver = new MessageListPanelHelper.LocalMessageObserver() {
private final MessageListPanelHelper.LocalMessageObserver incomingLocalMessageObserver = new MessageListPanelHelper.LocalMessageObserver() {
@Override
public void onAddMessage(IMMessage message) {
if (message == null || !container.account.equals(message.getSessionId())) {
@@ -202,7 +202,7 @@ public class MessageListPanelEx {
/**
* 消息撤回观察者
*/
private Observer<RevokeMsgNotification> revokeMessageObserver = new Observer<RevokeMsgNotification>() {
private final Observer<RevokeMsgNotification> revokeMessageObserver = new Observer<RevokeMsgNotification>() {
@Override
public void onEvent(RevokeMsgNotification notification) {
if (notification == null || notification.getMessage() == null) {
@@ -282,10 +282,10 @@ public class MessageListPanelEx {
}
private void initListView(IMMessage anchor) {
listviewBk = (ImageView) rootView.findViewById(R.id.message_activity_background);
listviewBk = rootView.findViewById(R.id.message_activity_background);
// RecyclerView
messageListView = (RecyclerView) rootView.findViewById(R.id.messageListView);
messageListView = rootView.findViewById(R.id.messageListView);
messageListView.setLayoutManager(new LinearLayoutManager(container.activity));
messageListView.requestDisallowInterceptTouchEvent(true);
messageListView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@@ -339,12 +339,7 @@ public class MessageListPanelEx {
}
public void scrollToBottom() {
uiHandler.postDelayed(new Runnable() {
@Override
public void run() {
doScrollToBottom();
}
}, 200);
doScrollToBottom();
}
private void doScrollToBottom() {
@@ -603,15 +598,11 @@ public class MessageListPanelEx {
}
private boolean receiveReceiptCheck(final IMMessage msg) {
if (msg != null && msg.getSessionType() == SessionTypeEnum.P2P
return msg != null && msg.getSessionType() == SessionTypeEnum.P2P
&& msg.getDirect() == MsgDirectionEnum.Out
&& msg.getMsgType() != MsgTypeEnum.tip
&& msg.getMsgType() != MsgTypeEnum.notification
&& msg.isRemoteRead()) {
return true;
}
return false;
&& msg.isRemoteRead();
}
/**
@@ -649,12 +640,8 @@ public class MessageListPanelEx {
}
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;
return msg != null && msg.getDirect() == MsgDirectionEnum.In &&
msg.getMsgType() != MsgTypeEnum.tip && msg.getMsgType() != MsgTypeEnum.notification; // 非收到的消息Tip消息和通知类消息不要发已读回执
}
// 删除消息
@@ -699,7 +686,7 @@ public class MessageListPanelEx {
if (message == null) {
// Toast.makeText(container.activity, "该类型不支持转发", Toast.LENGTH_SHORT).show();
SingleToastUtil.showToastShort("该类型不支持转发");
SingleToastUtil.showToast("该类型不支持转发");
return;
}
@@ -742,15 +729,15 @@ public class MessageListPanelEx {
private class MessageLoader implements BaseFetchLoadAdapter.RequestLoadMoreListener, BaseFetchLoadAdapter.RequestFetchMoreListener {
private int loadMsgCount = NimUIKitImpl.getOptions().messageCountLoadOnce;
private final int loadMsgCount = NimUIKitImpl.getOptions().messageCountLoadOnce;
private QueryDirectionEnum direction = null;
private IMMessage anchor;
private boolean remote;
private final IMMessage anchor;
private final boolean remote;
private boolean firstLoad = true;
private RequestCallback<List<IMMessage>> callback = new RequestCallbackWrapper<List<IMMessage>>() {
private final 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) {
@@ -1209,8 +1196,7 @@ public class MessageListPanelEx {
@Override
public void onClick() {
// Toast.makeText(container.activity, finalContent, Toast.LENGTH_SHORT).show();
SingleToastUtil.showToastShort(finalContent);
SingleToastUtil.showToast(finalContent);
setEarPhoneMode(!UserPreferences.isEarPhoneModeEnable(), true);
}
});
@@ -1257,8 +1243,7 @@ public class MessageListPanelEx {
@Override
public void onClick() {
if (!NetworkUtil.isNetAvailable(container.activity)) {
// Toast.makeText(container.activity, R.string.network_is_not_available, Toast.LENGTH_SHORT).show();
SingleToastUtil.showToastShort(R.string.network_is_not_available);
SingleToastUtil.showToast(R.string.network_is_not_available);
return;
}
NIMClient.getService(MsgService.class).revokeMessage(item).setCallback(new RequestCallback<Void>() {
@@ -1271,11 +1256,9 @@ public class MessageListPanelEx {
@Override
public void onFailed(int code) {
if (code == ResponseCode.RES_OVERDUE) {
// Toast.makeText(container.activity, R.string.revoke_failed, Toast.LENGTH_SHORT).show();
SingleToastUtil.showToastShort(R.string.revoke_failed);
SingleToastUtil.showToast(R.string.revoke_failed);
} else {
// Toast.makeText(container.activity, "revoke msg failed, code:" + code, Toast.LENGTH_SHORT).show();
SingleToastUtil.showToastShort("revoke msg failed, code:" + code);
SingleToastUtil.showToast("revoke msg failed, code:" + code);
}
}

View File

@@ -17,6 +17,31 @@ import androidx.annotation.Nullable;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.mango.core.audio.event.VoiceShakeHeartEvent;
import com.mango.core.auth.AuthModel;
import com.mango.core.im.friend.IMFriendModel;
import com.mango.core.mentoring_relationship.event.MentoringSuccessEvent;
import com.mango.core.noble.NobleUtil;
import com.mango.core.praise.PraiseModel;
import com.mango.core.praise.event.IsLikedEvent;
import com.mango.core.praise.event.PraiseEvent;
import com.mango.core.user.UserModel;
import com.mango.core.user.bean.UserDetailInfo;
import com.mango.core.user.bean.UserInfo;
import com.mango.core.utils.StringExtensionKt;
import com.mango.core.utils.SystemUidUtil;
import com.mango.core.utils.net.BeanObserver;
import com.mango.moshen.R;
import com.mango.moshen.avroom.activity.AVRoomActivity;
import com.mango.moshen.common.widget.CustomImageSpan;
import com.mango.moshen.mentoring_relationship.dialog.BuildMentoringRelationshipSuccessDialog;
import com.mango.moshen.ui.im.audio.ShakeHeartDialogFragment;
import com.mango.moshen.ui.im.fragment.MessageFragment;
import com.mango.moshen.ui.user.UserInfoActivity;
import com.mango.moshen.ui.utils.ImageLoadUtils;
import com.mango.xchat_android_library.utils.ListUtils;
import com.mango.xchat_android_library.utils.SingleToastUtil;
import com.netease.nim.uikit.StatusBarUtil;
import com.netease.nim.uikit.api.NimUIKit;
import com.netease.nim.uikit.api.model.contact.ContactChangedObserver;
import com.netease.nim.uikit.api.model.main.OnlineStateChangeObserver;
@@ -31,33 +56,6 @@ import com.netease.nimlib.sdk.Observer;
import com.netease.nimlib.sdk.msg.MsgServiceObserve;
import com.netease.nimlib.sdk.msg.constant.SessionTypeEnum;
import com.netease.nimlib.sdk.msg.model.CustomNotification;
import com.mango.moshen.R;
import com.mango.moshen.avroom.activity.AVRoomActivity;
import com.mango.moshen.common.widget.CustomImageSpan;
import com.mango.moshen.ui.im.audio.ShakeHeartDialogFragment;
import com.mango.moshen.ui.im.fragment.MessageFragment;
import com.mango.moshen.ui.user.UserInfoActivity;
import com.mango.moshen.ui.utils.ImageLoadUtils;
import com.mango.moshen.mentoring_relationship.dialog.BuildMentoringRelationshipSuccessDialog;
import com.mango.core.audio.event.VoiceShakeHeartEvent;
import com.mango.core.auth.AuthModel;
import com.mango.core.im.friend.IMFriendModel;
import com.mango.core.initial.InitialModel;
import com.mango.core.initial.bean.InitInfo;
import com.mango.core.level.UserLevelVo;
import com.mango.core.mentoring_relationship.event.MentoringSuccessEvent;
import com.mango.core.noble.NobleUtil;
import com.mango.core.praise.PraiseModel;
import com.mango.core.praise.event.IsLikedEvent;
import com.mango.core.praise.event.PraiseEvent;
import com.mango.core.user.UserModel;
import com.mango.core.user.bean.UserDetailInfo;
import com.mango.core.user.bean.UserInfo;
import com.mango.core.utils.StringExtensionKt;
import com.mango.core.utils.SystemUidUtil;
import com.mango.core.utils.net.BeanObserver;
import com.mango.xchat_android_library.utils.ListUtils;
import com.mango.xchat_android_library.utils.SingleToastUtil;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
@@ -414,21 +412,6 @@ public class NimP2PMessageActivity extends NewBaseMessageActivity {
MessageFragment fragment = new MessageFragment();
fragment.setArguments(arguments);
fragment.setContainerId(R.id.message_fragment_container);
// 等级限制:官方小秘书 和 系统消息,不设置等级限制
if (!SystemUidUtil.isSystemUid(sessionId)) {
UserInfo userInfo = UserModel.get().getCacheLoginUserInfo();
if (userInfo != null) {
UserLevelVo userLevelVo = userInfo.getUserLevelVo();
if (userLevelVo != null) {
fragment.setCurrentLevel(userLevelVo.experLevelSeq);
}
}
InitInfo initInfo = InitialModel.get().getCacheInitInfo();
if (initInfo != null) {
fragment.setLimitLevel(initInfo.getPrivateChatLevelNo());
}
}
return fragment;
}
@@ -486,4 +469,16 @@ public class NimP2PMessageActivity extends NewBaseMessageActivity {
ShakeHeartDialogFragment shakeHeartDialogFragment = ShakeHeartDialogFragment.newInstance(event.showTextHint);
shakeHeartDialogFragment.show(getSupportFragmentManager(), "shake_heart");
}
@Override
protected boolean needSteepStateBar() {
return true;
}
@Override
protected void setStatusBar() {
super.setStatusBar();
StatusBarUtil.transparencyBar(this);
StatusBarUtil.StatusBarLightMode(this);
}
}

View File

@@ -20,6 +20,24 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import com.mango.core.UriProvider;
import com.mango.core.auth.AuthModel;
import com.mango.core.im.chatterbox.ChatterBoxHelper;
import com.mango.core.im.chatterbox.HideInputEvent;
import com.mango.core.room.event.MessageSizeEvent;
import com.mango.core.statistic.StatisticManager;
import com.mango.core.statistic.protocol.StatisticsProtocol;
import com.mango.moshen.R;
import com.mango.moshen.common.widget.OriginalDrawStatusClickSpan;
import com.mango.moshen.ui.im.GreetPresenter;
import com.mango.moshen.ui.im.InputPanel;
import com.mango.moshen.ui.im.MessageListPanelEx;
import com.mango.moshen.ui.im.chat.MVHChatterBoxStart;
import com.mango.moshen.ui.im.model.IMCustomModel;
import com.mango.moshen.ui.webview.CommonWebViewActivity;
import com.mango.moshen.utils.PushMessageHandler;
import com.mango.xchat_android_library.utils.SingleToastUtil;
import com.mango.xchat_android_library.utils.config.BasicConfig;
import com.netease.nim.uikit.api.UIKitOptions;
import com.netease.nim.uikit.api.model.main.CustomPushContentProvider;
import com.netease.nim.uikit.api.model.session.SessionCustomization;
@@ -28,7 +46,6 @@ import com.netease.nim.uikit.business.session.actions.BaseAction;
import com.netease.nim.uikit.business.session.constant.Extras;
import com.netease.nim.uikit.business.session.module.Container;
import com.netease.nim.uikit.business.session.module.ModuleProxy;
import com.netease.nim.uikit.business.session.module.input.InputPanel;
import com.netease.nim.uikit.business.session.module.input.NimAudioChatEvent;
import com.netease.nim.uikit.business.session.module.input.NimImageActionEvent;
import com.netease.nim.uikit.common.fragment.TFragment;
@@ -51,25 +68,6 @@ import com.netease.nimlib.sdk.robot.model.NimRobotInfo;
import com.netease.nimlib.sdk.robot.model.RobotAttachment;
import com.netease.nimlib.sdk.robot.model.RobotMsgType;
import com.tbruyelle.rxpermissions2.RxPermissions;
import com.mango.moshen.R;
import com.mango.moshen.common.widget.OriginalDrawStatusClickSpan;
import com.mango.moshen.ui.im.GreetPresenter;
import com.mango.moshen.ui.im.MessageListPanelEx;
import com.mango.moshen.ui.im.chat.MVHChatterBoxStart;
import com.mango.moshen.ui.im.model.IMCustomModel;
import com.mango.moshen.ui.webview.CommonWebViewActivity;
import com.mango.moshen.utils.PushMessageHandler;
import com.mango.core.UriProvider;
import com.mango.core.auth.AuthModel;
import com.mango.core.im.chatterbox.ChatterBoxHelper;
import com.mango.core.im.chatterbox.HideInputEvent;
import com.mango.core.im.custom.bean.CustomAttachment;
import com.mango.core.im.custom.bean.ImTipAttachment;
import com.mango.core.room.event.MessageSizeEvent;
import com.mango.core.statistic.StatisticManager;
import com.mango.core.statistic.protocol.StatisticsProtocol;
import com.mango.xchat_android_library.utils.SingleToastUtil;
import com.mango.xchat_android_library.utils.config.BasicConfig;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
@@ -103,6 +101,7 @@ public class MessageFragment extends TFragment implements ModuleProxy, MessageLi
protected InputPanel inputPanel;
protected MessageListPanelEx messageListPanel;
protected AitManager aitManager;
private boolean inRoom;
/**
* 消息接收观察者
*/
@@ -190,27 +189,12 @@ public class MessageFragment extends TFragment implements ModuleProxy, MessageLi
}
}
/**
* 发送提示信息
*
* @param msg
*/
public void sendTipMessage(String msg, int second) {
ImTipAttachment attachment = new ImTipAttachment(CustomAttachment.CUSTOM_MSG_IM_TIP, second);
attachment.setMsg(msg);
IMMessage message = MessageBuilder.createCustomMessage(sessionId, SessionTypeEnum.P2P, attachment);
sendMessage(message);
}
public boolean onBackPressed() {
if (inputPanel.collapse(true)) {
return true;
}
if (messageListPanel.onBackPressed()) {
return true;
}
return false;
return messageListPanel.onBackPressed();
}
public void refreshMessageList() {
@@ -243,8 +227,6 @@ public class MessageFragment extends TFragment implements ModuleProxy, MessageLi
initAitManager();
inputPanel.switchRobotMode(NimUIKitImpl.getRobotInfoProvider().getRobotByAccount(sessionId) != null);
registerObservers(true);
if (customization != null) {
@@ -302,7 +284,7 @@ public class MessageFragment extends TFragment implements ModuleProxy, MessageLi
*/
// 是否允许发送消息
protected boolean isAllowSendMessage(final IMMessage message) {
return true;
return isChat;
}
/**
@@ -324,7 +306,9 @@ public class MessageFragment extends TFragment implements ModuleProxy, MessageLi
*/
@Override
public boolean sendMessage(IMMessage message) {
if (!isAllowSendMessage(message)) {
SingleToastUtil.showToast("未到达私聊等级!");
return false;
}
@@ -346,8 +330,6 @@ public class MessageFragment extends TFragment implements ModuleProxy, MessageLi
final IMMessage msg = message;
appendPushConfig(message);
// send message to server and save to db
IMMessage finalMessage = message;
NIMClient.getService(MsgService.class).sendMessage(message, false).setCallback(new RequestCallback<Void>() {
@Override
public void onSuccess(Void param) {
@@ -542,10 +524,6 @@ public class MessageFragment extends TFragment implements ModuleProxy, MessageLi
// 操作面板集合
protected List<BaseAction> getActionList() {
List<BaseAction> actions = new ArrayList<>();
// actions.add(new ImageAction());
// actions.add(new VideoAction());
// actions.add(new LocationAction());
if (customization != null && customization.actions != null) {
actions.addAll(customization.actions);
}
@@ -566,18 +544,6 @@ public class MessageFragment extends TFragment implements ModuleProxy, MessageLi
messageListPanel.receiveReceipt();
}
/**
* 重新加载 输入面板
*
* @param sessionCustomization
*/
public void reloadInputPanel(SessionCustomization sessionCustomization) {
this.customization = sessionCustomization;
Container container = new Container(getActivity(), sessionId, sessionType, this);
inputPanel.reload(container, customization);
inputPanel.reloadActions(sessionCustomization.actions);
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onHideInput(HideInputEvent event) {
if (inputPanel != null) {
@@ -592,51 +558,54 @@ public class MessageFragment extends TFragment implements ModuleProxy, MessageLi
if (inputPanel != null) {
if (event.getSize() > 0) {
tvChatLimit.setVisibility(View.GONE);
isChat = true;
inputPanel.setLimitLevel(true, "");
} else {
IMCustomModel.get().getPrivateChatLimit(sessionId).subscribe(((privateChatLimitInfo, throwable) -> {
if (throwable != null) {
throwable.printStackTrace();
} else {
isChat = privateChatLimitInfo.isChat();
hintText = "暂未达到可发起私聊等级";
inputPanel.setLimitLevel(privateChatLimitInfo.isChat(), privateChatLimitInfo.getMessage());
IMCustomModel.get()
.getPrivateChatLimit(sessionId)
.subscribe(((privateChatLimitInfo, throwable) -> {
if (throwable != null) {
throwable.printStackTrace();
} else {
isChat = privateChatLimitInfo.isChat();
hintText = "暂未达到可发起私聊等级";
inputPanel.setLimitLevel(privateChatLimitInfo.isChat(), privateChatLimitInfo.getMessage());
String experLevel = "财富等级≥" + privateChatLimitInfo.getWealthLevel();
String charmLevel = "魅力等级≥" + privateChatLimitInfo.getCharmLevel();
String privacyAgreementDescTip = getContext().getString(R.string.text_chat_limit, experLevel, charmLevel);
SpannableString ss = new SpannableString(privacyAgreementDescTip);
int experLevelIndex = privacyAgreementDescTip.indexOf(experLevel);
int charmLevelIndex = privacyAgreementDescTip.indexOf(charmLevel);
String experLevel = "财富等级≥" + privateChatLimitInfo.getWealthLevel();
String charmLevel = "魅力等级≥" + privateChatLimitInfo.getCharmLevel();
String privacyAgreementDescTip = getContext().getString(R.string.text_chat_limit, experLevel, charmLevel);
SpannableString ss = new SpannableString(privacyAgreementDescTip);
int experLevelIndex = privacyAgreementDescTip.indexOf(experLevel);
int charmLevelIndex = privacyAgreementDescTip.indexOf(charmLevel);
ss.setSpan(new ForegroundColorSpan(ContextCompat.getColor(getContext(), R.color.appColor)), experLevelIndex, experLevelIndex + experLevel.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
ss.setSpan(new OriginalDrawStatusClickSpan(ContextCompat.getColor(getContext(), R.color.appColor)) {
@Override
public void onClick(@NonNull View widget) {
if (widget instanceof TextView)
((TextView) widget).setHighlightColor(getResources().getColor(android.R.color.transparent));
ss.setSpan(new ForegroundColorSpan(ContextCompat.getColor(getContext(), R.color.appColor)), experLevelIndex, experLevelIndex + experLevel.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
ss.setSpan(new OriginalDrawStatusClickSpan(ContextCompat.getColor(getContext(), R.color.appColor)) {
@Override
public void onClick(@NonNull View widget) {
if (widget instanceof TextView)
((TextView) widget).setHighlightColor(getResources().getColor(android.R.color.transparent));
CommonWebViewActivity.start(getContext(), UriProvider.getUserLevelUrl());
CommonWebViewActivity.start(getContext(), UriProvider.getUserLevelUrl());
}
}, experLevelIndex, experLevelIndex + experLevel.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
ss.setSpan(new ForegroundColorSpan(ContextCompat.getColor(getContext(), R.color.appColor)), charmLevelIndex, charmLevelIndex + charmLevel.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
ss.setSpan(new OriginalDrawStatusClickSpan(ContextCompat.getColor(getContext(), R.color.appColor)) {
@Override
public void onClick(@NonNull View widget) {
if (widget instanceof TextView)
((TextView) widget).setHighlightColor(getResources().getColor(android.R.color.transparent));
CommonWebViewActivity.start(getContext(), UriProvider.getUserLevelUrl());
}
}, charmLevelIndex, charmLevelIndex + experLevel.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
tvChatLimit.setText(ss);
tvChatLimit.setHighlightColor(Color.TRANSPARENT);
tvChatLimit.setMovementMethod(new LinkMovementMethod());
tvChatLimit.setVisibility(isChat ? View.GONE : View.VISIBLE);
}
}, experLevelIndex, experLevelIndex + experLevel.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
ss.setSpan(new ForegroundColorSpan(ContextCompat.getColor(getContext(), R.color.appColor)), charmLevelIndex, charmLevelIndex + charmLevel.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
ss.setSpan(new OriginalDrawStatusClickSpan(ContextCompat.getColor(getContext(), R.color.appColor)) {
@Override
public void onClick(@NonNull View widget) {
if (widget instanceof TextView)
((TextView) widget).setHighlightColor(getResources().getColor(android.R.color.transparent));
CommonWebViewActivity.start(getContext(), UriProvider.getUserLevelUrl());
}
}, charmLevelIndex, charmLevelIndex + experLevel.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
tvChatLimit.setText(ss);
tvChatLimit.setHighlightColor(Color.TRANSPARENT);
tvChatLimit.setMovementMethod(new LinkMovementMethod());
tvChatLimit.setVisibility(isChat ? View.GONE : View.VISIBLE);
}
}));
}));
}
}
}
@@ -672,12 +641,4 @@ public class MessageFragment extends TFragment implements ModuleProxy, MessageLi
});
}
public void setLimitLevel(int limitLevel) {
}
//
//
public void setCurrentLevel(int currentLevel) {
}
}

View File

@@ -4,7 +4,8 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_normal_1c1b22">
android:background="@color/bg_normal_1c1b22"
android:paddingTop="30dp">
<LinearLayout
android:layout_width="match_parent"

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<com.netease.nim.uikit.business.session.emoji.EmoticonPickerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/emoticon_picker_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="center">
<TextView
android:id="@+id/nim_message_item_text_body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:autoLink="phone|email|web"
android:gravity="center_vertical|left"
android:includeFontPadding="false"
android:lineSpacingExtra="3dip"
android:maxWidth="222dp"
android:text="这是选择图片的玩意"
android:textColor="@color/text_title_white"
android:textSize="@dimen/dp_13" />
</LinearLayout>

View File

@@ -1,58 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<com.effective.android.panel.view.PanelSwitchLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/messageActivityLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_normal_1c1b22"
android:orientation="vertical">
android:orientation="vertical"
app:animationSpeed="standard">
<RelativeLayout
<com.effective.android.panel.view.content.LinearContentContainer
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1">
<FrameLayout
android:id="@+id/fl_game"
android:layout_width="299dp"
android:layout_height="163dp"
android:layout_centerHorizontal="true"
android:layout_marginBottom="-10dp"
android:background="@drawable/chat_popup_game_bg"
android:visibility="gone">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="25dp"
android:text="Ta爱玩的游戏"
android:textColor="#ff666666"
android:textSize="18sp" />
<ImageView
android:id="@+id/iv_close"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="right"
android:layout_marginRight="10dp"
android:scaleType="center"
android:src="@drawable/chat_popup_game_close" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="55dp" />
</FrameLayout>
android:layout_height="match_parent"
android:orientation="vertical"
app:edit_view="@id/editTextMessage">
<RelativeLayout
android:id="@+id/message_activity_list_view_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/fl_game"
android:layout_height="0dp"
android:layout_weight="1">
<com.netease.nim.uikit.business.session.helper.MsgBkImageView
@@ -72,7 +38,7 @@
</FrameLayout>
<androidx.recyclerview.widget.RecyclerView
<com.mango.moshen.ui.im.AutoHidePanelRecyclerView
android:id="@+id/messageListView"
style="@style/recycler_view"
android:layout_width="match_parent"
@@ -120,18 +86,173 @@
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="@dimen/dp_10"
android:text="哈哈哈哈哈"
android:paddingStart="@dimen/dp_10"
android:paddingEnd="@dimen/dp_10"
android:paddingTop="@dimen/dp_2"
android:paddingBottom="@dimen/dp_2"
android:background="@drawable/bg_chat_limit"
android:visibility="gone"
/>
android:paddingStart="@dimen/dp_10"
android:paddingTop="@dimen/dp_2"
android:paddingEnd="@dimen/dp_10"
android:paddingBottom="@dimen/dp_2"
android:text="哈哈哈哈哈"
android:visibility="gone" />
</RelativeLayout>
</RelativeLayout>
<LinearLayout
android:id="@+id/textMessageLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:gravity="center_vertical"
android:orientation="vertical"
android:paddingTop="@dimen/bottom_component_margin_vertical"
android:paddingBottom="@dimen/bottom_component_margin_vertical">
<include layout="@layout/nim_message_activity_bottom_layout" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="30dp"
android:gravity="center_vertical"
android:orientation="horizontal">
</LinearLayout>
<FrameLayout
android:id="@+id/switchLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/bottom_component_margin_horizontal"
android:layout_marginRight="@dimen/bottom_component_margin_horizontal">
<ImageView
android:id="@+id/buttonAudioMessage"
android:layout_width="30dp"
android:layout_height="30dp"
android:background="@drawable/nim_message_button_bottom_audio_selector"
android:contentDescription="@string/empty"
android:scaleType="center" />
<ImageView
android:id="@+id/buttonTextMessage"
android:layout_width="30dp"
android:layout_height="30dp"
android:background="@drawable/nim_message_button_bottom_text_selector"
android:contentDescription="@string/empty"
android:scaleType="center"
android:visibility="gone" />
</FrameLayout>
<Button
android:id="@+id/audioRecord"
android:layout_width="0dp"
android:layout_height="30dp"
android:layout_gravity="center"
android:layout_marginEnd="@dimen/bottom_component_margin_horizontal"
android:layout_weight="1"
android:background="@drawable/bg_message_voice"
android:text="@string/record_audio"
android:textColor="@color/white"
android:textSize="16sp"
android:visibility="gone" />
<EditText
android:id="@+id/editTextMessage"
android:layout_width="0dp"
android:layout_height="30dp"
android:layout_marginEnd="@dimen/bottom_component_margin_horizontal"
android:layout_weight="1"
android:autoLink="web|email|phone"
android:background="@drawable/bg_message_input"
android:hint="@string/message_hint"
android:imeOptions="actionSend"
android:inputType="text"
android:maxLength="120"
android:maxLines="4"
android:paddingStart="@dimen/dp_10"
android:paddingEnd="10dp"
android:textColor="@color/color_C6C6E9"
android:textColorHint="@color/text_hint_555574"
android:textCursorDrawable="@null"
android:textSize="13sp" />
<ImageView
android:id="@+id/emoji_button"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginEnd="@dimen/bottom_component_margin_horizontal"
android:background="@drawable/nim_message_button_bottom_emoji_selector"
android:contentDescription="@string/empty"
android:scaleType="center" />
<TextView
android:id="@+id/buttonSendMessage"
android:layout_width="56dp"
android:layout_height="30dp"
android:layout_marginEnd="6dp"
android:background="@drawable/common_btn_bg"
android:contentDescription="@string/empty"
android:gravity="center"
android:text="@string/send"
android:textColor="@color/white"
android:textSize="@dimen/sp_12"
android:visibility="gone"
tools:visibility="visible" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:gravity="center"
android:orientation="horizontal">
<ImageView
android:id="@+id/iv_image"
android:layout_width="32dp"
android:layout_height="32dp"
android:src="@drawable/message_input_ic_image" />
<ImageView
android:id="@+id/iv_camera"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginStart="88dp"
android:src="@drawable/message_input_ic_camera" />
<ImageView
android:id="@+id/iv_gift"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginStart="88dp"
android:src="@drawable/message_input_ic_gift" />
</LinearLayout>
</LinearLayout>
</com.effective.android.panel.view.content.LinearContentContainer>
<!-- 面板区域仅能包含PanelView-->
<com.effective.android.panel.view.panel.PanelContainer
android:id="@+id/panel_container"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- 每一项面板 -->
<!-- panel_layout 用于指定面板该 ID 对应的布局 ,必须项-->
<!-- panel_trigger 用于用户点击该 ID 对应的 View 时切换到该面板 -->
<!-- panel_toggle 用于当该面板显示时 ,用户再次点击 panel_trigger 对应的 View 时是否回切输入法-->
<com.effective.android.panel.view.panel.PanelView
android:id="@+id/panel_emotion"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:panel_layout="@layout/nim_message_emoji_layout"
app:panel_trigger="@id/emoji_button" />
<com.effective.android.panel.view.panel.PanelView
android:id="@+id/panel_photo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:panel_layout="@layout/nim_message_photo_layout"
app:panel_trigger="@id/iv_image" />
</com.effective.android.panel.view.panel.PanelContainer>
</com.effective.android.panel.view.PanelSwitchLayout>

View File

@@ -13,6 +13,15 @@ import android.widget.TextView;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.mango.core.im.friend.IMFriendModel;
import com.mango.core.manager.IMNetEaseManager;
import com.mango.core.manager.RoomEvent;
import com.mango.core.utils.SystemUidUtil;
import com.mango.moshen.R;
import com.mango.moshen.room_chat.event.ClickRootViewEvent;
import com.mango.moshen.ui.im.avtivity.NewBaseMessageActivity;
import com.mango.moshen.ui.im.fragment.MessageFragment;
import com.mango.xchat_android_library.utils.SingleToastUtil;
import com.netease.nim.uikit.api.NimUIKit;
import com.netease.nim.uikit.api.model.contact.ContactChangedObserver;
import com.netease.nim.uikit.api.model.main.OnlineStateChangeObserver;
@@ -25,20 +34,6 @@ import com.netease.nimlib.sdk.Observer;
import com.netease.nimlib.sdk.msg.MsgServiceObserve;
import com.netease.nimlib.sdk.msg.constant.SessionTypeEnum;
import com.netease.nimlib.sdk.msg.model.CustomNotification;
import com.mango.moshen.R;
import com.mango.moshen.ui.im.avtivity.NewBaseMessageActivity;
import com.mango.moshen.ui.im.fragment.MessageFragment;
import com.mango.moshen.room_chat.event.ClickRootViewEvent;
import com.mango.core.im.friend.IMFriendModel;
import com.mango.core.initial.InitialModel;
import com.mango.core.initial.bean.InitInfo;
import com.mango.core.level.UserLevelVo;
import com.mango.core.manager.IMNetEaseManager;
import com.mango.core.manager.RoomEvent;
import com.mango.core.user.UserModel;
import com.mango.core.user.bean.UserInfo;
import com.mango.core.utils.SystemUidUtil;
import com.mango.xchat_android_library.utils.SingleToastUtil;
import org.greenrobot.eventbus.EventBus;
@@ -259,14 +254,11 @@ public class NimRoomP2PMessageActivity extends NewBaseMessageActivity {
int id = json.getIntValue("id");
if (id == 1) {
// 正在输入
// Toast.makeText(NimRoomP2PMessageActivity.this, "对方正在输入...", Toast.LENGTH_LONG).show();
SingleToastUtil.showToastShort("对方正在输入...");
} else {
// Toast.makeText(NimP2PMessageActivity.this, "command: " + content, Toast.LENGTH_SHORT).show();
SingleToastUtil.showToast("对方正在输入...");
}
} catch (Exception e) {
e.printStackTrace();
}
}
@@ -281,29 +273,12 @@ public class NimRoomP2PMessageActivity extends NewBaseMessageActivity {
MessageFragment fragment = new MessageFragment();
fragment.setArguments(arguments);
fragment.setContainerId(R.id.message_fragment_container);
// 等级限制:官方小秘书 和 系统消息,不设置等级限制
if (!SystemUidUtil.isSystemUid(sessionId)) {
UserInfo userInfo = UserModel.get().getCacheLoginUserInfo();
if (userInfo != null) {
UserLevelVo userLevelVo = userInfo.getUserLevelVo();
if (userLevelVo != null) {
fragment.setCurrentLevel(userLevelVo.experLevelSeq);
}
}
InitInfo initInfo = InitialModel.get().getCacheInitInfo();
if (initInfo != null) {
fragment.setLimitLevel(initInfo.getPrivateChatLevelNo());
fragment.setLimitLevel(initInfo.getPrivateChatRegisterDay());
}
}
return fragment;
}
@Override
protected void initToolBar() {
//ToolBarOptions options = new NimToolBarOptions();
// setToolBar(R.id.toolbar, options);
}
@Override