ai问答效果视频
//1、初始化自定义AI操作manager
AiWSManager.ins().init(this@AiActivity)
AiWSManager.ins().initSessionId()
//2、创建监听器
AiWSManager.ins().aiMessageCallBackListener = object : AiWSManager.AiMessageCallBackListener {
override fun onMessage(text: String?) {
this@AiActivity.runOnUiThread {
if (!TextUtils.isEmpty(text)) {
val aIChatBean = Gson().fromJson(text, AiWSManager.AIChatBean::class.java)
if (aIChatBean.sessionId == AiWSManager.ins().sessionId) {
//检查当前列表是否存在该数据
val isInChatIndex = aIChatBeanList.indexOfFirst { it.chatId == aIChatBean.chatId }
if (isInChatIndex == -1) { // 不存在,创建新的
checkSave()
createOneQuestion(aIChatBean)
} else { // 存在,更新现有的
if (aIChatBeanList[isInChatIndex].interrupt == "F") {
// 获取现有对象
val existingBean = aIChatBeanList[isInChatIndex]
// 更新 useAnswer 字段
if (TextUtils.isEmpty(existingBean.useAnswer)) {
existingBean.useAnswer = aIChatBean.answer
} else {
existingBean.useAnswer += aIChatBean.answer
}
if (aIChatBean.matchKnowledges.size > 0) {
existingBean.matchKnowledges.clear()
existingBean.matchKnowledges.addAll(aIChatBean.matchKnowledges)
}
if (aIChatBean.knowledgeInfo.size > 0) {
existingBean.knowledgeInfo.clear()
existingBean.knowledgeInfo.addAll(aIChatBean.knowledgeInfo)
}
existingBean.queryData = aIChatBean.queryData
// 更新 answerFlag 字段
existingBean.answerFlag = aIChatBean.answerFlag
// 如果 answerFlag 是 "end",更新 keywords
if ("end" == existingBean.answerFlag) {
existingBean.keywords.clear()
existingBean.keywords.addAll(aIChatBean.keywords)
}
// 确保更新后的对象保留在列表中
aIChatBeanList[isInChatIndex] = existingBean
// 开启线程监听单条数据更新状态
AiWSManager.ins().aiAdapter.updateLastView(aIChatBeanList[isInChatIndex])
}
}
} else {
Log.e("MZQ_AI", "非同一个会话内容$text")
}
}
}
}
override fun onReconnect() {
Log.e("MZQ_AI", "onReconnect--Ai网络错误处理:" + isIngChat)
}
override fun onFailure() {
Log.e("MZQ_AI", "onFailure--Ai网络错误处理:" + isIngChat)
if (isIngChat) {
userStop("S", object : ChatStopListener {
override fun stop() {
}
})
}
}
override fun onStatusChange() {
if (!isIngChat) {
sendCheckView()
}
}
override fun onOpen() {
if (AiWSManager.ins().isConnect) {
if (isFirstFlag) {
isFirstFlag = false
val question = intent.getStringExtra("question")
if (!TextUtils.isEmpty(question)) {
if (!isIngChat) {
if (question != null) {
sendMessage(question)
}
}
}
if (!TextUtils.isEmpty(intent.getStringExtra("chatId"))) {
getChatById(intent.getStringExtra("chatId"))
}
}
}
}
}
//3、发起会话
if (FastClickUtil.isFastClick()) {
return@setOnClickListener
}
if (AiWSManager.ins().isConnect) {
if (isIngChat) {
userStop("T", object : ChatStopListener {
override fun stop() {
}
})
} else {
if (TextUtils.isEmpty(editSend.text.toString().trim())) {
SolutionToast.show("请输入您的问题")
return@setOnClickListener
}
sendMessage(editSend.text.toString())
}
} else {
Log.e("MZQ_AI", "AI大模型连接失败,当前AI提问功能不可用")
}
//manager具体内容
import android.app.Activity
import android.content.Context
import android.os.Build
import android.text.TextUtils
import android.util.Log
import androidx.annotation.RequiresApi
import com.example.okhttp.OkHttpManager
import com.google.gson.reflect.TypeToken
import com.mohism.medtion.base.websocketUtils.AIWsManager
import com.mohism.medtion.base.websocketUtils.listener.WsStatusListener
import com.mohism.medtion.bean.BaseBean
import com.mohism.medtion.constant.ConstantAI
import com.mohism.medtion.constant.ConstantObj
import com.mohism.medtion.okhttp.NyhHttpUtil
import com.mohism.medtion.requestcallback.MyResultCallback
import com.mohism.medtion.ui.ai.adapter.AIAdapter
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import okio.ByteString
import java.util.UUID
/**
* Ai问答
*/
class AiWSManager {
// 定义一个接口用于异步请求结果的回调
interface AiClientCallback<T> {
fun onSuccess(result: T?)
fun onError(code: Int, msg: String?)
}
companion object {
private var sInstance: AiWSManager? = null
const val AI_TIP = "AI_TIP"
@JvmStatic
fun ins(): AiWSManager {
if (sInstance == null) {
sInstance = AiWSManager()
}
return sInstance as AiWSManager
}
}
lateinit var aiAdapter: AIAdapter
var wsManager: AIWsManager? = null
var aiMessageCallBackListener: AiMessageCallBackListener? = null
var isConnect = false
var sessionId = ""
fun init(context: Context) {
close()
if (wsManager == null) {
wsManager = AIWsManager.Builder(context).client(
OkHttpClient().newBuilder()
.retryOnConnectionFailure(true).build()
).needReconnect(true).wsUrl(ConstantObj.AI_URL).build()
wsManager?.setWsStatusListener(wsStatusListener)
}
wsManager?.startConnect()
}
fun reInit(context: Context) {
if (wsManager == null) {
wsManager = AIWsManager.Builder(context).client(
OkHttpClient().newBuilder()
.retryOnConnectionFailure(true).build()
).needReconnect(true).wsUrl(ConstantObj.AI_URL).build()
wsManager?.setWsStatusListener(wsStatusListener)
}
wsManager?.startConnect()
}
fun initSessionId(session: String? = null) {
sessionId = if (!TextUtils.isEmpty(session)) {
session ?: UUID.randomUUID().toString()
} else {
UUID.randomUUID().toString()
}
}
fun close() {
sessionId = ""
wsManager?.stopConnect()
}
var isOnFailure = false
/**
* 发送ai消息
*/
fun sendAiMessage(json: String) {
if (isConnect) {
Log.e("MZQ_AI", "请求参数:$json")
wsManager?.sendMessage(json)
} else {
Log.e("MZQ_AI", "网络连接失败,请稍后重试!")
}
}
private val wsStatusListener: WsStatusListener = object : WsStatusListener() {
override fun onOpen(response: Response?) {
Log.e("MZQ_AI", "onOpen")
isConnect = true
isOnFailure = false
aiMessageCallBackListener?.onOpen()
}
@RequiresApi(Build.VERSION_CODES.O)
override fun onMessage(text: String?) {
Log.e("MZQ_AI", "onMessage:$text")
aiMessageCallBackListener?.onMessage(text)
}
override fun onMessage(bytes: ByteString?) {
Log.e("MZQ_AI", "onMessage")
}
override fun onReconnect() {
isConnect = false
Log.e("MZQ_AI", "onReconnect")
aiMessageCallBackListener?.onStatusChange()
}
override fun onClosing(code: Int, reason: String?) {
isConnect = false
Log.e("MZQ_AI", "onClosing$code------$reason")
aiMessageCallBackListener?.onStatusChange()
}
override fun onClosed(code: Int, reason: String?) {
isConnect = false
Log.e("MZQ_AI", "onClosed$code------$reason")
aiMessageCallBackListener?.onStatusChange()
}
override fun onFailure(t: Throwable?, response: Response?) {
isConnect = false
isOnFailure = true
Log.e("MZQ_AI", "onFailure")
aiMessageCallBackListener?.onFailure()
aiMessageCallBackListener?.onStatusChange()
}
}
interface AiMessageCallBackListener {
fun onStatusChange()
fun onMessage(text: String?)
fun onReconnect()
fun onFailure()
fun onOpen()
}
enum class AiModel(var modelName: String) {
CHAT_GPT("chatgpt"),
KIMI("kimi"),
DOU_BAO("doubao"),
}
/**
* 获取问答请求参数
*/
data class AiQuestionRequestBean(
var question: String? = null,
var model: String? = null,
var history: MutableList<AiQuestionHistory> = ArrayList(),
var sessionId: String? = null
)
data class AiStopRequestBean(
var stopFlag: Boolean = true,
var chatId: String? = null
)
data class AiQuestionHistory(
var query: String? = null,
var answer: String = ""
)
/**
* 相关问题请求对象
*/
data class AiQuestionOtherBean(
var question: String? = null,
var chatId: String? = null
)
data class AIHistoryBean(
val sessionId: String? = null, // 会话的唯一标识符
var name: String? = null,
var dateType: Int = 1,//1:今天 2:本周 3:本月 4:更早
var dateValue: String? = null,
)
/**
* 表示来自 AI 聊天系统的响应的数据类。
*
* @property queryData 查询数据的信息;NULL 表示没有找到相关数据。
* @property answerFlag 答案的状态;可以是 "start"、"ing"、"end" 或 NULL 表示答案尚未生成。
* @property answer AI 模型提供的答案。
* @property knowledgeInfo 与查询相关的知识信息列表。
* @property matchKnowledges 匹配到的相关知识列表(仅供展示,不提供跳转)。
* @property keyWords 与查询相关的关键词列表。
* @property chatId 聊天会话的唯一标识符。
* @property sessionId 会话的唯一标识符。
*/
data class AIChatBean(
var isDefaultChat: Boolean = false,//是否为默认消息
var queryData: String? = null, // NULL值说明没找到相关资料
var answerFlag: String? = null, // start:答案开始输出。 ing:答案输出中。 end:答案输出完成。 null值表示正在获取资料答案还没开始生成
var answer: String? = null, // 模型回答
var useAnswer: String? = null,//使用回答
val knowledgeInfo: MutableList<AIKnowledgeInfo> = ArrayList(), // 知识信息列表
val matchKnowledges: MutableList<AIMatchKnowledge> = ArrayList(), // 已查找到的相关参考资料列表
var chatId: String? = null, // 会话的唯一标识符
var sessionId: String? = null, // 会话的唯一标识符
val keywords: MutableList<String> = ArrayList(), // 关键词列表
val question: String? = null,//问题标题
var collect: String = "F",
var feedback: String = "F",
val relatedQuestions: MutableList<String> = ArrayList(),
var createTime: String? = null,
var updateTime: String? = null,
var interrupt: String = "F",//用户是否手动暂停?/或者网络中断
var isOpenMore: Boolean = false,//是否展开更多了
var isRequestOther: Boolean = false,//是否已经请求过该接口
var saveStatus: Boolean = false,
var isCollect: Boolean = false,//是否是收藏过来的数据
var isTipShowing:Boolean = false,//knowLedge是否展示完毕
)
/**
* 表示知识信息项的数据类。
*
* @property title 知识项的标题。
* @property type 知识项的类型(例如,信息、文献、社区问答)。
* @property url 知识项的 URL。
* @property author 知识项的作者,可能为 NULL。
*/
data class AIKnowledgeInfo(
val title: String? = null, // 标题
val type: String? = null,// 类型
val url: String? = null, // URL
var content: String? = null,
val magazineId: Int? = null,//
val parentId: Int? = null,//
val answerId: Int? = null,//
val userId: Int? = null,
val productId:Int?=null,
val extras:Int?=null,
)
data class AIFeedBackBean(var id: Int? = null, var name: String? = null, var isSelect: Boolean = false)
/**
* 表示匹配到的知识项的数据类(仅供展示)。
*
* @property title 匹配到的知识项的标题。
*/
data class AIMatchKnowledge(
val title: String // 标题
)
data class AiCollectBean(var collectStatus: String? = null)
}
//具体呈现的adapter
import android.app.Activity
import android.content.Context
import android.graphics.Color
import android.graphics.drawable.Animatable
import android.graphics.drawable.Drawable
import android.text.Html
import android.text.TextUtils
import android.util.DisplayMetrics
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.NonNull
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.RequestBuilder
import com.bumptech.glide.RequestManager
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target
import com.google.gson.Gson
import com.mohism.medtion.R
import com.mohism.medtion.bean.BaseBean
import com.mohism.medtion.constant.ConstantObj
import com.mohism.medtion.databinding.AiItemBinding
import com.mohism.medtion.databinding.AiItemTopBinding
import com.mohism.medtion.ext.gone
import com.mohism.medtion.ext.visible
import com.mohism.medtion.ui.ai.AiWSManager
import com.mohism.medtion.ui.ai.util.AIGrammarLocator
import com.mohism.medtion.ui.ai.util.VerticalSpaceItemDecoration
import com.mohism.medtion.ui.ai.view.AIBottomSheetFeedBackFragment
import com.mohism.medtion.ui.home.search.SearchAllActivity
import com.mohism.medtion.ui.homenew.widget.ForegroundActivityManager
import com.mohism.medtion.ui.rtc.view.BIMMessageRecyclerView
import com.mohism.medtion.util.AnalysysAgentUtil
import com.mohism.medtion.util.ShareUtil
import com.mohism.medtion.util.SharedPreferencesManager
import com.mohism.medtion.util.TagViewUtil
import com.mohism.medtion.util.ToolUtil
import io.noties.markwon.Markwon
import io.noties.markwon.image.AsyncDrawable
import io.noties.markwon.image.glide.GlideImagesPlugin
import io.noties.markwon.image.glide.GlideImagesPlugin.GlideStore
import io.noties.markwon.syntax.Prism4jThemeDefault
import io.noties.markwon.syntax.SyntaxHighlightPlugin
import io.noties.prism4j.Prism4j
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
open class AIAdapter(var context: Context, private val listBeans: List<AiWSManager.AIChatBean>, val recyclerView: BIMMessageRecyclerView) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private val inflater: LayoutInflater = LayoutInflater.from(context)
protected var spManager: SharedPreferencesManager = SharedPreferencesManager.getInstance(context)
protected var ds: DisplayMetrics = context.resources.displayMetrics
val prism4j = Prism4j(AIGrammarLocator())
private val markdown = Markwon.builder(context).usePlugin(GlideImagesPlugin.create(GifGlideStore(Glide.with(context))))
.usePlugin(SyntaxHighlightPlugin.create(prism4j, Prism4jThemeDefault.create(Color.parseColor("#f6f6f6")))).build()
private class GifGlideStore(requestManager: RequestManager) : GlideStore {
private val requestManager: RequestManager
init {
this.requestManager = requestManager
}
@NonNull
override fun load(@NonNull drawable: AsyncDrawable): RequestBuilder<Drawable> {
// NB! Strange behaviour: First time a resource is requested - it fails, the next time it loads fine
val destination: String = drawable.destination
return requestManager // it is enough to call this (in order to load GIF and non-GIF)
.asDrawable().addListener(object : RequestListener<Drawable?> {
override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Drawable?>?, isFirstResource: Boolean): Boolean {
return false
}
override fun onResourceReady(
resource: Drawable?, model: Any?, target: Target<Drawable?>?, dataSource: com.bumptech.glide.load.DataSource?, isFirstResource: Boolean
): Boolean {
// we must start GIF animation manually
if (resource is Animatable) {
(resource as Animatable).start()
}
return false
}
}).load(destination)
}
override fun cancel(target: Target<*>) {
requestManager.clear(target)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return if (viewType == TYPE_DEFAULT) {
DefaultViewHolder(AiItemTopBinding.inflate(inflater, parent, false))
} else {
ChatViewHolder(AiItemBinding.inflate(inflater, parent, false))
}
}
inner class DefaultViewHolder(val binding: AiItemTopBinding) : RecyclerView.ViewHolder(binding.root)
inner class ChatViewHolder(val binding: AiItemBinding) : RecyclerView.ViewHolder(binding.root)
override fun getItemViewType(position: Int): Int {
return if (listBeans[position].isDefaultChat) {
TYPE_DEFAULT
} else {
TYPE_CHAT
}
}
companion object {
const val TYPE_DEFAULT = 0
const val TYPE_CHAT = 1
}
var onAiCallBackListener: OnAiCallBackListener? = null
interface OnAiCallBackListener {
fun onStart(chatId: String?)
fun onIng(chatId: String?)
fun onNewQuestion(question: String)
fun saveChat(chatId: String?)
fun onStop()
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val aIChatBean = listBeans[position]
if (!aIChatBean.isDefaultChat) {
holder as ChatViewHolder
holder.binding.apply {
updateView(holder.itemView, aIChatBean)
getQuestListOther(holder.itemView, aIChatBean)
}
}
}
override fun getItemCount(): Int {
return listBeans.size
}
private val markdownLoading = """

"""
val regex = Regex("```")
private fun getRecycleLastView(): View? {
val lastItemPosition = recyclerView.adapter?.itemCount
if (lastItemPosition!! > 0) {
val viewHolder = recyclerView.findViewHolderForAdapterPosition(0)
return viewHolder?.itemView
}
return null
}
//
var currentDisplayedText = ""
var intervalTime = 20 // 1秒
suspend fun startDisplayingKnowledge() {
var isGo = true
while ((isFinish() || isGo) && listBeans[0].interrupt == "F") {
getRecycleLastView()?.apply {
try {
val binding = AiItemBinding.bind(this)
binding.apply {
val targetText = listBeans[0].useAnswer ?: ""
if (!TextUtils.isEmpty(targetText)) {
//执行完毕后调用内容线程
withContext(Dispatchers.Main) {
tvAiTitle.gone()
}
if (currentDisplayedText.length < targetText.length) {
isGo = true
currentDisplayedText += targetText[currentDisplayedText.length]
withContext(Dispatchers.Main) {
// 使用 Regex 查找包含 "```" 的个数 当持续输出代码块内容时 去除尾部追加的loading状态
val matchCount = regex.findAll(currentDisplayedText).count()
if (matchCount % 2 == 1) {
markdown.setMarkdown(tvAiContent, currentDisplayedText)
} else {
markdown.setMarkdown(tvAiContent, currentDisplayedText + markdownLoading)
}
tvAiContent.visible()
}
// 根据剩余内容量调整等待时间
val remainingChars = targetText.length - currentDisplayedText.length
val delayTime = if (remainingChars > 50) 5L else 20L
delay(delayTime) // 每个字符输出后等待相应的毫秒
}
if (!TextUtils.isEmpty(listBeans[0].answerFlag) && listBeans[0].answerFlag == "end" && currentDisplayedText.length == targetText.length) {
isGo = false
withContext(Dispatchers.Main) {
// 移除尾部追加的markdownContent
markdown.setMarkdown(tvAiContent, currentDisplayedText.replace(markdownLoading, ""))
if (currentDisplayedText.contains("您好,我是脑医汇")) {
llyBottom.gone()
llySave.gone()
llyShare.gone()
llyMenu.gone()
llyLine.gone()
tvTip.gone()
tvAiTitle.gone()
onAiCallBackListener?.saveChat(listBeans[0].chatId)
} else {
llyBottom.visible()
llySave.visible()
llyShare.visible()
llyMenu.visible()
llyLine.visible()
tvTip.visible()
llyBody.setBackgroundResource(R.drawable.ai_text_bg_off)
if (listBeans[0].relatedQuestions.size > 0) {
llyOther.visible()
} else {
llyOther.gone()
}
onAiCallBackListener?.saveChat(listBeans[0].chatId)
doEndView(listBeans[0], binding)
}
}
}
delay(20)
} else {
withContext(Dispatchers.Main) {
tvAiTitle.visible()
tvAiContent.gone()
if (intervalTime == 0) {
tvAiTitle.text = "答案生成中…"
} else {
tvAiTitle.text = Html.fromHtml("正在获取资料,答案预计在<font color='#0581ce'>$intervalTime 秒</font>内生成…", Html.FROM_HTML_MODE_LEGACY)
intervalTime--
}
}
delay(1000)//每秒执行一次
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}
private fun isFinish() = !listBeans[0].saveStatus
fun updateLastView(aIChatBean: AiWSManager.AIChatBean?) {
getRecycleLastView()?.apply {
val binding = AiItemBinding.bind(this)
binding.apply {
llyBottom.gone()
llyKnowLedgeInfo.gone()
tvAiContent.gone()
tvMore.gone()
viewMoreLine.gone()
llyTag.gone()
llyReTry.gone()
llyMenu.gone()
llySave.gone()
llyShare.gone()
llyOther.gone()
llyLine.gone()
tvTip.gone()
llyBody.setBackgroundResource(0)
ToolUtil.loadImg(context, spManager.getString(ConstantObj.AVATARADRESS), imgUserPhoto, R.drawable.ai_user_default, (24 * ds.density).toInt(), (24 * ds.density).toInt())
aIChatBean?.apply {
tvQuestion.text = aIChatBean.question
if (!TextUtils.isEmpty(aIChatBean.answerFlag)) {
when (aIChatBean.answerFlag) {
"start" -> {
onAiCallBackListener?.onStart(aIChatBean.chatId)
tvAiContent.text = ""
}
"ing" -> {
tvAiTitle.gone()
tvAiContent.visible()
onAiCallBackListener?.onIng(aIChatBean.chatId)
}
"end" -> {
tvAiContent.visible()
}
}
} else {
tvAiTitle.visible()
}
stopViewInit(binding, aIChatBean)
setListener(binding, aIChatBean)
}
}
}
}
private fun updateView(view: View?, aIChatBean: AiWSManager.AIChatBean?) {
view?.let {
val binding = AiItemBinding.bind(view)
binding.apply {
llyBottom.gone()
llyKnowLedgeInfo.gone()
tvAiContent.gone()
tvMore.gone()
viewMoreLine.gone()
llyTag.gone()
llyReTry.gone()
llyMenu.gone()
llySave.gone()
llyShare.gone()
llyOther.gone()
llyLine.visible()
tvTip.gone()
llyBody.setBackgroundResource(R.drawable.ai_text_bg_off)
ToolUtil.loadImg(context, spManager.getString(ConstantObj.AVATARADRESS), imgUserPhoto, R.drawable.ai_user_default, (24 * ds.density).toInt(), (24 * ds.density).toInt())
aIChatBean?.apply {
tvQuestion.text = aIChatBean.question
if (!TextUtils.isEmpty(aIChatBean.answerFlag)) {
when (aIChatBean.answerFlag) {
"start" -> {
if (!TextUtils.isEmpty(aIChatBean.useAnswer)) {
aIChatBean.useAnswer?.let { markdown.setMarkdown(tvAiContent, it) }
tvAiContent.visible()
}
}
"ing" -> {
if (!TextUtils.isEmpty(aIChatBean.useAnswer)) {
aIChatBean.useAnswer?.let { markdown.setMarkdown(tvAiContent, it) }
tvAiContent.visible()
}
}
"end" -> {
if (aIChatBean.answer?.contains("您好,我是脑医汇") == true || aIChatBean.useAnswer?.contains("您好,我是脑医汇") == true) {
llyBottom.gone()
llySave.gone()
llyShare.gone()
llyMenu.gone()
llyLine.gone()
tvTip.gone()
tvAiTitle.gone()
if (!TextUtils.isEmpty(aIChatBean.useAnswer)) {
aIChatBean.useAnswer?.let { markdown.setMarkdown(tvAiContent, it) }
tvAiContent.visible()
}
} else {
if (aIChatBean.saveStatus || aIChatBean.isCollect) {
llyBottom.visible()
llySave.visible()
llyShare.visible()
llyMenu.visible()
tvTip.visible()
doEndView(
aIChatBean, binding
)
if (!TextUtils.isEmpty(aIChatBean.useAnswer)) {
aIChatBean.useAnswer?.let { markdown.setMarkdown(tvAiContent, it) }
tvAiContent.visible()
}
} else {
tvTip.gone()
llyBottom.gone()
llySave.gone()
llyShare.gone()
llyMenu.gone()
}
}
}
}
} else {
tvAiTitle.gone()
}
stopViewInit(binding, aIChatBean)
setListener(binding, aIChatBean)
}
}
}
}
private fun stopViewInit(binding: AiItemBinding, aIChatBean: AiWSManager.AIChatBean) {
binding.apply {
if (aIChatBean.interrupt == "T" || aIChatBean.interrupt == "S") {
//去除markdown
aIChatBean.useAnswer?.replace(markdownLoading, "")?.let { markdown.setMarkdown(tvAiContent, it) }
tvPause.text = if (aIChatBean.interrupt == "T") "(用户暂停)" else "(连接已断开)"
llyBottom.gone()
llyMenu.visible()
llyReTry.visible()
llyZan.visible()
tvPause.visible()
llySave.gone()
llyShare.gone()
llyKnowLedgeInfo.gone()
tvAiTitle.gone()
tvTip.gone()
llyBody.setBackgroundResource(R.drawable.ai_text_bg_off)
} else {
llyReTry.gone()
tvPause.gone()
}
}
}
private fun setListener(binding: AiItemBinding, aIChatBean: AiWSManager.AIChatBean?) {
binding.apply {
llyReTry.setOnClickListener {
onAiCallBackListener?.onNewQuestion(aIChatBean?.question.toString())
}
llyShare.setOnClickListener {
ShareUtil.shareDefult(
context as Activity,
ConstantObj.SHARE_URL + AiWSManager.URL_SHARE_URL + aIChatBean?.chatId,
AiWSManager.URL_SHARE_TITLE + aIChatBean?.question,
AiWSManager.URL_SHARE_DES,
R.mipmap.ai_icon_share
)
}
llySave.setOnClickListener {
if (aIChatBean != null) {
collectAiQa(aIChatBean, imgSave)
}
}
llyZan.setOnClickListener {
if (aIChatBean?.feedback == "F") {
AIBottomSheetFeedBackFragment.showRtcBottomSheet(context as Activity, aIChatBean, imgZan)
} else {
if (aIChatBean != null) {
cancelSaveFeedback(aIChatBean, imgZan)
}
}
}
tvSendCommunity.setOnClickListener {
ForegroundActivityManager.getInstance().getCurrentActivity()?.let { it1 ->
TagViewUtil.createCommunity(
it1,
null,
"",
null,
null,
null,
AnalysysAgentUtil.AnalysysAgentType.AI_PAGE_NAME.AnalyName + "-" + aIChatBean?.question,
defaultQuestion = aIChatBean?.question.toString(), btnName = "发布问题"
)
}
}
}
}
private fun doEndView(
aIChatBean: AiWSManager.AIChatBean, binding: AiItemBinding
) {
//输出结束
binding.apply {
if (aIChatBean.knowledgeInfo.isNotEmpty()) {
llyKnowLedgeInfo.visible()
recyclerView.layoutManager = LinearLayoutManager(context)
val useKnowledgeInfo: MutableList<AiWSManager.AIKnowledgeInfo> = ArrayList() // 知识信息列表
val infoAdapter = AIInfoAdapter(context, useKnowledgeInfo, aIChatBean.question)
recyclerView.adapter = infoAdapter
if (aIChatBean.isOpenMore) {
useKnowledgeInfo.clear()
useKnowledgeInfo.addAll(aIChatBean.knowledgeInfo)
infoAdapter.notifyDataSetChanged()
tvMore.text = "收起"
tvMore.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ai_more_up, 0)
} else {
useKnowledgeInfo.clear()
useKnowledgeInfo.addAll(aIChatBean.knowledgeInfo.take(3).toMutableList())
infoAdapter.notifyDataSetChanged()
tvMore.text = "展开全部"
tvMore.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ai_more_down, 0)
}
if (aIChatBean.knowledgeInfo.size > 3) {
tvMore.visible()
viewMoreLine.visible()
tvMore.setOnClickListener {
if (aIChatBean.isOpenMore) {
aIChatBean.isOpenMore = false
useKnowledgeInfo.clear()
useKnowledgeInfo.addAll(aIChatBean.knowledgeInfo.take(3).toMutableList())
infoAdapter.notifyDataSetChanged()
tvMore.text = "展开全部"
tvMore.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ai_more_down, 0)
AnalysysAgentUtil.instance.btnClick(context, "AI问答", "参考资料-收起")
} else {
aIChatBean.isOpenMore = true
useKnowledgeInfo.clear()
useKnowledgeInfo.addAll(aIChatBean.knowledgeInfo)
infoAdapter.notifyDataSetChanged()
tvMore.text = "收起"
tvMore.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ai_more_up, 0)
AnalysysAgentUtil.instance.btnClick(context, "AI问答", "参考资料-展开全部")
}
}
}
if (recyclerView.itemDecorationCount == 0) {
recyclerView.addItemDecoration(VerticalSpaceItemDecoration((ds.density * 12).toInt()))
}
}
if (aIChatBean.keywords.isNotEmpty()) {
llyTag.visible()
floatTag.removeAllViews()
aIChatBean.keywords.take(3).forEach { keyWords ->
floatTag.addView(TextView(context).apply {
text = keyWords
textSize = 14f
maxLines = 1
ellipsize = TextUtils.TruncateAt.END
setTextColor(Color.parseColor("#111111"))
setBackgroundResource(R.drawable.ai_tag_key)
setPadding((ds.density * 8).toInt(), (ds.density * 4).toInt(), (ds.density * 8).toInt(), (ds.density * 4).toInt())
setOnClickListener {
onAiCallBackListener?.onStop()
SearchAllActivity.startSearchAllActivitySearch(context, keyWords)
AnalysysAgentUtil.instance.btnClick(context, "AI问答", "关键词-$keyWords")
}
})
}
}
if (aIChatBean.collect == "F") {
imgSave.setImageResource(R.drawable.ai_save)
} else {
imgSave.setImageResource(R.drawable.ai_save_on)
}
if (aIChatBean.feedback == "F") {
imgZan.setImageResource(R.drawable.ai_zan)
} else {
imgZan.setImageResource(R.drawable.ai_zan_on)
}
}
}
private fun cancelSaveFeedback(aIChatBean: AiWSManager.AIChatBean, imgZan: ImageView) {
AiWSManager.ins().cancelFeedback(context as Activity, HashMap<String, String?>().apply {
put("userId", spManager.getString(ConstantObj.USERID))
put("chatId", aIChatBean.chatId)
}, object : AiWSManager.AiClientCallback<Boolean> {
override fun onSuccess(result: Boolean?) {
imgZan.setImageResource(R.drawable.ai_zan)
aIChatBean.feedback = "F"
}
override fun onError(code: Int, msg: String?) {
}
})
}
}