Jelajahi Sumber

[feat]键盘插件,AI键盘,对接Flutter的超会回、超会说、开场白的API

hezihao 8 bulan lalu
induk
melakukan
f579107bc5

+ 15 - 9
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/component/child/impl/AiChatComponent.kt

@@ -88,7 +88,6 @@ class AiChatComponent @JvmOverloads constructor(
         }
 
         setupViewModel()
-        setData()
     }
 
     override fun getComponentInterfaceClazz(): Class<IAiChatComponent> {
@@ -107,6 +106,12 @@ class AiChatComponent @JvmOverloads constructor(
      * 配置ViewModel
      */
     private fun setupViewModel() {
+        // 监听Ai生成列表的更新
+        KeyboardHolder.getKeyboardService()?.run {
+            getKeyboardViewModel().aiChatListResult.observe(getLifecycleOwner()) {
+                loadAiList(it)
+            }
+        }
     }
 
     override fun onComponentShow() {
@@ -122,19 +127,20 @@ class AiChatComponent @JvmOverloads constructor(
     }
 
     /**
-     * 设置数据
+     * 加载Ai内容列表
      */
     @SuppressLint("NotifyDataSetChanged")
-    private fun setData() {
+    private fun loadAiList(list: List<String>) {
+        mListItems.clear()
         mListItems.apply {
             // 空占位条目
             add(EmptyPlaceholderModel(ConvertUtils.dp2px(40f)))
-            // 生成内容条目
-            add(AiChatModel("巴拉巴拉小魔仙?呵,这种无聊的东西,以后不准再提。", true))
-            add(AiChatModel("巴拉巴拉小魔仙?呵,这种无聊的东西"))
-            add(AiChatModel("巴拉巴拉小魔仙?"))
-            add(AiChatModel("巴拉巴拉小魔仙,好看好看"))
-            add(AiChatModel("巴拉巴拉小魔仙,什么呀,没听过"))
+            // 添加生成内容条目
+            addAll(list.map {
+                AiChatModel(
+                    text = it
+                )
+            }.toList())
         }
         mListAdapter.notifyDataSetChanged()
     }

+ 81 - 3
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/component/child/impl/AiKeyboardCommonPanelComponent.kt

@@ -89,13 +89,13 @@ class AiKeyboardCommonPanelComponent @JvmOverloads constructor(
                     if (it.isVip) {
                         // 检查是否VIP
                         UserInfoHelper.checkVip {
-                            controlAiChatPageShowing(true)
+                            handleItemClickLogic(it)
                             LogUtil.d("Ai键盘,已经是VIP,打开Ai内容面板")
                         }
                     } else {
                         // 不需要VIP,直接打开
-                        controlAiChatPageShowing(true)
-                        LogUtil.d("Ai键盘,不需要VIP,直接打开Ai内容面板")
+                        handleItemClickLogic(it)
+                        LogUtil.d("Ai键盘,不需要VIP,直接进行下一步")
                     }
                 })
             }
@@ -195,6 +195,13 @@ class AiKeyboardCommonPanelComponent @JvmOverloads constructor(
                     vKeyList.setVisible()
                 }
             }
+            // 监听生成的Ai内容
+            getKeyboardViewModel().chatSuperReplyResult.observe(getLifecycleOwner()) {
+                // 生成的内容,直接添加到输入框
+                KeyboardHolder.getKeyboardService()?.asInputMethodService()?.let { service ->
+                    InputMethodUtil.inputText(service.currentInputConnection, it)
+                }
+            }
         }
     }
 
@@ -218,6 +225,8 @@ class AiKeyboardCommonPanelComponent @JvmOverloads constructor(
                         AiKeyboardKeyModel(
                             // 显示的文本 = 表情 + 名字
                             text = "${it.emoji} ${it.name}",
+                            // 拓展字段,存储人设Id
+                            payload = it.id,
                             // 是否需要VIP
                             isVip = it.isVip
                         )
@@ -243,4 +252,73 @@ class AiKeyboardCommonPanelComponent @JvmOverloads constructor(
             vCommonRouteComponent.routeChildComponent(FakeComponent::class.java, true)
         }
     }
+
+    /**
+     * 处理条目点击逻辑
+     */
+    private fun handleItemClickLogic(item: AiKeyboardKeyModel) {
+        // 判断帮助模式
+        val helpMode = KeyboardHolder.getKeyboardService()
+            ?.getKeyboardViewModel()?.helpMode?.value
+        if (helpMode == null) {
+            return
+        }
+
+        LogUtil.d("当前帮助模式 => $helpMode")
+
+        // 获取剪切板内容
+        val clipboardText =
+            KeyboardHolder.getKeyboardService()?.getKeyboardViewModel()?.userClipboardData?.value
+                ?: ""
+        if (clipboardText.isBlank()) {
+            ToastUtils.showShort(context.resources.getString(R.string.no_clipboard_data_tip))
+            return
+        }
+
+        if (HelpMode.HELP_CHAT == helpMode) {
+            // 帮聊只会生成一条内容,直接添加到输入框
+            doChatSuperReply(item.payload)
+        } else {
+            // 教你说和开场白,有多条数据,需要添加到列表中展示
+            if (HelpMode.TEACH_YOU_SAY == helpMode) {
+                doChatSuperSpeak(item.payload)
+                controlAiChatPageShowing(true)
+            } else if (HelpMode.OPEN_REMARKS == helpMode) {
+                doChatPrologue(item.payload)
+                controlAiChatPageShowing(true)
+            }
+        }
+    }
+
+    /**
+     * 生成<帮聊>的内容
+     *
+     * @param characterId 人设Id
+     */
+    private fun doChatSuperReply(characterId: String) {
+        val viewModel = KeyboardHolder.getKeyboardService()?.getKeyboardViewModel()
+        viewModel?.chatSuperReply(characterId, onFail = {
+            ToastUtils.showShort(it)
+        })
+    }
+
+    /**
+     * 生成<教你说>的内容
+     */
+    private fun doChatSuperSpeak(characterId: String) {
+        val viewModel = KeyboardHolder.getKeyboardService()?.getKeyboardViewModel()
+        viewModel?.chatSuperSpeak(characterId, onFail = {
+            ToastUtils.showShort(it)
+        })
+    }
+
+    /**
+     * 生成<开场白>的内容
+     */
+    private fun doChatPrologue(name: String) {
+        val viewModel = KeyboardHolder.getKeyboardService()?.getKeyboardViewModel()
+        viewModel?.chatPrologue(name, onFail = {
+            ToastUtils.showShort(it)
+        })
+    }
 }

+ 76 - 4
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/component/child/impl/AiKeyboardProloguePanelComponent.kt

@@ -13,6 +13,7 @@ import com.atmob.keyboard_android.component.child.IAiChatComponent
 import com.atmob.keyboard_android.component.child.IAiKeyboardProloguePanelComponent
 import com.atmob.keyboard_android.component.item.AiKeyboardKeyViewBinder
 import com.atmob.keyboard_android.constant.Constants
+import com.atmob.keyboard_android.enums.HelpMode
 import com.atmob.keyboard_android.ext.click
 import com.atmob.keyboard_android.ext.setGone
 import com.atmob.keyboard_android.ext.setVisible
@@ -172,13 +173,13 @@ class AiKeyboardProloguePanelComponent @JvmOverloads constructor(
                     if (it.isVip) {
                         // 检查是否VIP
                         UserInfoHelper.checkVip {
-                            controlAiChatPageShowing(true)
-                            LogUtil.d("Ai键盘 => 开场白类型-键盘面板,已经是VIP,打开Ai内容面板")
+                            handleItemClickLogic(it)
+                            LogUtil.d("Ai键盘,已经是VIP,打开Ai内容面板")
                         }
                     } else {
                         // 不需要VIP,直接打开
-                        controlAiChatPageShowing(true)
-                        LogUtil.d("Ai键盘 => 开场白类型-键盘面板,不需要VIP,直接打开Ai内容面板")
+                        handleItemClickLogic(it)
+                        LogUtil.d("Ai键盘,不需要VIP,直接进行下一步")
                     }
                 })
             }
@@ -307,6 +308,8 @@ class AiKeyboardProloguePanelComponent @JvmOverloads constructor(
             AiKeyboardKeyModel(
                 // 显示的文本 = 名字
                 text = it.name,
+                // 拓展字段,存储话题名字
+                payload = it.name,
                 // 是否需要VIP
                 isVip = false
             )
@@ -328,4 +331,73 @@ class AiKeyboardProloguePanelComponent @JvmOverloads constructor(
             vPrologueRouteComponent.routeChildComponent(FakeComponent::class.java, true)
         }
     }
+
+    /**
+     * 处理条目点击逻辑
+     */
+    private fun handleItemClickLogic(item: AiKeyboardKeyModel) {
+        // 判断帮助模式
+        val helpMode = KeyboardHolder.getKeyboardService()
+            ?.getKeyboardViewModel()?.helpMode?.value
+        if (helpMode == null) {
+            return
+        }
+
+        LogUtil.d("当前帮助模式 => $helpMode")
+
+        // 获取剪切板内容
+        val clipboardText =
+            KeyboardHolder.getKeyboardService()?.getKeyboardViewModel()?.userClipboardData?.value
+                ?: ""
+        if (clipboardText.isBlank()) {
+            ToastUtils.showShort(context.resources.getString(R.string.no_clipboard_data_tip))
+            return
+        }
+
+        if (HelpMode.HELP_CHAT == helpMode) {
+            // 帮聊只会生成一条内容,直接添加到输入框
+            doChatSuperReply(item.payload)
+        } else {
+            // 教你说和开场白,有多条数据,需要添加到列表中展示
+            if (HelpMode.TEACH_YOU_SAY == helpMode) {
+                doChatSuperSpeak(item.payload)
+                controlAiChatPageShowing(true)
+            } else if (HelpMode.OPEN_REMARKS == helpMode) {
+                doChatPrologue(item.payload)
+                controlAiChatPageShowing(true)
+            }
+        }
+    }
+
+    /**
+     * 生成<帮聊>的内容
+     *
+     * @param characterId 人设Id
+     */
+    private fun doChatSuperReply(characterId: String) {
+        val viewModel = KeyboardHolder.getKeyboardService()?.getKeyboardViewModel()
+        viewModel?.chatSuperReply(characterId, onFail = {
+            ToastUtils.showShort(it)
+        })
+    }
+
+    /**
+     * 生成<教你说>的内容
+     */
+    private fun doChatSuperSpeak(characterId: String) {
+        val viewModel = KeyboardHolder.getKeyboardService()?.getKeyboardViewModel()
+        viewModel?.chatSuperSpeak(characterId, onFail = {
+            ToastUtils.showShort(it)
+        })
+    }
+
+    /**
+     * 生成<开场白>的内容
+     */
+    private fun doChatPrologue(name: String) {
+        val viewModel = KeyboardHolder.getKeyboardService()?.getKeyboardViewModel()
+        viewModel?.chatPrologue(name, onFail = {
+            ToastUtils.showShort(it)
+        })
+    }
 }

+ 6 - 6
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/enums/FlutterMethod.kt

@@ -40,17 +40,17 @@ enum class FlutterMethod(val methodName: String) {
     GET_PROLOGUE_LIST("getPrologueList"),
 
     /**
-     * 超会回
+     * AI聊天-超会回
      */
-    SUPER_REPLY("superReply"),
+    CHAT_SUPER_REPLY("chatSuperReply"),
 
     /**
-     * 超会说
+     * AI聊天-超会说
      */
-    SUPER_SPEAK("superSpeak"),
+    CHAT_SUPER_SPEAK("chatSuperSpeak"),
 
     /**
-     * 开场白
+     * AI聊天-开场白
      */
-    PROLOGUE("prologue"),
+    CHAT_PROLOGUE("chatPrologue"),
 }

+ 4 - 0
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/model/AiKeyboardKeyModel.kt

@@ -11,6 +11,10 @@ data class AiKeyboardKeyModel(
      */
     val text: String,
     /**
+     * 开场白模式下为话题的name,其他模式下为人设Id
+     */
+    val payload: String,
+    /**
      * 是否需要VIP
      */
     val isVip: Boolean = false

+ 38 - 0
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/mvvm/repository/KeyboardRepository.kt

@@ -2,10 +2,15 @@ package com.atmob.keyboard_android.mvvm.repository
 
 import com.atmob.keyboard_android.util.bridge.FlutterBridgeManager
 import com.atmob.keyboard_android.util.bridge.model.base.EmptyResp
+import com.atmob.keyboard_android.util.bridge.model.req.SuperReplyReq
+import com.atmob.keyboard_android.util.bridge.model.req.SuperSpeakReq
 import com.atmob.keyboard_android.util.bridge.model.resp.CharacterListResp
 import com.atmob.keyboard_android.util.bridge.model.resp.CurrentKeyboardIdResp
 import com.atmob.keyboard_android.util.bridge.model.resp.KeyboardListResp
 import com.atmob.keyboard_android.util.bridge.model.resp.PrologueListResp
+import com.atmob.keyboard_android.util.bridge.model.resp.PrologueResp
+import com.atmob.keyboard_android.util.bridge.model.resp.SuperReplyResp
+import com.atmob.keyboard_android.util.bridge.model.resp.SuperSpeakResp
 
 /**
  * 键盘Repository
@@ -86,4 +91,37 @@ class KeyboardRepository {
     ) {
         FlutterBridgeManager.getPrologueList(onSuccess, onFail)
     }
+
+    /**
+     * AI聊天-超会回(帮聊)
+     */
+    fun chatSuperReply(
+        req: SuperReplyReq,
+        onSuccess: (SuperReplyResp) -> Unit,
+        onFail: (String) -> Unit
+    ) {
+        FlutterBridgeManager.chatSuperReply(req, onSuccess, onFail)
+    }
+
+    /**
+     * AI聊天-超会说(教你说)
+     */
+    fun chatSuperSpeak(
+        req: SuperSpeakReq,
+        onSuccess: (SuperSpeakResp) -> Unit,
+        onFail: (String) -> Unit
+    ) {
+        FlutterBridgeManager.chatSuperSpeak(req, onSuccess, onFail)
+    }
+
+    /**
+     * AI聊天-开场白
+     */
+    fun chatPrologue(
+        name: String,
+        onSuccess: (PrologueResp) -> Unit,
+        onFail: (String) -> Unit
+    ) {
+        FlutterBridgeManager.chatPrologue(name, onSuccess, onFail)
+    }
 }

+ 74 - 1
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/mvvm/viewmodel/KeyboardViewModel.kt

@@ -8,6 +8,8 @@ import com.atmob.keyboard_android.enums.KeyboardGlobalType
 import com.atmob.keyboard_android.model.KeyboardSelectModel
 import com.atmob.keyboard_android.mvvm.repository.KeyboardRepository
 import com.atmob.keyboard_android.util.bridge.model.base.EmptyResp
+import com.atmob.keyboard_android.util.bridge.model.req.SuperReplyReq
+import com.atmob.keyboard_android.util.bridge.model.req.SuperSpeakReq
 import com.atmob.keyboard_android.util.bridge.model.resp.CharacterListResp
 import com.atmob.keyboard_android.util.bridge.model.resp.PrologueListResp.PrologueListItem
 import com.atmob.keyboard_android.util.bridge.model.resp.PrologueListResp.Topic
@@ -85,13 +87,25 @@ class KeyboardViewModel : ViewModel() {
     val prologueList: LiveData<List<PrologueListItem>> = _prologueList
 
     /**
+     * AI聊天-超会回(帮聊)结果
+     */
+    private val _chatSuperReplyResult = MutableLiveData<String>()
+    val chatSuperReplyResult: LiveData<String> = _chatSuperReplyResult
+
+    /**
+     * AI聊天-超会说或开场白(教你说)结果
+     */
+    private val _aiChatListResult = MutableLiveData<List<String>>()
+    val aiChatListResult: LiveData<List<String>> = _aiChatListResult
+
+    /**
      * 用户的剪切板数据
      */
     private val _userClipboardData = MutableLiveData("")
     val userClipboardData: LiveData<String> = _userClipboardData
 
     /**
-     * 键盘列表数据
+     * 键盘选择列表数据
      */
     private val _keyboardList = MutableLiveData<List<KeyboardSelectModel>>()
     val keyboardList: LiveData<List<KeyboardSelectModel>> = _keyboardList
@@ -265,4 +279,63 @@ class KeyboardViewModel : ViewModel() {
         // 该Tab下的开场白列表
         return tabPrologues?.topics ?: listOf()
     }
+
+    /**
+     * AI聊天-超会回(帮聊)
+     *
+     * @param characterId 人设Id
+     */
+    fun chatSuperReply(
+        characterId: String,
+        onFail: (String) -> Unit
+    ) {
+        val req = SuperReplyReq(
+            // 键盘Id
+            keyboardId = _currentKeyboardInfo.value?.id ?: "",
+            // 人设Id
+            characterId = characterId,
+            // 用户粘贴板中的内容
+            content = _userClipboardData.value ?: ""
+        )
+        mKeyboardRepository.chatSuperReply(req, onSuccess = {
+            // 只有一条数据,直接添加到输入框
+            _chatSuperReplyResult.value = it.content
+        }, onFail)
+    }
+
+    /**
+     * AI聊天-超会说(教你说)
+     *
+     * @param characterId 人设Id
+     */
+    fun chatSuperSpeak(
+        characterId: String,
+        onFail: (String) -> Unit
+    ) {
+        val req = SuperSpeakReq(
+            // 键盘Id
+            keyboardId = _currentKeyboardInfo.value?.id ?: "",
+            // 人设Id
+            characterId = characterId,
+            // 用户粘贴板中的内容
+            content = _userClipboardData.value ?: ""
+        )
+        mKeyboardRepository.chatSuperSpeak(req, onSuccess = {
+            // 有多条数据,需要跳转列表页面中显示
+            _aiChatListResult.value = it.list
+        }, onFail)
+    }
+
+    /**
+     * AI聊天-开场白
+     */
+    fun chatPrologue(
+        name: String,
+        onFail: (String) -> Unit
+    ) {
+        mKeyboardRepository.chatPrologue(name, onSuccess = {
+            // 有多条数据,需要跳转列表页面中显示
+            _aiChatListResult.value = it.list
+        }, onFail)
+    }
 }

+ 6 - 6
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/util/bridge/FlutterBridgeManager.kt

@@ -145,13 +145,13 @@ object FlutterBridgeManager : IBridgeApi {
         )
     }
 
-    override fun superReply(
+    override fun chatSuperReply(
         req: SuperReplyReq,
         onSuccess: (SuperReplyResp) -> Unit,
         onFail: (String) -> Unit
     ) {
         mFlutterMethodCaller.callMethod<SuperReplyResp>(
-            FlutterMethod.SUPER_REPLY.methodName,
+            FlutterMethod.CHAT_SUPER_REPLY.methodName,
             args = mapOf(
                 "keyboardId" to req.keyboardId,
                 "characterId" to req.characterId,
@@ -163,13 +163,13 @@ object FlutterBridgeManager : IBridgeApi {
         )
     }
 
-    override fun superSpeak(
+    override fun chatSuperSpeak(
         req: SuperSpeakReq,
         onSuccess: (SuperSpeakResp) -> Unit,
         onFail: (String) -> Unit
     ) {
         mFlutterMethodCaller.callMethod<SuperSpeakResp>(
-            FlutterMethod.SUPER_SPEAK.methodName,
+            FlutterMethod.CHAT_SUPER_SPEAK.methodName,
             args = mapOf(
                 "keyboardId" to req.keyboardId,
                 "characterId" to req.characterId,
@@ -181,13 +181,13 @@ object FlutterBridgeManager : IBridgeApi {
         )
     }
 
-    override fun prologue(
+    override fun chatPrologue(
         name: String,
         onSuccess: (PrologueResp) -> Unit,
         onFail: (String) -> Unit
     ) {
         mFlutterMethodCaller.callMethod<PrologueResp>(
-            FlutterMethod.PROLOGUE.methodName,
+            FlutterMethod.CHAT_PROLOGUE.methodName,
             args = mapOf(
                 "name" to name,
             ),

+ 3 - 3
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/util/bridge/api/AiChatApi.kt

@@ -13,7 +13,7 @@ interface AiChatApi {
     /**
      * 超会回
      */
-    fun superReply(
+    fun chatSuperReply(
         req: SuperReplyReq,
         onSuccess: (SuperReplyResp) -> Unit,
         onFail: (String) -> Unit
@@ -22,7 +22,7 @@ interface AiChatApi {
     /**
      * 超会说
      */
-    fun superSpeak(
+    fun chatSuperSpeak(
         req: SuperSpeakReq,
         onSuccess: (SuperSpeakResp) -> Unit,
         onFail: (String) -> Unit
@@ -31,7 +31,7 @@ interface AiChatApi {
     /**
      * 开场白
      */
-    fun prologue(
+    fun chatPrologue(
         name: String,
         onSuccess: (PrologueResp) -> Unit,
         onFail: (String) -> Unit

+ 1 - 0
plugins/keyboard_android/android/src/main/res/values/string.xml

@@ -28,4 +28,5 @@
     <string name="invitation_script">邀约话术</string>
     <string name="how_to_confess">如何告白</string>
     <string name="promotion_relationship">关系升温</string>
+    <string name="no_clipboard_data_tip">还没有复制内容喔</string>
 </resources>