901 lines
35 KiB
Java
901 lines
35 KiB
Java
package com.ckkj.water.chat;
|
||
|
||
import static android.app.Activity.RESULT_OK;
|
||
|
||
import android.content.Context;
|
||
import android.content.Intent;
|
||
import android.graphics.Color;
|
||
import android.os.Bundle;
|
||
import android.os.Handler;
|
||
import android.text.Editable;
|
||
import android.text.TextUtils;
|
||
import android.text.TextWatcher;
|
||
import android.view.MotionEvent;
|
||
import android.view.View;
|
||
import android.view.inputmethod.InputMethodManager;
|
||
|
||
import androidx.annotation.NonNull;
|
||
import androidx.annotation.Nullable;
|
||
import androidx.core.content.ContextCompat;
|
||
import androidx.core.widget.NestedScrollView;
|
||
import androidx.recyclerview.widget.GridLayoutManager;
|
||
|
||
import com.chad.library.adapter.base.BaseQuickAdapter;
|
||
import com.ckkj.water.AppConfig;
|
||
import com.ckkj.water.EventMessage;
|
||
import com.ckkj.water.R;
|
||
import com.ckkj.water.base.BaseFragment;
|
||
import com.ckkj.water.chat.adapter.ChatQuestionDetailAdapter;
|
||
import com.ckkj.water.chat.adapter.ChatZixunAdapter;
|
||
import com.ckkj.water.chat.entity.ChatQuestionEntity;
|
||
import com.ckkj.water.custom.GridSpacingItemDecoration;
|
||
import com.ckkj.water.databinding.IndexLayoutBinding;
|
||
import com.ckkj.water.chat.adapter.IndexChatAdapter;
|
||
import com.ckkj.water.chat.adapter.IndexOptionAdapter;
|
||
import com.ckkj.water.chat.adapter.IndexEnterHintAdapter;
|
||
import com.ckkj.water.chat.adapter.IndexQuickAskAdapter;
|
||
import com.ckkj.water.chat.entity.CharHistoryEntity;
|
||
import com.ckkj.water.chat.entity.ChatAiMsgEntity;
|
||
import com.ckkj.water.chat.entity.ChatMultiItem;
|
||
import com.ckkj.water.chat.entity.IndexTemplateEntity;
|
||
import com.ckkj.water.chat.mvp.IndexContract;
|
||
import com.ckkj.water.chat.mvp.IndexImpl;
|
||
import com.ckkj.water.chat.mvp.IndexPresenter;
|
||
import com.ckkj.water.dialog.ContentReportDialog;
|
||
import com.ckkj.water.login.LoginActivity;
|
||
import com.ckkj.water.network.BaseBean;
|
||
import com.ckkj.water.plate.entity.BaziResultBean;
|
||
import com.ckkj.water.plate.entity.PaipanCache;
|
||
import com.ckkj.water.utils.click.ClickUtil;
|
||
import com.ckkj.water.utils.manager.ColorManage;
|
||
import com.ckkj.water.utils.show.ToastUtil;
|
||
|
||
import java.util.ArrayList;
|
||
import java.util.Arrays;
|
||
import java.util.List;
|
||
|
||
import de.greenrobot.event.Subscribe;
|
||
import de.greenrobot.event.ThreadMode;
|
||
import android.content.ClipData;
|
||
import android.content.ClipboardManager;
|
||
|
||
/**
|
||
* @author fengxiaoyang
|
||
* @date 2025/7/7
|
||
* @description TODO: 描述该类的作用
|
||
*/
|
||
|
||
public class ChatFragment extends BaseFragment<IndexLayoutBinding, IndexPresenter> implements IndexContract.IndexView {
|
||
|
||
//顶部 adapter
|
||
private IndexOptionAdapter mOptionAdapter;
|
||
private ChatZixunAdapter mZixunQuestAdapter;
|
||
private ChatQuestionDetailAdapter mQuesDetailAdapter;
|
||
//快速提问 adapter
|
||
private IndexQuickAskAdapter mQuickAskAdapter;
|
||
private List<IndexTemplateEntity> mQuickAskList = new ArrayList<>();
|
||
|
||
private List<ChatMultiItem> mChatMsgList = new ArrayList<>();
|
||
//聊天 adapter
|
||
private IndexChatAdapter mChatAdapter;
|
||
//欢迎语 adapter
|
||
private IndexEnterHintAdapter mHintAdapter;
|
||
//输入的内容
|
||
private String mCurrentInputContent = "";
|
||
//当前options 的 pos
|
||
private int mCurrentStreamPos = 0;
|
||
//模板数据 list
|
||
private List<IndexTemplateEntity> mIndexTemList = new ArrayList<>();
|
||
//输出是否完成
|
||
private boolean mIsStreamStop = true;
|
||
//当前选中 option 的 typeID
|
||
private String mSelectTypeID = "1";
|
||
//会话 ID
|
||
private String mSessionId = "";
|
||
|
||
// 添加流式数据累积变量
|
||
private StringBuilder mStreamContentBuilder = new StringBuilder();
|
||
private ChatAiMsgEntity mCurrentStreamEntity = null;
|
||
|
||
// 智能滚动管理
|
||
private boolean mIsUserScrolling = false;
|
||
|
||
// 优化防抖机制
|
||
private Handler mUpdateHandler = new Handler();
|
||
private Runnable mUpdateRunnable;
|
||
|
||
private List<ChatQuestionEntity> mQuesTypeList = new ArrayList<>();
|
||
private boolean mIsShowQuestions = true;
|
||
|
||
@Override
|
||
public void initData(Bundle state) {
|
||
|
||
mOptionAdapter = new IndexOptionAdapter(mContext, R.layout.index_device_list_item, mIndexTemList);
|
||
viewBinding.rvOptions.setAdapter(mOptionAdapter);
|
||
mQuickAskAdapter = new IndexQuickAskAdapter(mContext, R.layout.index_home_quick_ask_item, mQuickAskList);
|
||
viewBinding.rvQuickAsk.setAdapter(mQuickAskAdapter);
|
||
mChatAdapter = new IndexChatAdapter(mContext, mChatMsgList, viewBinding.rvChat);
|
||
viewBinding.rvChat.setAdapter(mChatAdapter);
|
||
//提问类型
|
||
GridLayoutManager manager = new GridLayoutManager(mContext, 2);
|
||
viewBinding.rvChatQuick.setLayoutManager(manager);
|
||
List<ChatQuestionEntity.SubheadingsDTO> subList = new ArrayList<>();
|
||
mQuesDetailAdapter = new ChatQuestionDetailAdapter(R.layout.chat_quest_detail_item, subList);
|
||
viewBinding.rvChatQuick.setAdapter(mQuesDetailAdapter);
|
||
viewBinding.rvChatQuick.addItemDecoration(new GridSpacingItemDecoration(2, 10, true));
|
||
mZixunQuestAdapter = new ChatZixunAdapter(mContext, R.layout.chat_zixun_type_item, mQuesTypeList);
|
||
viewBinding.rvChatType.setAdapter(mZixunQuestAdapter);
|
||
|
||
// 设置聊天RecyclerView的LayoutManager(禁用滚动)
|
||
androidx.recyclerview.widget.LinearLayoutManager chatLayoutManager =
|
||
new androidx.recyclerview.widget.LinearLayoutManager(mContext);
|
||
chatLayoutManager.setStackFromEnd(false);
|
||
viewBinding.rvChat.setLayoutManager(chatLayoutManager);
|
||
|
||
// 禁用RecyclerView的滚动,让NestedScrollView处理滚动
|
||
viewBinding.rvChat.setNestedScrollingEnabled(false);
|
||
|
||
mHintAdapter = new IndexEnterHintAdapter(mContext, R.layout.index_enter_hint_item, new ArrayList<>());
|
||
viewBinding.rvEnterHint.setAdapter(mHintAdapter);
|
||
|
||
setAdapterClick();
|
||
|
||
// 添加EditText的焦点监听器
|
||
viewBinding.editInput.setOnFocusChangeListener(new View.OnFocusChangeListener() {
|
||
@Override
|
||
public void onFocusChange(View v, boolean hasFocus) {
|
||
if (hasFocus) {
|
||
// EditText获取焦点时,延迟滚动到底部,确保键盘弹出后再滚动
|
||
new Handler().postDelayed(new Runnable() {
|
||
@Override
|
||
public void run() {
|
||
scrollNestedScrollViewToBottom();
|
||
}
|
||
}, 300); // 延迟300ms,等待键盘弹出
|
||
}
|
||
}
|
||
});
|
||
|
||
viewBinding.editInput.addTextChangedListener(new TextWatcher() {
|
||
@Override
|
||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||
}
|
||
|
||
@Override
|
||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||
}
|
||
|
||
@Override
|
||
public void afterTextChanged(Editable s) {
|
||
if (s != null) {
|
||
mCurrentInputContent = s.toString().trim();
|
||
if (TextUtils.isEmpty(s.toString().trim())) {
|
||
viewBinding.imgSend.setImageDrawable(ContextCompat.getDrawable(mContext, R.mipmap.icon_index_send));
|
||
viewBinding.editInput.setHint("请输入您的问题");
|
||
} else {
|
||
viewBinding.imgSend.setImageDrawable(ContextCompat.getDrawable(mContext, R.mipmap.icon_index_send_b));
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
//请求页面模板数据
|
||
mPresenter.getIndexTemplateDatas();
|
||
mPresenter.getChatTemplateDatas();
|
||
//组件增加点击区域
|
||
ClickUtil.addClickPart(viewBinding.imgCloseQuick, 30);
|
||
ClickUtil.addClickPart(viewBinding.imgRefresh, 30);
|
||
ClickUtil.addClickPart(viewBinding.imgHistory, 30);
|
||
|
||
// 设置智能滚动监听
|
||
setupSmartScrollListener();
|
||
// 设置页面点击监听,用于隐藏键盘
|
||
setupPageClickListener();
|
||
// 检查是否有传递过来的参数
|
||
checkAndHandleBundle();
|
||
}
|
||
|
||
/**
|
||
* 检查并处理传递过来的Bundle参数
|
||
*/
|
||
private void checkAndHandleBundle() {
|
||
Bundle args = getArguments();
|
||
if (args != null) {
|
||
handleBirthInfoBundle(args);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理出生信息Bundle参数
|
||
*
|
||
* @param bundle 包含出生信息的Bundle
|
||
*/
|
||
public void handleBirthInfoBundle(Bundle bundle) {
|
||
if (bundle != null) {
|
||
BaziResultBean baseEntity = PaipanCache.get();
|
||
// 获取出生信息
|
||
BaziResultBean.BaseInfoDTO birthInfo = baseEntity.getBaseInfo();
|
||
BaziResultBean.DetailInfoDTO.SizhuInfoDTO sizhuInfoDTO = baseEntity.getDetailInfo().getSizhuInfo();
|
||
BaziResultBean.DetailInfoDTO.DayunInfoDTO defaulLiunian = (BaziResultBean.DetailInfoDTO.DayunInfoDTO) bundle.getSerializable("defaul_liunian");
|
||
if (birthInfo != null) {
|
||
viewBinding.editInput.setText("我叫" + birthInfo.getName() + "," +
|
||
((birthInfo.getSex().equals("乾造") ? "男" : "女") + "," +
|
||
"生于公历" + birthInfo.getGongli() + "," + "当前大运为" + defaulLiunian.getDayunStartYear() + "--" +
|
||
defaulLiunian.getDayunEndYear() + "," + defaulLiunian.getDayunStartGanzhi() + "大运,四柱为"
|
||
+ sizhuInfoDTO.getYear().getTg() + sizhuInfoDTO.getYear().getDz() + "、" +
|
||
sizhuInfoDTO.getMonth().getTg() + sizhuInfoDTO.getMonth().getDz() + "、" +
|
||
sizhuInfoDTO.getDay().getTg() + sizhuInfoDTO.getDay().getDz() + "、" +
|
||
sizhuInfoDTO.getHour().getTg() + sizhuInfoDTO.getHour().getDz() + "," +
|
||
"现在需要你根据我的八字,给我具体分析一下我的事业、感情、健康、财富、格局、喜用神"));
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 当Fragment参数更新时调用(用于处理onNewIntent的情况)
|
||
*/
|
||
@Override
|
||
public void onResume() {
|
||
super.onResume();
|
||
|
||
// 检查是否有新的参数需要处理
|
||
Bundle args = getArguments();
|
||
if (args != null && args.getBoolean("goto_index_fragment", false)) {
|
||
// 处理参数
|
||
handleBirthInfoBundle(args);
|
||
|
||
// 清除标记,避免重复处理
|
||
args.remove("goto_index_fragment");
|
||
}
|
||
}
|
||
|
||
@Subscribe(threadMode = ThreadMode.MainThread)
|
||
public void onGetMessage(EventMessage message) {
|
||
//用户添加地址成功
|
||
if (message.getEventStrKey().equals(EventMessage.HISTORY_SESSIONID_DELETE_SUCCESS)) {
|
||
if (message.getMsg().equals(mSessionId)) {
|
||
// viewBinding.linearOptionHint.setVisibility(View.VISIBLE);
|
||
mChatMsgList.clear();
|
||
mChatAdapter.setNewData(mChatMsgList);
|
||
mSessionId = "";
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* 设置页面点击监听器
|
||
*/
|
||
private void setupPageClickListener() {
|
||
viewBinding.rvChat.setOnTouchListener(new View.OnTouchListener() {
|
||
@Override
|
||
public boolean onTouch(View v, MotionEvent event) {
|
||
// 当用户触摸NestedScrollView时,隐藏键盘并清除焦点
|
||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
||
hideKeyboardAndClearFocus();
|
||
}
|
||
return false;
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 隐藏键盘并清除EditText焦点
|
||
*/
|
||
private void hideKeyboardAndClearFocus() {
|
||
// 清除EditText焦点
|
||
viewBinding.editInput.clearFocus();
|
||
|
||
// 隐藏键盘
|
||
InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||
if (imm != null) {
|
||
imm.hideSoftInputFromWindow(viewBinding.editInput.getWindowToken(), 0);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 滚动NestedScrollView到底部
|
||
*/
|
||
private void scrollNestedScrollViewToBottom() {
|
||
if (viewBinding.nestedScrollChat != null) {
|
||
// 获取NestedScrollView的子视图(RecyclerView)
|
||
View childView = viewBinding.nestedScrollChat.getChildAt(0);
|
||
if (childView != null) {
|
||
// 滚动到底部
|
||
viewBinding.nestedScrollChat.smoothScrollTo(0, childView.getHeight());
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 设置智能滚动监听器(修改为监听NestedScrollView)
|
||
*/
|
||
private void setupSmartScrollListener() {
|
||
// 监听NestedScrollView的滚动
|
||
viewBinding.nestedScrollChat.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
|
||
@Override
|
||
public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
|
||
// 检测用户是否向上滑动
|
||
if (scrollY < oldScrollY) {
|
||
// 用户向上滑动
|
||
mIsUserScrolling = true;
|
||
} else if (scrollY > oldScrollY) {
|
||
// 用户向下滑动
|
||
mIsUserScrolling = true;
|
||
}
|
||
|
||
// 检查是否滚动到底部
|
||
if (scrollY >= (v.getChildAt(0).getHeight() - v.getHeight())) {
|
||
// 滚动到底部,恢复自动滚动
|
||
mIsUserScrolling = false;
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
|
||
private void setAdapterClick() {
|
||
//顶部的选项 adapter 点击事件
|
||
mOptionAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
|
||
@Override
|
||
public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
|
||
if (mCurrentStreamPos != position) {
|
||
if (mIsStreamStop) {
|
||
viewBinding.rlQuickAsk.setVisibility(View.GONE);
|
||
mOptionAdapter.setSelectPos(position);
|
||
setOptionSelStyle(position);
|
||
mCurrentStreamPos = position;
|
||
mSessionId = "";
|
||
mSelectTypeID = mIndexTemList.get(position).getId();
|
||
} else {
|
||
ToastUtil.showShort(mContext, "Ai解答中,请稍后操作");
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
//咨询类型 adapter
|
||
mZixunQuestAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
|
||
@Override
|
||
public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
|
||
if (mIsStreamStop) {
|
||
mZixunQuestAdapter.setSelectPos(position);
|
||
mQuesDetailAdapter.setNewData(mQuesTypeList.get(position).getSubheadings());
|
||
} else {
|
||
ToastUtil.showShort(mContext, "Ai解答中,请稍后操作");
|
||
}
|
||
}
|
||
});
|
||
|
||
//快速提问
|
||
mQuesDetailAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
|
||
@Override
|
||
public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
|
||
if (!AppConfig.USER_LOGIN) {
|
||
goTo(mContext, LoginActivity.class, null);
|
||
return;
|
||
}
|
||
if (!mIsStreamStop) {
|
||
ToastUtil.showShort(mContext, "Ai解答中,请稍后操作");
|
||
return;
|
||
}
|
||
String quickAskContent = mQuesTypeList.get(mZixunQuestAdapter.getmSelPos()).
|
||
getSubheadings().get(position).getContent();
|
||
sendMsgToAi(quickAskContent);
|
||
}
|
||
});
|
||
|
||
//流式输出,是否完成
|
||
mChatAdapter.setOnStreamCompleteListener(new IndexChatAdapter.OnStreamCompleteListener() {
|
||
@Override
|
||
//此时已经流式输出完成
|
||
public void onStreamComplete(int position) {
|
||
mCurrentStreamPos = position;
|
||
viewBinding.imgSend.setVisibility(View.VISIBLE);
|
||
viewBinding.imgStop.setVisibility(View.GONE);
|
||
mIsStreamStop = true;
|
||
}
|
||
|
||
@Override
|
||
public void onStreamStop() {
|
||
viewBinding.imgSend.setVisibility(View.VISIBLE);
|
||
viewBinding.imgStop.setVisibility(View.GONE);
|
||
mIsStreamStop = true;
|
||
}
|
||
});
|
||
mChatAdapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() {
|
||
@Override
|
||
public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) {
|
||
switch (view.getId()) {
|
||
case R.id.img_jb:
|
||
ContentReportDialog cancelDialog = new ContentReportDialog(mContext, R.layout.dialog_report_reason, "");
|
||
cancelDialog.show();
|
||
cancelDialog.setOnDialogListener(new ContentReportDialog.DialogListener() {
|
||
@Override
|
||
public void callBackDatas(String reason) {
|
||
showLoadingDialog();
|
||
//举报
|
||
mPresenter.commitReport(reason);
|
||
}
|
||
});
|
||
break;
|
||
}
|
||
}
|
||
});
|
||
|
||
// 设置长按菜单监听器
|
||
mChatAdapter.setOnItemLongClickMenuListener(new IndexChatAdapter.OnItemLongClickMenuListener() {
|
||
@Override
|
||
public void onCopyClick(ChatMultiItem item, int position) {
|
||
// 处理复制操作
|
||
String content = item.getMessage().getContent();
|
||
if (!TextUtils.isEmpty(content)) {
|
||
ClipboardManager clipboard = (ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
|
||
ClipData clip = ClipData.newPlainText("chat_content", content);
|
||
clipboard.setPrimaryClip(clip);
|
||
ToastUtil.showShort(mContext, "内容已复制");
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void onReportClick(ChatMultiItem item, int position) {
|
||
// 处理举报操作
|
||
ContentReportDialog cancelDialog = new ContentReportDialog(mContext, R.layout.dialog_report_reason, "");
|
||
cancelDialog.show();
|
||
cancelDialog.setOnDialogListener(new ContentReportDialog.DialogListener() {
|
||
@Override
|
||
public void callBackDatas(String reason) {
|
||
showLoadingDialog();
|
||
//举报
|
||
mPresenter.commitReport(reason);
|
||
}
|
||
});
|
||
}
|
||
});
|
||
|
||
// 添加长按监听器
|
||
// mChatAdapter.setOnItemChildLongClickListener(new BaseQuickAdapter.OnItemChildLongClickListener() {
|
||
// @Override
|
||
// public boolean onItemChildLongClick(BaseQuickAdapter adapter, View view, int position) {
|
||
// switch (view.getId()) {
|
||
// case R.id.tv_text: // 文本内容的view id
|
||
// XPopup.fixLongClick(view); // 能保证弹窗弹出后,下层的View无法滑动
|
||
//
|
||
// // 获取当前消息内容
|
||
// ChatMultiItem item = mChatMsgList.get(position);
|
||
// String content = item.getMessage().getContent();
|
||
//
|
||
// // 使用链式调用,确保atView在asAttachList之前
|
||
// new XPopup.Builder(getContext())
|
||
// .hasShadowBg(false)
|
||
// .atView(view) // 使用atView而不是watchView
|
||
// .asAttachList(new String[]{"复制", "举报"},
|
||
// null,
|
||
// new OnSelectListener() {
|
||
// @Override
|
||
// public void onSelect(int selectPosition, String text) {
|
||
// switch (selectPosition) {
|
||
// case 0: // 复制
|
||
// if (!TextUtils.isEmpty(content)) {
|
||
// ClipboardManager clipboard = (ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
|
||
// ClipData clip = ClipData.newPlainText("chat_content", content);
|
||
// clipboard.setPrimaryClip(clip);
|
||
// ToastUtil.showShort(mContext, "内容已复制");
|
||
// }
|
||
// break;
|
||
// case 1: // 举报
|
||
// ContentReportDialog cancelDialog = new ContentReportDialog(mContext, R.layout.dialog_report_reason, "");
|
||
// cancelDialog.show();
|
||
// cancelDialog.setOnDialogListener(new ContentReportDialog.DialogListener() {
|
||
// @Override
|
||
// public void callBackDatas(String reason) {
|
||
// showLoadingDialog();
|
||
// //举报
|
||
// mPresenter.commitReport(reason);
|
||
// }
|
||
// });
|
||
// break;
|
||
// }
|
||
// }
|
||
// })
|
||
// .show();
|
||
// return true;
|
||
// }
|
||
// return false;
|
||
// }
|
||
// });
|
||
}
|
||
|
||
|
||
/**
|
||
* 停止输出
|
||
*/
|
||
private void stopSteamOutput() {
|
||
// 停止当前网络请求
|
||
mPresenter.stopCurrentRequest();
|
||
|
||
// 获取当前正在流式输出的位置
|
||
int currentPosition = mChatMsgList.size() - 1;
|
||
|
||
// 标记当前消息为被停止状态
|
||
mChatAdapter.markAsStopped(currentPosition);
|
||
|
||
// 停止适配器中的流式输出
|
||
mChatAdapter.setStreamerStop(currentPosition);
|
||
|
||
// 清除当前流式输出位置
|
||
mChatAdapter.setCurrentStreamingPosition(-1);
|
||
|
||
// 强制更新当前item以显示停止标识
|
||
mChatAdapter.notifyItemChanged(currentPosition);
|
||
|
||
// 恢复UI状态
|
||
viewBinding.imgSend.setVisibility(View.VISIBLE);
|
||
viewBinding.imgStop.setVisibility(View.GONE);
|
||
mIsStreamStop = true;
|
||
}
|
||
|
||
@Override
|
||
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
|
||
super.onActivityResult(requestCode, resultCode, data);
|
||
if (data != null) {
|
||
if (resultCode == RESULT_OK) {
|
||
switch (requestCode) {
|
||
case REQUEST_EVENT_CODE_10001:
|
||
String sessionId = data.getStringExtra("session_id");
|
||
String numerologyType = data.getStringExtra("numerology_type");
|
||
int showSelPos = -1;
|
||
for (int i = 0; i < mIndexTemList.size(); i++) {
|
||
if (numerologyType.equals(mIndexTemList.get(i).getId())) {
|
||
showSelPos = i;
|
||
}
|
||
}
|
||
if (mCurrentStreamPos != showSelPos) {
|
||
|
||
}
|
||
if (mIsStreamStop) {
|
||
viewBinding.rlQuickAsk.setVisibility(View.GONE);
|
||
mOptionAdapter.setSelectPos(showSelPos);
|
||
setOptionSelStyle(showSelPos);
|
||
mCurrentStreamPos = showSelPos;
|
||
mSelectTypeID = mIndexTemList.get(showSelPos).getId();
|
||
if (!TextUtils.isEmpty(sessionId)) {
|
||
mPresenter.getChatDetailByID(sessionId);
|
||
}
|
||
} else {
|
||
ToastUtil.showShort(mContext, "Ai解答中,请稍后操作");
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 发送消息给 AI
|
||
*/
|
||
private void sendMsgToAi(String senMsg) {
|
||
if (!AppConfig.USER_LOGIN) {
|
||
goTo(mContext, LoginActivity.class, null);
|
||
return;
|
||
}
|
||
viewBinding.cardQuestion.setVisibility(View.GONE);
|
||
viewBinding.imgClose.setImageDrawable(ContextCompat.getDrawable(mContext, R.mipmap.icon_chat_down));
|
||
mIsShowQuestions = false;
|
||
viewBinding.linearOptionHint.setVisibility(View.GONE);
|
||
// 重置滚动状态
|
||
mIsUserScrolling = false;
|
||
// 清理适配器缓存
|
||
mChatAdapter.clearCache();
|
||
|
||
// 创建用户消息
|
||
ChatAiMsgEntity chatMsgEntity = new ChatAiMsgEntity();
|
||
chatMsgEntity.setSessionId(mSessionId);
|
||
chatMsgEntity.setType(mSelectTypeID);
|
||
chatMsgEntity.setContent(senMsg);
|
||
ChatMultiItem userEntity = new ChatMultiItem(ChatMultiItem.TYPE_TEXT_RIGHT, chatMsgEntity);
|
||
mChatMsgList.add(userEntity);
|
||
mChatAdapter.notifyItemInserted(mChatMsgList.size() - 1);
|
||
|
||
// 创建AI回复消息(初始为空)
|
||
mCurrentStreamEntity = new ChatAiMsgEntity();
|
||
mCurrentStreamEntity.setSessionId(mSessionId);
|
||
mCurrentStreamEntity.setType(mSelectTypeID);
|
||
mCurrentStreamEntity.setContent(""); // 初始为空
|
||
ChatMultiItem aiEntity = new ChatMultiItem(ChatMultiItem.TYPE_TEXT_LEFT, mCurrentStreamEntity);
|
||
mChatMsgList.add(aiEntity);
|
||
|
||
// 设置当前流式输出位置
|
||
int aiMessagePosition = mChatMsgList.size() - 1;
|
||
mChatAdapter.setCurrentStreamingPosition(aiMessagePosition);
|
||
mChatAdapter.notifyItemInserted(aiMessagePosition);
|
||
|
||
// 重置流式数据累积器
|
||
mStreamContentBuilder.setLength(0);
|
||
|
||
// 清空输入框
|
||
viewBinding.editInput.setText("");
|
||
|
||
// 设置UI状态
|
||
viewBinding.imgSend.setVisibility(View.GONE);
|
||
viewBinding.imgStop.setVisibility(View.VISIBLE);
|
||
|
||
// 发送消息后自动滚动到底部
|
||
scrollToBottomAfterSend();
|
||
|
||
// 发起流式请求
|
||
mPresenter.talkToAi(senMsg, mSelectTypeID, mSessionId);
|
||
}
|
||
|
||
@Override
|
||
public IndexPresenter createPresenter() {
|
||
return new IndexPresenter(new IndexImpl());
|
||
}
|
||
|
||
@Override
|
||
public void getTalkResultSuccess(ChatAiMsgEntity bean) {
|
||
hideDialogLoading();
|
||
}
|
||
|
||
/**
|
||
* 首页数据获取成功
|
||
*
|
||
* @param list
|
||
*/
|
||
@Override
|
||
public void getIndexTemplateSuccess(List<IndexTemplateEntity> list) {
|
||
if (list != null && list.size() != 0) {
|
||
mIndexTemList = list;
|
||
mOptionAdapter.setNewData(mIndexTemList);
|
||
setAdapterClick();
|
||
setOptionSelStyle(0);
|
||
mSelectTypeID = mIndexTemList.get(0).getId();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 切换设置显示内容
|
||
*
|
||
* @param pos
|
||
*/
|
||
private void setOptionSelStyle(int pos) {
|
||
try {
|
||
mChatMsgList.clear();
|
||
mChatAdapter.setNewData(mChatMsgList);
|
||
// 清理缓存
|
||
mChatAdapter.clearCache();
|
||
// viewBinding.linearOptionHint.setVisibility(View.VISIBLE);
|
||
IndexTemplateEntity defaulEntity = mIndexTemList.get(pos);
|
||
viewBinding.tvTitleExpert.setText(defaulEntity.getTitle() + "专家");
|
||
viewBinding.tvSelectOptionLable.setText(defaulEntity.getTitle() + "专家");
|
||
viewBinding.tvSelectOptionSlogan.setText(defaulEntity.getSubheading());
|
||
if (ColorManage.getInstance().isParsableColor(defaulEntity.getColor())) {
|
||
viewBinding.tvSelectOptionSlogan.setTextColor(Color.parseColor(defaulEntity.getColor()));
|
||
} else {
|
||
viewBinding.tvSelectOptionSlogan.setTextColor(ContextCompat.getColor(mContext, R.color.color_zhu));
|
||
}
|
||
//提示语
|
||
List<String> hintMsgList = new ArrayList<>();
|
||
hintMsgList = Arrays.asList(defaulEntity.getIntermediateContent().split("#"));
|
||
mHintAdapter.setNewData(hintMsgList);
|
||
//快速提问
|
||
mQuickAskList.clear();
|
||
List<String> quickAskList = new ArrayList<>();
|
||
quickAskList = Arrays.asList(defaulEntity.getQuickQuestions().split("#"));
|
||
for (String content : quickAskList) {
|
||
IndexTemplateEntity entity = new IndexTemplateEntity();
|
||
entity.setColor(defaulEntity.getColor());
|
||
entity.setBlackcolor(defaulEntity.getBlackcolor());
|
||
entity.setSpliteItemContent(content);
|
||
mQuickAskList.add(entity);
|
||
}
|
||
mQuickAskAdapter.setNewData(mQuickAskList);
|
||
} catch (Exception e) {
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void onNetChange(int netModel) {
|
||
|
||
}
|
||
|
||
@Override
|
||
public void showError(String message) {
|
||
hideDialogLoading();
|
||
ToastUtil.showShort(mContext, message);
|
||
// 流式传输完成,恢复UI状态
|
||
viewBinding.imgSend.setVisibility(View.VISIBLE);
|
||
viewBinding.imgStop.setVisibility(View.GONE);
|
||
mIsStreamStop = true;
|
||
|
||
// 可以在这里处理会话ID等
|
||
if (mCurrentStreamEntity != null && !TextUtils.isEmpty(mCurrentStreamEntity.getSessionId())) {
|
||
mSessionId = mCurrentStreamEntity.getSessionId();
|
||
}
|
||
|
||
// 清理当前流式实体
|
||
mCurrentStreamEntity = null;
|
||
mStreamContentBuilder.setLength(0);
|
||
}
|
||
|
||
@Override
|
||
public void logOut(BaseBean bean) {
|
||
|
||
}
|
||
|
||
// 实现流式消息接收回调
|
||
@Override
|
||
public void onStreamMessageReceived(String messageChunk) {
|
||
mIsStreamStop = false;
|
||
if (mCurrentStreamEntity != null) {
|
||
// 更新当前AI消息的内容
|
||
mCurrentStreamEntity.setContent(messageChunk);
|
||
|
||
// 取消之前的更新任务
|
||
if (mUpdateRunnable != null) {
|
||
mUpdateHandler.removeCallbacks(mUpdateRunnable);
|
||
}
|
||
|
||
mUpdateRunnable = () -> {
|
||
// 只更新最后一个item
|
||
int lastPosition = mChatMsgList.size() - 1;
|
||
if (lastPosition >= 0) {
|
||
// 使用更轻量的更新方式,避免重新布局
|
||
mChatAdapter.notifyItemChanged(lastPosition, "content_only");
|
||
|
||
// 流式输出时,如果用户没有手动滑动,则自动滚动NestedScrollView
|
||
if (!mIsUserScrolling) {
|
||
smoothScrollNestedScrollView();
|
||
}
|
||
}
|
||
};
|
||
|
||
// 增加延迟时间,减少更新频率
|
||
mUpdateHandler.postDelayed(mUpdateRunnable, 100);
|
||
}
|
||
}
|
||
|
||
|
||
// 实现流式完成回调
|
||
@Override
|
||
public void onStreamComplete(String complateMsg) {
|
||
// 流式传输完成,恢复UI状态
|
||
viewBinding.imgSend.setVisibility(View.VISIBLE);
|
||
viewBinding.imgStop.setVisibility(View.GONE);
|
||
mIsStreamStop = true;
|
||
|
||
// 清除当前流式输出位置
|
||
mChatAdapter.setCurrentStreamingPosition(-1);
|
||
|
||
// 注意:这里不标记为停止状态,因为这是正常完成
|
||
|
||
// 可以在这里处理会话ID等
|
||
if (mCurrentStreamEntity != null && !TextUtils.isEmpty(mCurrentStreamEntity.getSessionId())) {
|
||
mSessionId = mCurrentStreamEntity.getSessionId();
|
||
}
|
||
|
||
// 清理当前流式实体
|
||
mCurrentStreamEntity = null;
|
||
mStreamContentBuilder.setLength(0);
|
||
// 假设你最后一个消息是流式的 placeholder,现在替换它
|
||
ChatMultiItem lastMessage = mChatMsgList.get(mChatMsgList.size() - 1);
|
||
lastMessage.getMessage().setContent(complateMsg); // 你可以解析 Markdown 变 PlainText,或保留 Markdown 渲染
|
||
// mChatAdapter.notifyItemChanged(mChatMsgList.size() - 1); // 更新 UI
|
||
int lastPos = mChatAdapter.getItemCount() - 1;
|
||
mChatAdapter.updateTipsVisibility(lastPos);
|
||
}
|
||
|
||
|
||
/**
|
||
* 根据会话 id 查询对话详情
|
||
*
|
||
* @param list
|
||
*/
|
||
@Override
|
||
public void getChatDetailSuccess(List<ChatAiMsgEntity> list) {
|
||
mChatMsgList.clear();
|
||
if (list != null && list.size() != 0) {
|
||
mSessionId = list.get(0).getSessionId();
|
||
}
|
||
for (ChatAiMsgEntity entity : list) {
|
||
ChatMultiItem item;
|
||
if (entity.getRole().equals("user")) {
|
||
item = new ChatMultiItem(ChatMultiItem.TYPE_TEXT_RIGHT, entity);
|
||
} else {
|
||
item = new ChatMultiItem(ChatMultiItem.TYPE_TEXT_LEFT, entity);
|
||
}
|
||
mChatMsgList.add(item);
|
||
}
|
||
mChatAdapter.setNewData(mChatMsgList);
|
||
viewBinding.linearOptionHint.setVisibility(View.GONE);
|
||
}
|
||
|
||
@Override
|
||
public void loginLost() {
|
||
if (isFragmentVisible()) {
|
||
handleLoginLost();
|
||
}
|
||
stopSteamOutput();
|
||
if (mChatAdapter != null) {
|
||
mChatAdapter.remove(mChatMsgList.size() - 1);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取咨询模板成功
|
||
*
|
||
* @param list
|
||
*/
|
||
@Override
|
||
public void getChatZixunSuccess(List<ChatQuestionEntity> list) {
|
||
this.mQuesTypeList = list;
|
||
if (list != null) {
|
||
mZixunQuestAdapter.setNewData(list);
|
||
mQuesDetailAdapter.setNewData(list.get(0).getSubheadings());
|
||
setAdapterClick();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 举报成功
|
||
* @param bean
|
||
*/
|
||
@Override
|
||
public void commitReportSuccess(BaseBean bean) {
|
||
ToastUtil.showShort(mContext, "星语明灯感谢您的反馈");
|
||
hideDialogLoading();
|
||
}
|
||
|
||
// 实现sessionId接收回调
|
||
@Override
|
||
public void onSessionReceived(String sessionId) {
|
||
// 保存sessionId
|
||
mSessionId = sessionId;
|
||
}
|
||
|
||
@Override
|
||
public void getHistoryDataSuccess(CharHistoryEntity entity) {
|
||
|
||
}
|
||
|
||
@Override
|
||
public void deleteHistorySuccess(BaseBean bean, int delPos) {
|
||
|
||
}
|
||
|
||
|
||
/**
|
||
* 平滑滚动NestedScrollView到底部
|
||
*/
|
||
private void smoothScrollNestedScrollView() {
|
||
if (mChatAdapter != null && mChatAdapter.getItemCount() > 0) {
|
||
// 直接滚动NestedScrollView到底部
|
||
viewBinding.nestedScrollChat.post(() -> {
|
||
viewBinding.nestedScrollChat.fullScroll(View.FOCUS_DOWN);
|
||
});
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* 发送消息后滚动到底部
|
||
*/
|
||
private void scrollToBottomAfterSend() {
|
||
new Handler().postDelayed(() -> {
|
||
if (isFragmentVisible()) {
|
||
viewBinding.nestedScrollChat.fullScroll(View.FOCUS_DOWN);
|
||
}
|
||
}, 100);
|
||
}
|
||
|
||
@Override
|
||
public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
|
||
|
||
}
|
||
|
||
@Override
|
||
public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
|
||
|
||
}
|
||
}
|