From a4e41eefb36f5dde23734321631ae8dae8c59c1e Mon Sep 17 00:00:00 2001 From: VitaliyDovbnya Date: Thu, 17 Oct 2024 16:10:06 +0300 Subject: [PATCH] 0.10.0 --- README.md | 8 +- build.gradle | 4 +- gradle/wrapper/gradle-wrapper.properties | 2 +- ui-kit/build.gradle | 46 ++--- ui-kit/src/main/AndroidManifest.xml | 3 +- .../android_ui_kit/QuickBloxUiKit.kt | 166 +++++++++++++++++- .../data/dto/remote/user/RemoteUserDTO.kt | 2 +- .../data/repository/ai/AIRepositoryImpl.kt | 35 +++- .../data/source/ai/AIDataSource.kt | 31 +++- .../data/source/ai/AIDataSourceImpl.kt | 66 ++++++- .../android_ui_kit/domain/Languages.kt | 77 ++++++++ .../domain/entity/UserEntity.kt | 4 +- .../entity/implementation/UserEntityImpl.kt | 6 +- .../domain/repository/AIRepository.kt | 15 +- ...ssistantWithSmartChatAssistantIdUseCase.kt | 137 +++++++++++++++ .../LoadAIRephraseWithApiKeyUseCase.kt | 2 +- ...ranslateWithSmartChatAssistantIdUseCase.kt | 142 +++++++++++++++ .../base/BaseMessageViewHolder.kt | 1 + .../screens/chat/group/GroupChatFragment.kt | 6 +- .../screens/chat/group/GroupChatViewModel.kt | 67 +++++-- .../chat/individual/PrivateChatFragment.kt | 6 +- .../chat/individual/PrivateChatViewModel.kt | 101 +++++++++-- .../domain/entity/UserEntityTest.kt | 4 +- .../domain/usecases/TypingEventUseCaseTest.kt | 1 + .../stub/entity/UserEntityStub.kt | 4 +- 25 files changed, 849 insertions(+), 87 deletions(-) create mode 100644 ui-kit/src/main/java/com/quickblox/android_ui_kit/domain/Languages.kt create mode 100644 ui-kit/src/main/java/com/quickblox/android_ui_kit/domain/usecases/LoadAIAnswerAssistantWithSmartChatAssistantIdUseCase.kt create mode 100644 ui-kit/src/main/java/com/quickblox/android_ui_kit/domain/usecases/LoadAITranslateWithSmartChatAssistantIdUseCase.kt diff --git a/README.md b/README.md index 4263a0f..72c7fa0 100644 --- a/README.md +++ b/README.md @@ -74,11 +74,11 @@ Then need to add implementation of QuickBlox UIKit and QuickBlox SDK to dependen ``` dependencies { - implementation "com.quickblox:android-ui-kit:0.9.0" + implementation "com.quickblox:android-ui-kit:0.10.0" - implementation 'com.quickblox:quickblox-android-sdk-messages:4.1.1' - implementation 'com.quickblox:quickblox-android-sdk-chat:4.1.1' - implementation 'com.quickblox:quickblox-android-sdk-content:4.1.1' + implementation 'com.quickblox:quickblox-android-sdk-messages:4.2.2' + implementation 'com.quickblox:quickblox-android-sdk-chat:4.2.2' + implementation 'com.quickblox:quickblox-android-sdk-content:4.2.2' } ``` diff --git a/build.gradle b/build.gradle index 107663a..b90f65e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { - id 'com.android.library' version '7.2.2' apply false - id 'org.jetbrains.kotlin.android' version '1.8.0' apply false + id 'com.android.library' version '8.2.0' apply false + id 'org.jetbrains.kotlin.android' version '1.8.21' apply false } task clean(type: Delete) { diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3a6d870..5dc1f00 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Mon Oct 17 15:31:17 CEST 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/ui-kit/build.gradle b/ui-kit/build.gradle index 00f4502..6bf2216 100644 --- a/ui-kit/build.gradle +++ b/ui-kit/build.gradle @@ -7,20 +7,19 @@ plugins { } ext { - qbSdkVersion = '4.1.1' + qbSdkVersion = '4.2.2' uiKitVersionCode = 1 - uiKitVersionName = "0.9.0" + uiKitVersionName = "0.10.0" } android { namespace = "com.quickblox.android_ui_kit" - compileSdk 33 - defaultConfig { + compileSdk 34 minSdkVersion 21 - targetSdkVersion 33 + targetSdkVersion 34 versionCode uiKitVersionCode versionName uiKitVersionName @@ -52,12 +51,12 @@ android { } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = '1.8' + jvmTarget = '17' } testOptions { @@ -70,34 +69,35 @@ dependencies { api "com.quickblox:quickblox-android-sdk-messages:$qbSdkVersion" api "com.quickblox:quickblox-android-sdk-chat:$qbSdkVersion" api "com.quickblox:quickblox-android-sdk-content:$qbSdkVersion" + api "com.quickblox:quickblox-android-sdk-ai:$qbSdkVersion" // QuickBlox AI api "com.quickblox:android-ai-answer-assistant:2.0.0" api "com.quickblox:android-ai-translate:2.0.0" api "com.quickblox:android-ai-editing-assistant:2.1.0" - implementation 'androidx.fragment:fragment-ktx:1.5.6' - implementation 'androidx.core:core-ktx:1.9.0' - implementation 'androidx.appcompat:appcompat:1.6.1' - implementation 'com.google.android.material:material:1.8.0' + implementation 'androidx.fragment:fragment-ktx:1.8.2' + implementation 'androidx.core:core-ktx:1.13.1' + implementation 'androidx.appcompat:appcompat:1.7.0' + implementation 'com.google.android.material:material:1.12.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' - implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.1' - implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1' + implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.8.4' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.4' implementation 'android.arch.lifecycle:extensions:1.1.1' // Glide implementation 'com.github.bumptech.glide:glide:4.15.0' // Coroutines and Kotlin Flow - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3" // Tests testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation 'androidx.test.ext:junit:1.2.1' testImplementation "org.mockito.kotlin:mockito-kotlin:4.1.0" - androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' - testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1' + testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3' testImplementation 'org.json:json:20231013' } @@ -106,7 +106,7 @@ kapt { } jacoco { - toolVersion = "0.8.8" + toolVersion = "0.8.12" } publishing { @@ -144,9 +144,9 @@ task jacocoTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest']) { def enabledCSV = false reports { - xml { enabled enabledXML } - html { enabled enabledHTML } - csv { enabled enabledCSV } + xml.required = enabledXML + html.required = enabledHTML + csv.required = enabledCSV } def sourceFile = "${project.projectDir}/src/main/java" diff --git a/ui-kit/src/main/AndroidManifest.xml b/ui-kit/src/main/AndroidManifest.xml index 9d6b931..d281f5e 100644 --- a/ui-kit/src/main/AndroidManifest.xml +++ b/ui-kit/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ - + diff --git a/ui-kit/src/main/java/com/quickblox/android_ui_kit/QuickBloxUiKit.kt b/ui-kit/src/main/java/com/quickblox/android_ui_kit/QuickBloxUiKit.kt index e10d4a1..2686054 100644 --- a/ui-kit/src/main/java/com/quickblox/android_ui_kit/QuickBloxUiKit.kt +++ b/ui-kit/src/main/java/com/quickblox/android_ui_kit/QuickBloxUiKit.kt @@ -38,13 +38,28 @@ object QuickBloxUiKit { private var enabledForward = true private var enabledReply = true + private var answerAssistantSmartChatAssistantId: String = "" + + @Deprecated("This method is deprecated and will be removed in future versions.") private var answerAssistantOpenAIToken: String = "" + + @Deprecated("This method is deprecated and will be removed in future versions.") private var answerAssistantProxyServerURL: String = "" + + @Deprecated("This method is deprecated and will be removed in future versions.") private var answerAssistantMaxRequestTokens: Int = 3000 + + @Deprecated("This method is deprecated and will be removed in future versions.") private var answerAssistantOpenAIModel: AnswerAssistantSettingsImpl.Model = AnswerAssistantSettingsImpl.Model.GPT_3_5_TURBO + + @Deprecated("This method is deprecated and will be removed in future versions.") private var answerAssistantOrganization: String? = null + + @Deprecated("This method is deprecated and will be removed in future versions.") private var answerAssistantMaxResponseTokens: Int? = null + + @Deprecated("This method is deprecated and will be removed in future versions.") private var answerAssistantTemperature: Float = 0.5f private var rephraseOpenAIToken: String = "" @@ -55,13 +70,30 @@ object QuickBloxUiKit { private var rephraseMaxResponseTokens: Int? = null private var rephraseTemperature: Float = 0.5f + private var translateSmartChatAssistantId: String = "" + + @Deprecated("This method is deprecated and will be removed in future versions.") private var translateOpenAIToken: String = "" + + @Deprecated("This method is deprecated and will be removed in future versions.") private var translateProxyServerURL: String = "" + + @Deprecated("This method is deprecated and will be removed in future versions.") private var translateMaxRequestTokens: Int = 3000 + + @Deprecated("This method is deprecated and will be removed in future versions.") private var translateOpenAIModel: TranslateSettingsImpl.Model = GPT_3_5_TURBO + + @Deprecated("This method is deprecated and will be removed in future versions.") private var translateOrganization: String? = null + + @Deprecated("This method is deprecated and will be removed in future versions.") private var translateMaxResponseTokens: Int? = null + + @Deprecated("This method is deprecated and will be removed in future versions.") private var translateTemperature: Float = 0.5f + + @Deprecated("This method is deprecated and will be removed in future versions.") private var translateLanguage: Languages = Languages.ENGLISH private var regexUserName: String? = null @@ -103,6 +135,7 @@ object QuickBloxUiKit { } } + @Deprecated("This method is deprecated and will be removed in future versions.") private fun getDefaultLanguage(): Languages { val systemLanguageName = getSystemLanguageName() var defaultLanguage: Languages @@ -116,14 +149,17 @@ object QuickBloxUiKit { return defaultLanguage } + @Deprecated("This method is deprecated and will be removed in future versions.") fun setupLanguageToAITranslate(language: Languages) { translateLanguage = language } + @Deprecated("This method is deprecated and will be removed in future versions.") fun getAITranslateLanguage(): Languages { return translateLanguage } + @Deprecated("This method is deprecated and will be removed in future versions.") private fun getSystemLanguageName(): String { val defaultLocale: Locale = Locale.getDefault() val englishLocale = Locale("en") @@ -147,21 +183,39 @@ object QuickBloxUiKit { theme = uiKitTheme } - fun enableAIAnswerAssistantWithOpenAIToken(openAIToken: String) { + fun enableAIAnswerAssistantWithSmartChatAssistantId(smartChatAssistantId: String) { + if (smartChatAssistantId.isBlank()) { + throw RuntimeException("The smartChatAssistantId shouldn't be empty") + } + this.answerAssistantSmartChatAssistantId = smartChatAssistantId + this.answerAssistantOpenAIToken = "" + this.answerAssistantProxyServerURL = "" + enabledAIAnswerAssistant = true + } + + @Deprecated( + message = "Use enableAIAnswerAssistantWithSmartChatAssistantId(smartChatAssistantId) instead", + replaceWith = ReplaceWith("enableAIAnswerAssistantWithSmartChatAssistantId(smartChatAssistantId)") + ) fun enableAIAnswerAssistantWithOpenAIToken(openAIToken: String) { if (openAIToken.isBlank()) { throw RuntimeException("The openAIToken shouldn't be empty") } this.answerAssistantOpenAIToken = openAIToken this.answerAssistantProxyServerURL = "" + this.answerAssistantSmartChatAssistantId = "" enabledAIAnswerAssistant = true } - fun enableAIAnswerAssistantWithProxyServer(proxyServerURL: String) { + @Deprecated( + message = "Use enableAIAnswerAssistantWithSmartChatAssistantId(smartChatAssistantId) instead", + replaceWith = ReplaceWith("enableAIAnswerAssistantWithSmartChatAssistantId(smartChatAssistantId)") + ) fun enableAIAnswerAssistantWithProxyServer(proxyServerURL: String) { if (proxyServerURL.isBlank()) { throw RuntimeException("The proxyServerURL shouldn't be empty") } this.answerAssistantOpenAIToken = "" this.answerAssistantProxyServerURL = proxyServerURL + this.answerAssistantSmartChatAssistantId = "" enabledAIAnswerAssistant = true } @@ -169,26 +223,41 @@ object QuickBloxUiKit { enabledAIAnswerAssistant = false } + fun isAIAnswerAssistantEnabledWithSmartChatAssistantId(): Boolean { + val isNotEnabledAIAnswerAssistant = !isEnabledAIAnswerAssistant() + if (isNotEnabledAIAnswerAssistant) { + throw RuntimeException("The AI Answer assistant is disabled") + } + + if (answerAssistantSmartChatAssistantId.isNotBlank() && isExistAITokenAndProxyServer(answerAssistantOpenAIToken, answerAssistantProxyServerURL)) { + throw RuntimeException("Error initialization. There are Smart Chat Assistant Id, Open AI Token and Proxy Server Url. The AI Answer Assistant should be initialized with Smart Chat Assistant Id, Open AI Token or Proxy server") + } + + return answerAssistantSmartChatAssistantId.isNotBlank() + } + + @Deprecated("This method is deprecated and will be removed in future versions.") fun isAIAnswerAssistantEnabledWithOpenAIToken(): Boolean { val isNotEnabledAIAnswerAssistant = !isEnabledAIAnswerAssistant() if (isNotEnabledAIAnswerAssistant) { throw RuntimeException("The AI Answer assistant is disabled") } - if (isExistAITokenAndProxyServer(answerAssistantOpenAIToken, answerAssistantProxyServerURL)) { + if (answerAssistantSmartChatAssistantId.isNotBlank() && isExistAITokenAndProxyServer(answerAssistantOpenAIToken, answerAssistantProxyServerURL)) { throw RuntimeException("Error initialization. There are Open AI Token and Proxy Server Url. The AI Answer Assistant should be initialized with Open AI Token or Proxy server") } return answerAssistantOpenAIToken.isNotBlank() } + @Deprecated("This method is deprecated and will be removed in future versions.") fun isAIAnswerAssistantEnabledWithProxyServer(): Boolean { val isNotEnabledAIAnswerAssistant = !isEnabledAIAnswerAssistant() if (isNotEnabledAIAnswerAssistant) { throw RuntimeException("The AI Answer assistant is disabled") } - if (isExistAITokenAndProxyServer(answerAssistantOpenAIToken, answerAssistantProxyServerURL)) { + if (answerAssistantSmartChatAssistantId.isNotBlank() && isExistAITokenAndProxyServer(answerAssistantOpenAIToken, answerAssistantProxyServerURL)) { throw RuntimeException("Error initialization. There are Open AI Token and Proxy Server Url. The AI Answer Assistant should be initialized with Open AI Token or Proxy server") } @@ -299,60 +368,91 @@ object QuickBloxUiKit { return rephraseProxyServerURL.isNotBlank() } + fun enableAITranslateWithSmartChatAssistantId(smartChatAssistantId: String) { + if (smartChatAssistantId.isBlank()) { + throw RuntimeException("The smartChatAssistantId shouldn't be empty") + } + this.translateSmartChatAssistantId = smartChatAssistantId + this.translateOpenAIToken = "" + this.translateProxyServerURL = "" + enabledAITranslate = true + } + + @Deprecated( + message = "Use enableAITranslateWithSmartChatAssistantId(smartChatAssistantId) instead", + replaceWith = ReplaceWith("enableAITranslateWithSmartChatAssistantId(smartChatAssistantId)") + ) fun enableAITranslateWithOpenAIToken(openAIToken: String) { if (openAIToken.isBlank()) { throw RuntimeException("The openAIToken shouldn't be empty") } this.translateOpenAIToken = openAIToken this.translateProxyServerURL = "" + this.translateSmartChatAssistantId = "" enabledAITranslate = true } + @Deprecated( + message = "Use enableAITranslateWithSmartChatAssistantId(smartChatAssistantId) instead", + replaceWith = ReplaceWith("enableAITranslateWithSmartChatAssistantId(smartChatAssistantId)") + ) fun enableAITranslateWithProxyServer(proxyServerURL: String) { if (proxyServerURL.isBlank()) { throw RuntimeException("The proxyServerURL shouldn't be empty") } this.translateOpenAIToken = "" + this.translateSmartChatAssistantId = "" this.translateProxyServerURL = proxyServerURL enabledAITranslate = true } + @Deprecated("This method is deprecated and will be removed in future versions.") fun setMaxRequestTokensForAITranslate(maxTokens: Int) { translateMaxRequestTokens = maxTokens } + @Deprecated("This method is deprecated and will be removed in future versions.") fun getMaxRequestTokensForAITranslate(): Int { return translateMaxRequestTokens } + @Deprecated("This method is deprecated and will be removed in future versions.") fun setOpenAIModelForAITranslate(openAIModel: TranslateSettingsImpl.Model) { this.translateOpenAIModel = openAIModel } + @Deprecated("This method is deprecated and will be removed in future versions.") + fun getOpenAIModelForAITranslate(): TranslateSettingsImpl.Model { return translateOpenAIModel } + @Deprecated("This method is deprecated and will be removed in future versions.") fun setOrganizationForAITranslate(organization: String) { this.translateOrganization = organization } + @Deprecated("This method is deprecated and will be removed in future versions.") fun getOrganizationForAITranslate(): String? { return translateOrganization } + @Deprecated("This method is deprecated and will be removed in future versions.") fun setMaxResponseTokensForAITranslate(maxTokens: Int) { translateMaxResponseTokens = maxTokens } + @Deprecated("This method is deprecated and will be removed in future versions.") fun getMaxResponseTokensForAITranslate(): Int? { return translateMaxResponseTokens } + @Deprecated("This method is deprecated and will be removed in future versions.") fun setTemperatureForAITranslate(temperature: Float) { translateTemperature = temperature } + @Deprecated("This method is deprecated and will be removed in future versions.") fun getTemperatureForAITranslate(): Float { return translateTemperature } @@ -361,40 +461,76 @@ object QuickBloxUiKit { enabledAITranslate = false } + @Deprecated("This method is deprecated and will be removed in future versions.") fun isAITranslateEnabledWithOpenAIToken(): Boolean { val isNotEnabledAITranslate = !isEnabledAITranslate() if (isNotEnabledAITranslate) { throw RuntimeException("The AI Translate is disabled") } - if (isExistAITokenAndProxyServer(translateOpenAIToken, translateProxyServerURL)) { + if (translateSmartChatAssistantId.isNotBlank() && isExistAITokenAndProxyServer( + translateOpenAIToken, + translateProxyServerURL, + ) + ) { throw RuntimeException("Error initialization. There are Open AI Token and Proxy Server Url. The AI Translate should be initialized with Open AI Token or Proxy server") } return translateOpenAIToken.isNotBlank() } + @Deprecated("This method is deprecated and will be removed in future versions.") fun isAITranslateEnabledWithProxyServer(): Boolean { val isNotEnabledAITranslate = !isEnabledAITranslate() if (isNotEnabledAITranslate) { throw RuntimeException("The AI Translate is disabled") } - if (isExistAITokenAndProxyServer(translateOpenAIToken, translateProxyServerURL)) { + if (translateSmartChatAssistantId.isNotBlank() && isExistAITokenAndProxyServer( + translateOpenAIToken, + translateProxyServerURL + ) + ) { throw RuntimeException("Error initialization. There are Open AI Token and Proxy Server Url. The AI Translate should be initialized with Open AI Token or Proxy server") } return translateProxyServerURL.isNotBlank() } - private fun isExistAITokenAndProxyServer(openAIToken: String, proxyServerURL: String): Boolean { + fun isAITranslateEnabledWithSmartChatAssistantId(): Boolean { + val isNotEnabledAITranslate = !isEnabledAITranslate() + if (isNotEnabledAITranslate) { + throw RuntimeException("The AI Translate is disabled") + } + translateSmartChatAssistantId.isNotBlank() + if (translateSmartChatAssistantId.isNotBlank() && isExistAITokenAndProxyServer( + translateOpenAIToken, + translateProxyServerURL + ) + ) { + throw RuntimeException("Error initialization. There are Open AI Token and Proxy Server Url. The AI Translate should be initialized with Open AI Token or Proxy server") + } + + return translateSmartChatAssistantId.isNotBlank() + } + + private fun isExistAITokenAndProxyServer( + openAIToken: String, + proxyServerURL: String, + ): Boolean { return openAIToken.isNotBlank() && proxyServerURL.isNotBlank() } + fun getAnswerAssistantSmartChatAssistantId(): String { + return answerAssistantSmartChatAssistantId + } + + @Deprecated("This method is deprecated and will be removed in future versions.") fun getAnswerAssistantProxyServerURL(): String { return answerAssistantProxyServerURL } + @Deprecated("This method is deprecated and will be removed in future versions.") fun getAnswerAssistantOpenAIToken(): String { return answerAssistantOpenAIToken } @@ -407,50 +543,66 @@ object QuickBloxUiKit { return rephraseOpenAIToken } + @Deprecated("This method is deprecated and will be removed in future versions.") fun getTranslateProxyServerURL(): String { return translateProxyServerURL } + @Deprecated("This method is deprecated and will be removed in future versions.") fun getTranslateOpenAIToken(): String { return translateOpenAIToken } + fun getTranslateSmartChatAssistantId(): String { + return translateSmartChatAssistantId + } + + @Deprecated("This method is deprecated and will be removed in future versions.") fun setMaxRequestTokensForAIAnswerAssistant(maxTokens: Int) { answerAssistantMaxRequestTokens = maxTokens } + @Deprecated("This method is deprecated and will be removed in future versions.") fun getMaxRequestTokensForAIAnswerAssistant(): Int { return answerAssistantMaxRequestTokens } + @Deprecated("This method is deprecated and will be removed in future versions.") fun setOpenAIModelForAIAnswerAssistant(openAIModel: AnswerAssistantSettingsImpl.Model) { this.answerAssistantOpenAIModel = openAIModel } + @Deprecated("This method is deprecated and will be removed in future versions.") fun getOpenAIModelForAIAnswerAssistant(): AnswerAssistantSettingsImpl.Model { return answerAssistantOpenAIModel } + @Deprecated("This method is deprecated and will be removed in future versions.") fun setOrganizationForAIAnswerAssistant(organization: String) { this.answerAssistantOrganization = organization } + @Deprecated("This method is deprecated and will be removed in future versions.") fun getOrganizationForAIAnswerAssistant(): String? { return answerAssistantOrganization } + @Deprecated("This method is deprecated and will be removed in future versions.") fun setMaxResponseTokensForAIAnswerAssistant(maxTokens: Int) { answerAssistantMaxResponseTokens = maxTokens } + @Deprecated("This method is deprecated and will be removed in future versions.") fun getMaxResponseTokensForAIAnswerAssistant(): Int? { return answerAssistantMaxResponseTokens } + @Deprecated("This method is deprecated and will be removed in future versions.") fun setTemperatureForAIAnswerAssistant(temperature: Float) { answerAssistantTemperature = temperature } + @Deprecated("This method is deprecated and will be removed in future versions.") fun getTemperatureForAIAnswerAssistant(): Float { return answerAssistantTemperature } diff --git a/ui-kit/src/main/java/com/quickblox/android_ui_kit/data/dto/remote/user/RemoteUserDTO.kt b/ui-kit/src/main/java/com/quickblox/android_ui_kit/data/dto/remote/user/RemoteUserDTO.kt index af42323..fb3dddf 100644 --- a/ui-kit/src/main/java/com/quickblox/android_ui_kit/data/dto/remote/user/RemoteUserDTO.kt +++ b/ui-kit/src/main/java/com/quickblox/android_ui_kit/data/dto/remote/user/RemoteUserDTO.kt @@ -18,6 +18,6 @@ class RemoteUserDTO { var facebookId: String? = null var blobId: Int? = null var avatarUrl: String? = null - var tags: ArrayList? = null + var tags: String? = null var customData: String? = null } \ No newline at end of file diff --git a/ui-kit/src/main/java/com/quickblox/android_ui_kit/data/repository/ai/AIRepositoryImpl.kt b/ui-kit/src/main/java/com/quickblox/android_ui_kit/data/repository/ai/AIRepositoryImpl.kt index 0dd7a7e..394ea4e 100644 --- a/ui-kit/src/main/java/com/quickblox/android_ui_kit/data/repository/ai/AIRepositoryImpl.kt +++ b/ui-kit/src/main/java/com/quickblox/android_ui_kit/data/repository/ai/AIRepositoryImpl.kt @@ -14,14 +14,29 @@ import com.quickblox.android_ui_kit.data.source.exception.AIDataSourceException import com.quickblox.android_ui_kit.domain.entity.AIRephraseEntity import com.quickblox.android_ui_kit.domain.entity.AIRephraseToneEntity import com.quickblox.android_ui_kit.domain.entity.implementation.message.AITranslateIncomingChatMessageEntity -import com.quickblox.android_ui_kit.domain.entity.message.ChatMessageEntity import com.quickblox.android_ui_kit.domain.entity.message.ForwardedRepliedMessageEntity -import com.quickblox.android_ui_kit.domain.entity.message.IncomingChatMessageEntity import com.quickblox.android_ui_kit.domain.entity.message.MessageEntity import com.quickblox.android_ui_kit.domain.exception.repository.AIRepositoryException import com.quickblox.android_ui_kit.domain.repository.AIRepository class AIRepositoryImpl(private val aiDataSource: AIDataSource) : AIRepository { + override fun translateIncomingMessageWithSmartChatAssistantId( + messageEntity: ForwardedRepliedMessageEntity, + messagesFromUIKit: List, + langguageCode: String, + ): AITranslateIncomingChatMessageEntity { + try { + val translateDTO = AITranslateMapper.DTOFrom(messageEntity) + val messagesDTO = AITranslateMapper.messagesToDtos(messagesFromUIKit) + val responseDTO = + aiDataSource.translateIncomingMessageWithSmartChatAssistantId(translateDTO, messagesDTO, langguageCode) + + return AITranslateMapper.entityFrom(responseDTO, messageEntity) + } catch (exception: AIDataSourceException) { + throw AIRepositoryException(exception.message ?: "Unexpected Exception") + } + } + override fun translateIncomingMessageWithApiKey( messageEntity: ForwardedRepliedMessageEntity, messagesFromUIKit: List, @@ -53,6 +68,20 @@ class AIRepositoryImpl(private val aiDataSource: AIDataSource) : AIRepository { } } + override fun createAnswerWithSmartChatAssistantId( + messageToAssist: String, + historyMessagesDTO: List, + ): String { + try { + val historyMessagesDTO = AIAnswerAssistantMapper.messagesToDtos(historyMessagesDTO) + val answer = aiDataSource.createAnswerWithSmartChatAssistantId(messageToAssist, historyMessagesDTO) + + return answer + } catch (exception: AIDataSourceException) { + throw AIRepositoryException(exception.message ?: "Unexpected Exception") + } + } + override fun createAnswerWithApiKey(messagesFromUIKit: List): String { try { val messagesDTO = AIAnswerAssistantMapper.messagesToDtos(messagesFromUIKit) @@ -75,7 +104,7 @@ class AIRepositoryImpl(private val aiDataSource: AIDataSource) : AIRepository { } } - override fun rephraseWithApiKE( + override fun rephraseWithApiKey( toneEntity: AIRephraseEntity, messagesFromUIKit: List, ): AIRephraseEntity { diff --git a/ui-kit/src/main/java/com/quickblox/android_ui_kit/data/source/ai/AIDataSource.kt b/ui-kit/src/main/java/com/quickblox/android_ui_kit/data/source/ai/AIDataSource.kt index 79c157e..ae139ae 100644 --- a/ui-kit/src/main/java/com/quickblox/android_ui_kit/data/source/ai/AIDataSource.kt +++ b/ui-kit/src/main/java/com/quickblox/android_ui_kit/data/source/ai/AIDataSource.kt @@ -6,17 +6,42 @@ package com.quickblox.android_ui_kit.data.source.ai -import com.quickblox.android_ui_kit.data.dto.ai.* +import com.quickblox.android_ui_kit.data.dto.ai.AIAnswerAssistantMessageDTO +import com.quickblox.android_ui_kit.data.dto.ai.AIRephraseDTO +import com.quickblox.android_ui_kit.data.dto.ai.AIRephraseMessageDTO +import com.quickblox.android_ui_kit.data.dto.ai.AIRephraseToneDTO +import com.quickblox.android_ui_kit.data.dto.ai.AITranslateDTO +import com.quickblox.android_ui_kit.data.dto.ai.AITranslateMessageDTO interface AIDataSource { - fun translateIncomingMessageWithApiKey(translateDTO: AITranslateDTO, messagesDTO: List): AITranslateDTO + fun translateIncomingMessageWithSmartChatAssistantId( + translateDTO: AITranslateDTO, + messagesDTO: List, + languageCode: String, + ): AITranslateDTO + + @Deprecated("This method is deprecated and will be removed in future versions.") + fun translateIncomingMessageWithApiKey( + translateDTO: AITranslateDTO, + messagesDTO: List, + ): AITranslateDTO + + @Deprecated("This method is deprecated and will be removed in future versions.") fun translateIncomingMessageWithProxyServer( translateDTO: AITranslateDTO, token: String, - messagesDTO: List + messagesDTO: List, ): AITranslateDTO + fun createAnswerWithSmartChatAssistantId( + messageToAssist: String, + historyMessagesDTO: List, + ): String + + @Deprecated("This method is deprecated and will be removed in future versions.") fun createAnswerWithApiKey(messagesDTO: List): String + + @Deprecated("This method is deprecated and will be removed in future versions.") fun createAnswerWithProxyServer(messagesDTO: List, token: String): String fun rephraseWithApiKey(rephraseDTO: AIRephraseDTO, messagesDTO: List): AIRephraseDTO diff --git a/ui-kit/src/main/java/com/quickblox/android_ui_kit/data/source/ai/AIDataSourceImpl.kt b/ui-kit/src/main/java/com/quickblox/android_ui_kit/data/source/ai/AIDataSourceImpl.kt index a8bc4b1..3d5d85a 100644 --- a/ui-kit/src/main/java/com/quickblox/android_ui_kit/data/source/ai/AIDataSourceImpl.kt +++ b/ui-kit/src/main/java/com/quickblox/android_ui_kit/data/source/ai/AIDataSourceImpl.kt @@ -6,6 +6,11 @@ package com.quickblox.android_ui_kit.data.source.ai +import com.quickblox.ai.QB +import com.quickblox.ai.answer_assist.models.message.QBAIAnswerAssistHistoryMessage +import com.quickblox.ai.answer_assist.models.message.QBAIAnswerAssistHistoryMessageImpl +import com.quickblox.ai.answer_assist.models.result.QBAIAnswerAssistResult +import com.quickblox.ai.translate.models.QBAITranslateResult import com.quickblox.android_ai_answer_assistant.QBAIAnswerAssistant import com.quickblox.android_ai_answer_assistant.settings.AnswerAssistantSettingsImpl import com.quickblox.android_ai_editing_assistant.QBAIRephrase @@ -17,16 +22,40 @@ import com.quickblox.android_ai_translate.QBAITranslate import com.quickblox.android_ai_translate.exception.QBAITranslateException import com.quickblox.android_ai_translate.settings.TranslateSettingsImpl import com.quickblox.android_ui_kit.QuickBloxUiKit -import com.quickblox.android_ui_kit.data.dto.ai.* +import com.quickblox.android_ui_kit.data.dto.ai.AIAnswerAssistantMessageDTO +import com.quickblox.android_ui_kit.data.dto.ai.AIRephraseDTO +import com.quickblox.android_ui_kit.data.dto.ai.AIRephraseMessageDTO +import com.quickblox.android_ui_kit.data.dto.ai.AIRephraseToneDTO +import com.quickblox.android_ui_kit.data.dto.ai.AITranslateDTO +import com.quickblox.android_ui_kit.data.dto.ai.AITranslateMessageDTO import com.quickblox.android_ui_kit.data.source.ai.mapper.AIAnswerAssistantDTOMapper import com.quickblox.android_ui_kit.data.source.ai.mapper.AIRephraseDTOMapper import com.quickblox.android_ui_kit.data.source.ai.mapper.AITranslateDTOMapper import com.quickblox.android_ui_kit.data.source.exception.AIDataSourceException import com.quickblox.android_ui_kit.domain.exception.repository.MappingException +import com.quickblox.core.exception.QBResponseException class AIDataSourceImpl : AIDataSource { val tones: MutableList = crateDefaultTones() + override fun translateIncomingMessageWithSmartChatAssistantId( + translateDTO: AITranslateDTO, + messagesDTO: List, + languageCode: String, + ): AITranslateDTO { + val text = translateDTO.content + + val smartChatAssistantId = QuickBloxUiKit.getTranslateSmartChatAssistantId() + try { + val result: QBAITranslateResult = QB.ai.translate(smartChatAssistantId, text, languageCode).perform() + translateDTO.translation = result.message + } catch (exception: QBResponseException) { + throw AIDataSourceException(exception.message) + } + + return translateDTO + } + override fun translateIncomingMessageWithApiKey( translateDTO: AITranslateDTO, messagesDTO: List, @@ -109,6 +138,41 @@ class AIDataSourceImpl : AIDataSource { } } + override fun createAnswerWithSmartChatAssistantId( + messageToAssist: String, + historyMessagesDTO: List, + ): String { + val smartChatAssistantId = QuickBloxUiKit.getAnswerAssistantSmartChatAssistantId() + + + val historyMessages: List = mapAnswerAssistHistoryMessagesFrom(historyMessagesDTO) + try { + val result: QBAIAnswerAssistResult = + QB.ai.answerAssist(smartChatAssistantId, messageToAssist, historyMessages).perform() + return result.message + } catch (exception: QBResponseException) { + throw AIDataSourceException(exception.message) + } + } + + private fun mapAnswerAssistHistoryMessagesFrom(messagesDTO: List): List { + val historyMessages = mutableListOf() + + messagesDTO.forEach { dto -> + val role: QBAIAnswerAssistHistoryMessage.Role? + + if (dto.isIncomeMessage) { + role = QBAIAnswerAssistHistoryMessage.Role.ASSISTANT + } else { + role = QBAIAnswerAssistHistoryMessage.Role.USER + } + + val message = QBAIAnswerAssistHistoryMessageImpl(role, dto.text) + historyMessages.add(message) + } + return historyMessages; + } + override fun createAnswerWithApiKey(messagesDTO: List): String { val openAIToken = QuickBloxUiKit.getAnswerAssistantOpenAIToken() diff --git a/ui-kit/src/main/java/com/quickblox/android_ui_kit/domain/Languages.kt b/ui-kit/src/main/java/com/quickblox/android_ui_kit/domain/Languages.kt new file mode 100644 index 0000000..dd63a62 --- /dev/null +++ b/ui-kit/src/main/java/com/quickblox/android_ui_kit/domain/Languages.kt @@ -0,0 +1,77 @@ +/* + * Created by Injoit on 13.9.2024. + * Copyright © 2024 Quickblox. All rights reserved. + * + */ + +package com.quickblox.android_ui_kit.domain + +enum class Language(val code: String) { + English("en"), + Spanish("es"), + ChineseSimplified("zh-Hans"), + ChineseTraditional("zh-Hant"), + French("fr"), + German("de"), + Japanese("ja"), + Korean("ko"), + Italian("it"), + Russian("ru"), + Portuguese("pt"), + Arabic("ar"), + Hindi("hi"), + Turkish("tr"), + Dutch("nl"), + Polish("pl"), + Ukrainian("uk"), + Albanian("sq"), + Armenian("hy"), + Azerbaijani("az"), + Basque("eu"), + Belarusian("be"), + Bengali("bn"), + Bosnian("bs"), + Bulgarian("bg"), + Catalan("ca"), + Croatian("hr"), + Czech("cs"), + Danish("da"), + Estonian("et"), + Finnish("fi"), + Galician("gl"), + Georgian("ka"), + Greek("el"), + Gujarati("gu"), + Hungarian("hu"), + Indonesian("id"), + Irish("ga"), + Kannada("kn"), + Kazakh("kk"), + Latvian("lv"), + Lithuanian("lt"), + Macedonian("mk"), + Malay("ms"), + Maltese("mt"), + Mongolian("mn"), + Nepali("ne"), + Norwegian("no"), + Pashto("ps"), + Persian("fa"), + Punjabi("pa"), + Romanian("ro"), + Sanskrit("sa"), + Serbian("sr"), + Sindhi("sd"), + Sinhala("si"), + Slovak("sk"), + Slovenian("sl"), + Uzbek("uz"), + Vietnamese("vi"), + Welsh("cy"); + + companion object { + fun isLanguageSupported(code: String): Boolean { + return values().any { it.code == code } + } + } +} diff --git a/ui-kit/src/main/java/com/quickblox/android_ui_kit/domain/entity/UserEntity.kt b/ui-kit/src/main/java/com/quickblox/android_ui_kit/domain/entity/UserEntity.kt index 1906e95..ec3ad71 100644 --- a/ui-kit/src/main/java/com/quickblox/android_ui_kit/domain/entity/UserEntity.kt +++ b/ui-kit/src/main/java/com/quickblox/android_ui_kit/domain/entity/UserEntity.kt @@ -37,8 +37,8 @@ interface UserEntity { fun getAvatarUrl(): String? fun setAvatarUrl(url: String?) - fun getTags(): ArrayList? - fun setTags(tags: ArrayList?) + fun getTags(): String? + fun setTags(tags: String?) fun getCustomData(): String? fun setCustomData(customData: String?) diff --git a/ui-kit/src/main/java/com/quickblox/android_ui_kit/domain/entity/implementation/UserEntityImpl.kt b/ui-kit/src/main/java/com/quickblox/android_ui_kit/domain/entity/implementation/UserEntityImpl.kt index 2adbe93..cf9dff2 100644 --- a/ui-kit/src/main/java/com/quickblox/android_ui_kit/domain/entity/implementation/UserEntityImpl.kt +++ b/ui-kit/src/main/java/com/quickblox/android_ui_kit/domain/entity/implementation/UserEntityImpl.kt @@ -19,7 +19,7 @@ class UserEntityImpl : UserEntity { private var externalId: String? = null private var facebookId: String? = null private var avatarUrl: String? = null - private var tags: ArrayList? = null + private var tags: String? = null private var customData: String? = null override fun getUserId(): Int? { @@ -102,11 +102,11 @@ class UserEntityImpl : UserEntity { this.avatarUrl = url } - override fun getTags(): ArrayList? { + override fun getTags(): String? { return tags } - override fun setTags(tags: ArrayList?) { + override fun setTags(tags: String?) { this.tags = tags } diff --git a/ui-kit/src/main/java/com/quickblox/android_ui_kit/domain/repository/AIRepository.kt b/ui-kit/src/main/java/com/quickblox/android_ui_kit/domain/repository/AIRepository.kt index 7e471a2..ac235fc 100644 --- a/ui-kit/src/main/java/com/quickblox/android_ui_kit/domain/repository/AIRepository.kt +++ b/ui-kit/src/main/java/com/quickblox/android_ui_kit/domain/repository/AIRepository.kt @@ -10,23 +10,34 @@ import com.quickblox.android_ui_kit.domain.entity.AIRephraseEntity import com.quickblox.android_ui_kit.domain.entity.AIRephraseToneEntity import com.quickblox.android_ui_kit.domain.entity.implementation.message.AITranslateIncomingChatMessageEntity import com.quickblox.android_ui_kit.domain.entity.message.ForwardedRepliedMessageEntity -import com.quickblox.android_ui_kit.domain.entity.message.IncomingChatMessageEntity import com.quickblox.android_ui_kit.domain.entity.message.MessageEntity interface AIRepository { + fun translateIncomingMessageWithSmartChatAssistantId( + messageEntity: ForwardedRepliedMessageEntity, + messagesFromUIKit: List, languageCode: String, + ): AITranslateIncomingChatMessageEntity + + @Deprecated("This method is deprecated and will be removed in future versions.") fun translateIncomingMessageWithApiKey( messageEntity: ForwardedRepliedMessageEntity, messagesFromUIKit: List, ): AITranslateIncomingChatMessageEntity + @Deprecated("This method is deprecated and will be removed in future versions.") fun translateIncomingMessageWithProxyServer( messageEntity: ForwardedRepliedMessageEntity, token: String, messagesFromUIKit: List, ): AITranslateIncomingChatMessageEntity + fun createAnswerWithSmartChatAssistantId(messageToAssist: String, historyMessagesDTO: List): String + + @Deprecated("This method is deprecated and will be removed in future versions.") fun createAnswerWithApiKey(messagesFromUIKit: List): String + + @Deprecated("This method is deprecated and will be removed in future versions.") fun createAnswerWithProxyServer(messagesFromUIKit: List, token: String): String - fun rephraseWithApiKE( + fun rephraseWithApiKey( toneEntity: AIRephraseEntity, messagesFromUIKit: List, ): AIRephraseEntity diff --git a/ui-kit/src/main/java/com/quickblox/android_ui_kit/domain/usecases/LoadAIAnswerAssistantWithSmartChatAssistantIdUseCase.kt b/ui-kit/src/main/java/com/quickblox/android_ui_kit/domain/usecases/LoadAIAnswerAssistantWithSmartChatAssistantIdUseCase.kt new file mode 100644 index 0000000..ed480a8 --- /dev/null +++ b/ui-kit/src/main/java/com/quickblox/android_ui_kit/domain/usecases/LoadAIAnswerAssistantWithSmartChatAssistantIdUseCase.kt @@ -0,0 +1,137 @@ +/* + * Created by Injoit on 13.9.2024. + * Copyright © 2024 Quickblox. All rights reserved. + * + */ + +package com.quickblox.android_ui_kit.domain.usecases + +import com.quickblox.android_ui_kit.ExcludeFromCoverage +import com.quickblox.android_ui_kit.QuickBloxUiKit +import com.quickblox.android_ui_kit.domain.entity.PaginationEntity +import com.quickblox.android_ui_kit.domain.entity.implementation.PaginationEntityImpl +import com.quickblox.android_ui_kit.domain.entity.implementation.message.AITranslateIncomingChatMessageEntity +import com.quickblox.android_ui_kit.domain.entity.message.ChatMessageEntity +import com.quickblox.android_ui_kit.domain.entity.message.ForwardedRepliedMessageEntity +import com.quickblox.android_ui_kit.domain.entity.message.MessageEntity +import com.quickblox.android_ui_kit.domain.exception.DomainException +import com.quickblox.android_ui_kit.domain.exception.repository.AIRepositoryException +import com.quickblox.android_ui_kit.domain.exception.repository.MessagesRepositoryException +import com.quickblox.android_ui_kit.domain.usecases.base.BaseUseCase +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +@ExcludeFromCoverage +class LoadAIAnswerAssistantWithSmartChatAssistantIdUseCase( + private val dialogId: String, + private val message: ForwardedRepliedMessageEntity, +) : BaseUseCase() { + private val messageRepository = QuickBloxUiKit.getDependency().getMessagesRepository() + private val aiRepository = QuickBloxUiKit.getDependency().getAIRepository() + + override suspend fun execute(): String { + val receivedAnswer: String + + if (message.getContent().isNullOrEmpty()) { + throw DomainException("The message content shouldn't be empty") + } + + withContext(Dispatchers.IO) { + try { + val pagination = searchStartPagination(dialogId, message) + val historyMessages = loadHistoryMessages(dialogId, message, pagination) + + val messageToAssistant = message.getContent()!! + + receivedAnswer = aiRepository.createAnswerWithSmartChatAssistantId(messageToAssistant, historyMessages) + } catch (exception: MessagesRepositoryException) { + throw DomainException(exception.message ?: "Unexpected Exception") + } catch (exception: AIRepositoryException) { + throw DomainException(exception.message ?: "Unexpected Exception") + } + } + + return receivedAnswer + } + + private suspend fun searchStartPagination( + dialogId: String, + incomingMessage: ForwardedRepliedMessageEntity, + ): PaginationEntity { + var paginationResult: PaginationEntity = PaginationEntityImpl() + paginationResult.setHasNextPage(true) + + var isNotFoundPagination = true + + while (paginationResult.hasNextPage()) { + messageRepository.getMessagesFromRemote(dialogId, paginationResult).collect { pairResult -> + val receivedMessage = pairResult.getOrThrow().first + val receivedPagination = pairResult.getOrThrow().second + + paginationResult = receivedPagination + + val messageId = incomingMessage.getRelatedMessageId() ?: incomingMessage.getMessageId() + val isEqualsMessages = receivedMessage?.getMessageId() == messageId + if (isNotFoundPagination && isEqualsMessages) { + isNotFoundPagination = false + return@collect + } + } + + if (isNotFoundPagination) { + paginationResult.nextPage() + } + } + + if (isNotFoundPagination) { + throw DomainException("Didn't find message with id: ${incomingMessage.getMessageId()}") + } + + return paginationResult + } + + private suspend fun loadHistoryMessages( + dialogId: String, + startMessage: ForwardedRepliedMessageEntity, + paginationEntity: PaginationEntity, + ): List { + val messages = mutableListOf() + + var pagination = paginationEntity + pagination.setHasNextPage(true) + + var foundStartMessage = false + + val MAX_PAGES_COUNT = 5 + if (pagination.hasNextPage() && pagination.getCurrentPage() < MAX_PAGES_COUNT) { + messageRepository.getMessagesFromRemote(dialogId, pagination).collect { pairResult -> + val receivedMessage = pairResult.getOrThrow().first + val receivedPagination = pairResult.getOrThrow().second + + pagination = receivedPagination + + val messageId = startMessage.getRelatedMessageId() ?: startMessage.getMessageId() + receivedMessage?.let { + if (receivedMessage.getMessageId() == messageId) { + foundStartMessage = true + if (receivedMessage is ChatMessageEntity) { + if (startMessage is AITranslateIncomingChatMessageEntity && startMessage.isTranslated()) { + receivedMessage.setContent(startMessage.getTranslation()) + } else { + receivedMessage.setContent(startMessage.getContent()) + } + } + } + + val isTextMessage = + receivedMessage is ChatMessageEntity && receivedMessage.getContentType() == ChatMessageEntity.ContentTypes.TEXT + if (foundStartMessage && isTextMessage) { + messages.add(receivedMessage) + } + } + } + } + + return messages + } +} \ No newline at end of file diff --git a/ui-kit/src/main/java/com/quickblox/android_ui_kit/domain/usecases/LoadAIRephraseWithApiKeyUseCase.kt b/ui-kit/src/main/java/com/quickblox/android_ui_kit/domain/usecases/LoadAIRephraseWithApiKeyUseCase.kt index d025612..e00555b 100644 --- a/ui-kit/src/main/java/com/quickblox/android_ui_kit/domain/usecases/LoadAIRephraseWithApiKeyUseCase.kt +++ b/ui-kit/src/main/java/com/quickblox/android_ui_kit/domain/usecases/LoadAIRephraseWithApiKeyUseCase.kt @@ -34,7 +34,7 @@ class LoadAIRephraseWithApiKeyUseCase(private val dialogId: String?, private val messagesFromUIKit = loadMessages(it) } - resultEntity = aiRepository.rephraseWithApiKE(toneEntity, messagesFromUIKit) + resultEntity = aiRepository.rephraseWithApiKey(toneEntity, messagesFromUIKit) } catch (exception: AIRepositoryException) { throw DomainException(exception.message ?: "Unexpected Exception") } catch (exception: MessagesRepositoryException) { diff --git a/ui-kit/src/main/java/com/quickblox/android_ui_kit/domain/usecases/LoadAITranslateWithSmartChatAssistantIdUseCase.kt b/ui-kit/src/main/java/com/quickblox/android_ui_kit/domain/usecases/LoadAITranslateWithSmartChatAssistantIdUseCase.kt new file mode 100644 index 0000000..c43347d --- /dev/null +++ b/ui-kit/src/main/java/com/quickblox/android_ui_kit/domain/usecases/LoadAITranslateWithSmartChatAssistantIdUseCase.kt @@ -0,0 +1,142 @@ +/* + * Created by Injoit on 12.9.2024. + * Copyright © 2024 Quickblox. All rights reserved. + * + */ + +package com.quickblox.android_ui_kit.domain.usecases + +import com.quickblox.android_ui_kit.QuickBloxUiKit +import com.quickblox.android_ui_kit.domain.Language +import com.quickblox.android_ui_kit.domain.entity.PaginationEntity +import com.quickblox.android_ui_kit.domain.entity.implementation.PaginationEntityImpl +import com.quickblox.android_ui_kit.domain.entity.implementation.message.AITranslateIncomingChatMessageEntity +import com.quickblox.android_ui_kit.domain.entity.message.ChatMessageEntity +import com.quickblox.android_ui_kit.domain.entity.message.ForwardedRepliedMessageEntity +import com.quickblox.android_ui_kit.domain.entity.message.MessageEntity +import com.quickblox.android_ui_kit.domain.exception.DomainException +import com.quickblox.android_ui_kit.domain.exception.repository.AIRepositoryException +import com.quickblox.android_ui_kit.domain.usecases.base.BaseUseCase +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import java.util.Locale + +class LoadAITranslateWithSmartChatAssistantIdUseCase( + private val dialogId: String?, + private val message: ForwardedRepliedMessageEntity, +) : + BaseUseCase() { + private val aiRepository = QuickBloxUiKit.getDependency().getAIRepository() + private val messageRepository = QuickBloxUiKit.getDependency().getMessagesRepository() + + override suspend fun execute(): AITranslateIncomingChatMessageEntity? { + var entity: AITranslateIncomingChatMessageEntity? = null + + withContext(Dispatchers.IO) { + var messagesFromUIKit = listOf() + dialogId?.let { + val pagination = searchStartPagination(dialogId, message) + messagesFromUIKit = loadMessages(dialogId, message, pagination) + } + + val languageCode: String = getDefaultLanguageCode() + + try { + entity = aiRepository.translateIncomingMessageWithSmartChatAssistantId( + message, + messagesFromUIKit, + languageCode + ) + } catch (exception: AIRepositoryException) { + throw DomainException(exception.message ?: "Unexpected Exception") + } + } + entity?.setRelatedMessageId(message.getRelatedMessageId()) + return entity + } + + private fun getDefaultLanguageCode(): String { + val defaultLanguageCode: String = Locale.getDefault().language + + val isSupportDefaultLanguage = Language.isLanguageSupported(defaultLanguageCode) + return if (isSupportDefaultLanguage) { + defaultLanguageCode + } else { + Language.English.code + } + } + + private suspend fun searchStartPagination( + dialogId: String, + incomingMessage: ForwardedRepliedMessageEntity, + ): PaginationEntity { + var paginationResult: PaginationEntity = PaginationEntityImpl() + paginationResult.setHasNextPage(true) + + var isNotFoundPagination = true + + while (paginationResult.hasNextPage()) { + messageRepository.getMessagesFromRemote(dialogId, paginationResult).collect { pairResult -> + val receivedMessage = pairResult.getOrThrow().first + val receivedPagination = pairResult.getOrThrow().second + + paginationResult = receivedPagination + + val messageId = incomingMessage.getRelatedMessageId() ?: incomingMessage.getMessageId() + val isEqualsMessages = receivedMessage?.getMessageId() == messageId + if (isNotFoundPagination && isEqualsMessages) { + isNotFoundPagination = false + return@collect + } + } + + if (isNotFoundPagination) { + paginationResult.nextPage() + } + } + + if (isNotFoundPagination) { + throw DomainException("Didn't find message with id: ${incomingMessage.getMessageId()}") + } + + return paginationResult + } + + private suspend fun loadMessages( + dialogId: String, + startMessage: ForwardedRepliedMessageEntity, + paginationEntity: PaginationEntity, + ): List { + val messages = mutableListOf() + + var pagination = paginationEntity + pagination.setHasNextPage(true) + + var foundStartMessage = false + + val MAX_PAGES_COUNT = 5 + if (pagination.hasNextPage() && pagination.getCurrentPage() < MAX_PAGES_COUNT) { + messageRepository.getMessagesFromRemote(dialogId, pagination).collect { pairResult -> + val receivedMessage = pairResult.getOrThrow().first + val receivedPagination = pairResult.getOrThrow().second + + pagination = receivedPagination + + val messageId = startMessage.getRelatedMessageId() ?: startMessage.getMessageId() + receivedMessage?.let { + if (receivedMessage.getMessageId() == messageId) { + foundStartMessage = true + } + + val isTextMessage = + receivedMessage is ChatMessageEntity && receivedMessage.getContentType() == ChatMessageEntity.ContentTypes.TEXT + if (foundStartMessage && isTextMessage) { + messages.add(receivedMessage) + } + } + } + } + + return messages + } +} \ No newline at end of file diff --git a/ui-kit/src/main/java/com/quickblox/android_ui_kit/presentation/base/BaseMessageViewHolder.kt b/ui-kit/src/main/java/com/quickblox/android_ui_kit/presentation/base/BaseMessageViewHolder.kt index 4d55586..9e28708 100644 --- a/ui-kit/src/main/java/com/quickblox/android_ui_kit/presentation/base/BaseMessageViewHolder.kt +++ b/ui-kit/src/main/java/com/quickblox/android_ui_kit/presentation/base/BaseMessageViewHolder.kt @@ -168,6 +168,7 @@ abstract class BaseMessageViewHolder(viewBinding: VB) : BaseVi } else { setAIListener(message, aiListener, textMessageBinding) setTranslateListener(textMessage!!, aiListener, textMessageBinding) + } if (QuickBloxUiKit.isEnabledAITranslate()) { diff --git a/ui-kit/src/main/java/com/quickblox/android_ui_kit/presentation/screens/chat/group/GroupChatFragment.kt b/ui-kit/src/main/java/com/quickblox/android_ui_kit/presentation/screens/chat/group/GroupChatFragment.kt index fac4bc7..c9d389c 100644 --- a/ui-kit/src/main/java/com/quickblox/android_ui_kit/presentation/screens/chat/group/GroupChatFragment.kt +++ b/ui-kit/src/main/java/com/quickblox/android_ui_kit/presentation/screens/chat/group/GroupChatFragment.kt @@ -651,8 +651,9 @@ open class GroupChatFragment : BaseFragment() { private fun isConfiguredAIAnswerAssistant(): Boolean { val enabledByOpenAIToken = QuickBloxUiKit.isAIAnswerAssistantEnabledWithOpenAIToken() val enabledByQuickBloxToken = QuickBloxUiKit.isAIAnswerAssistantEnabledWithProxyServer() + val enabledBySmartChatAssistantId = QuickBloxUiKit.isAIAnswerAssistantEnabledWithSmartChatAssistantId() - val enabledByOpenAITokenOrQuickBloxToken = enabledByOpenAIToken || enabledByQuickBloxToken + val enabledByOpenAITokenOrQuickBloxToken = enabledByOpenAIToken || enabledByQuickBloxToken || enabledBySmartChatAssistantId return QuickBloxUiKit.isEnabledAIAnswerAssistant() && enabledByOpenAITokenOrQuickBloxToken } @@ -669,8 +670,9 @@ open class GroupChatFragment : BaseFragment() { private fun isConfiguredAITranslate(): Boolean { val enabledByOpenAIToken = QuickBloxUiKit.isAITranslateEnabledWithOpenAIToken() val enabledByQuickBloxToken = QuickBloxUiKit.isAITranslateEnabledWithProxyServer() + val enabledBySmartChatAssistantId= QuickBloxUiKit.isAITranslateEnabledWithSmartChatAssistantId() - val enabledByOpenAITokenOrQuickBloxToken = enabledByOpenAIToken || enabledByQuickBloxToken + val enabledByOpenAITokenOrQuickBloxToken = enabledByOpenAIToken || enabledByQuickBloxToken || enabledBySmartChatAssistantId return QuickBloxUiKit.isEnabledAITranslate() && enabledByOpenAITokenOrQuickBloxToken } diff --git a/ui-kit/src/main/java/com/quickblox/android_ui_kit/presentation/screens/chat/group/GroupChatViewModel.kt b/ui-kit/src/main/java/com/quickblox/android_ui_kit/presentation/screens/chat/group/GroupChatViewModel.kt index ff5127e..82bbb37 100644 --- a/ui-kit/src/main/java/com/quickblox/android_ui_kit/presentation/screens/chat/group/GroupChatViewModel.kt +++ b/ui-kit/src/main/java/com/quickblox/android_ui_kit/presentation/screens/chat/group/GroupChatViewModel.kt @@ -33,11 +33,13 @@ import com.quickblox.android_ui_kit.domain.usecases.GetDialogByIdUseCase import com.quickblox.android_ui_kit.domain.usecases.GetLocalFileByUriUseCase import com.quickblox.android_ui_kit.domain.usecases.LoadAIAnswerAssistantWithApiKeyUseCase import com.quickblox.android_ui_kit.domain.usecases.LoadAIAnswerAssistantWithProxyServerUseCase +import com.quickblox.android_ui_kit.domain.usecases.LoadAIAnswerAssistantWithSmartChatAssistantIdUseCase import com.quickblox.android_ui_kit.domain.usecases.LoadAIRephraseWithApiKeyUseCase import com.quickblox.android_ui_kit.domain.usecases.LoadAIRephraseWithProxyServerUseCase import com.quickblox.android_ui_kit.domain.usecases.LoadAIRephrasesUseCase import com.quickblox.android_ui_kit.domain.usecases.LoadAITranslateWithApiKeyUseCase import com.quickblox.android_ui_kit.domain.usecases.LoadAITranslateWithProxyServerUseCase +import com.quickblox.android_ui_kit.domain.usecases.LoadAITranslateWithSmartChatAssistantIdUseCase import com.quickblox.android_ui_kit.domain.usecases.MessagesEventUseCase import com.quickblox.android_ui_kit.domain.usecases.ReadMessageUseCase import com.quickblox.android_ui_kit.domain.usecases.SendChatMessageUseCase @@ -377,18 +379,22 @@ class GroupChatViewModel : BaseViewModel() { var relatedMessage: OutgoingChatMessageEntity? = null viewModelScope.launch { - relatedMessage = CreateMessageUseCase(contentType, dialogId, text, file).execute().lastOrNull() - val replyMessage = relatedMessage?.let { - CreateReplyMessageUseCase(repliedMessage, it).execute() - } + try { + relatedMessage = CreateMessageUseCase(contentType, dialogId, text, file).execute().lastOrNull() + val replyMessage = relatedMessage?.let { + CreateReplyMessageUseCase(repliedMessage, it).execute() + } - replyMessage as MessageEntity - if (isNeedAddHeaderBeforeFirst(replyMessage)) { - addHeaderBeforeFirst(replyMessage) - } + replyMessage as MessageEntity + if (isNeedAddHeaderBeforeFirst(replyMessage)) { + addHeaderBeforeFirst(replyMessage) + } - addAsFirst(replyMessage) - sendReplyMessage(replyMessage) + addAsFirst(replyMessage) + sendReplyMessage(replyMessage) + } catch (exception: DomainException) { + showError(exception.message) + } } } @@ -543,6 +549,22 @@ class GroupChatViewModel : BaseViewModel() { } fun executeAIAnswerAssistant(dialogId: String, message: ForwardedRepliedMessageEntity) { + if (QuickBloxUiKit.isAIAnswerAssistantEnabledWithSmartChatAssistantId()) { + viewModelScope.launch { + try { + showLoading() + val answer = LoadAIAnswerAssistantWithSmartChatAssistantIdUseCase(dialogId, message).execute() + if (answer.isNotEmpty()) { + _aiAnswer.postValue(answer) + } + } catch (exception: DomainException) { + showError(exception.message) + } finally { + hideLoading() + } + } + } + if (QuickBloxUiKit.isAIAnswerAssistantEnabledWithOpenAIToken()) { viewModelScope.launch { try { @@ -587,6 +609,31 @@ class GroupChatViewModel : BaseViewModel() { updateMessage = findMessageBy(message.getRelatedMessageId()) } addOrUpdateMessage(updateMessage) + return + } + + if (QuickBloxUiKit.isAITranslateEnabledWithSmartChatAssistantId()) { + viewModelScope.launch { + try { + var updateMessage: MessageEntity + val entity = + LoadAITranslateWithSmartChatAssistantIdUseCase(dialog?.getDialogId(), message).execute()!! + entity.setTranslated(true) + updateMessage = entity + + val isInternalMessage = !message.getRelatedMessageId().isNullOrEmpty() + if (isInternalMessage) { + val foundMessage = findMessageBy(entity.getRelatedMessageId()) + foundMessage.setForwardedRepliedMessages(listOf(entity as ForwardedRepliedMessageEntity)) + updateMessage = foundMessage + } + + addOrUpdateMessage(updateMessage) + } catch (exception: DomainException) { + showError(exception.message) + addOrUpdateMessage(message) + } + } } if (QuickBloxUiKit.isAITranslateEnabledWithOpenAIToken()) { diff --git a/ui-kit/src/main/java/com/quickblox/android_ui_kit/presentation/screens/chat/individual/PrivateChatFragment.kt b/ui-kit/src/main/java/com/quickblox/android_ui_kit/presentation/screens/chat/individual/PrivateChatFragment.kt index 9e89344..0325b20 100644 --- a/ui-kit/src/main/java/com/quickblox/android_ui_kit/presentation/screens/chat/individual/PrivateChatFragment.kt +++ b/ui-kit/src/main/java/com/quickblox/android_ui_kit/presentation/screens/chat/individual/PrivateChatFragment.kt @@ -639,8 +639,9 @@ open class PrivateChatFragment : BaseFragment() { private fun isConfiguredAIAnswerAssistant(): Boolean { val enabledByOpenAIToken = QuickBloxUiKit.isAIAnswerAssistantEnabledWithOpenAIToken() val enabledByQuickBloxToken = QuickBloxUiKit.isAIAnswerAssistantEnabledWithProxyServer() + val enabledBySmartChatAssistantId = QuickBloxUiKit.isAIAnswerAssistantEnabledWithSmartChatAssistantId() - val enabledByOpenAITokenOrQuickBloxToken = enabledByOpenAIToken || enabledByQuickBloxToken + val enabledByOpenAITokenOrQuickBloxToken = enabledByOpenAIToken || enabledByQuickBloxToken || enabledBySmartChatAssistantId return QuickBloxUiKit.isEnabledAIAnswerAssistant() && enabledByOpenAITokenOrQuickBloxToken } @@ -657,8 +658,9 @@ open class PrivateChatFragment : BaseFragment() { private fun isConfiguredAITranslate(): Boolean { val enabledByOpenAIToken = QuickBloxUiKit.isAITranslateEnabledWithOpenAIToken() val enabledByQuickBloxToken = QuickBloxUiKit.isAITranslateEnabledWithProxyServer() + val enabledBySmartChatAssistantId= QuickBloxUiKit.isAITranslateEnabledWithSmartChatAssistantId() - val enabledByOpenAITokenOrQuickBloxToken = enabledByOpenAIToken || enabledByQuickBloxToken + val enabledByOpenAITokenOrQuickBloxToken = enabledByOpenAIToken || enabledByQuickBloxToken || enabledBySmartChatAssistantId return QuickBloxUiKit.isEnabledAITranslate() && enabledByOpenAITokenOrQuickBloxToken } diff --git a/ui-kit/src/main/java/com/quickblox/android_ui_kit/presentation/screens/chat/individual/PrivateChatViewModel.kt b/ui-kit/src/main/java/com/quickblox/android_ui_kit/presentation/screens/chat/individual/PrivateChatViewModel.kt index 32a00b0..3b6e84b 100644 --- a/ui-kit/src/main/java/com/quickblox/android_ui_kit/presentation/screens/chat/individual/PrivateChatViewModel.kt +++ b/ui-kit/src/main/java/com/quickblox/android_ui_kit/presentation/screens/chat/individual/PrivateChatViewModel.kt @@ -17,9 +17,35 @@ import com.quickblox.android_ui_kit.domain.entity.PaginationEntity import com.quickblox.android_ui_kit.domain.entity.UserEntity import com.quickblox.android_ui_kit.domain.entity.implementation.PaginationEntityImpl import com.quickblox.android_ui_kit.domain.entity.implementation.message.AITranslateIncomingChatMessageEntity -import com.quickblox.android_ui_kit.domain.entity.message.* +import com.quickblox.android_ui_kit.domain.entity.message.ChatMessageEntity +import com.quickblox.android_ui_kit.domain.entity.message.ForwardedRepliedMessageEntity +import com.quickblox.android_ui_kit.domain.entity.message.IncomingChatMessageEntity +import com.quickblox.android_ui_kit.domain.entity.message.MessageEntity +import com.quickblox.android_ui_kit.domain.entity.message.OutgoingChatMessageEntity import com.quickblox.android_ui_kit.domain.exception.DomainException -import com.quickblox.android_ui_kit.domain.usecases.* +import com.quickblox.android_ui_kit.domain.usecases.CreateLocalFileUseCase +import com.quickblox.android_ui_kit.domain.usecases.CreateMessageUseCase +import com.quickblox.android_ui_kit.domain.usecases.CreateReplyMessageUseCase +import com.quickblox.android_ui_kit.domain.usecases.DeliverMessageUseCase +import com.quickblox.android_ui_kit.domain.usecases.GetAllMessagesUseCase +import com.quickblox.android_ui_kit.domain.usecases.GetDialogByIdUseCase +import com.quickblox.android_ui_kit.domain.usecases.GetLocalFileByUriUseCase +import com.quickblox.android_ui_kit.domain.usecases.LoadAIAnswerAssistantWithApiKeyUseCase +import com.quickblox.android_ui_kit.domain.usecases.LoadAIAnswerAssistantWithProxyServerUseCase +import com.quickblox.android_ui_kit.domain.usecases.LoadAIAnswerAssistantWithSmartChatAssistantIdUseCase +import com.quickblox.android_ui_kit.domain.usecases.LoadAIRephraseWithApiKeyUseCase +import com.quickblox.android_ui_kit.domain.usecases.LoadAIRephraseWithProxyServerUseCase +import com.quickblox.android_ui_kit.domain.usecases.LoadAIRephrasesUseCase +import com.quickblox.android_ui_kit.domain.usecases.LoadAITranslateWithApiKeyUseCase +import com.quickblox.android_ui_kit.domain.usecases.LoadAITranslateWithProxyServerUseCase +import com.quickblox.android_ui_kit.domain.usecases.LoadAITranslateWithSmartChatAssistantIdUseCase +import com.quickblox.android_ui_kit.domain.usecases.MessagesEventUseCase +import com.quickblox.android_ui_kit.domain.usecases.ReadMessageUseCase +import com.quickblox.android_ui_kit.domain.usecases.SendChatMessageUseCase +import com.quickblox.android_ui_kit.domain.usecases.SendForwardReplyMessageUseCase +import com.quickblox.android_ui_kit.domain.usecases.StartTypingEventUseCase +import com.quickblox.android_ui_kit.domain.usecases.StopTypingEventUseCase +import com.quickblox.android_ui_kit.domain.usecases.TypingEventUseCase import com.quickblox.android_ui_kit.presentation.base.BaseViewModel import com.quickblox.android_ui_kit.presentation.checkStringByRegex import com.quickblox.android_ui_kit.presentation.components.messages.DateHeaderMessageEntity @@ -30,7 +56,8 @@ import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.launch import java.text.SimpleDateFormat -import java.util.* +import java.util.Date +import java.util.Locale class PrivateChatViewModel : BaseViewModel() { enum class TypingEvents { @@ -353,25 +380,30 @@ class PrivateChatViewModel : BaseViewModel() { var relatedMessage: OutgoingChatMessageEntity? = null viewModelScope.launch { - relatedMessage = CreateMessageUseCase(contentType, dialogId, text, file).execute().lastOrNull() - val replyMessage = relatedMessage?.let { - CreateReplyMessageUseCase(repliedMessage, it).execute() - } + try { + relatedMessage = CreateMessageUseCase(contentType, dialogId, text, file).execute().lastOrNull() + val replyMessage = relatedMessage?.let { + CreateReplyMessageUseCase(repliedMessage, it).execute() + } - replyMessage as MessageEntity - if (isNeedAddHeaderBeforeFirst(replyMessage)) { - addHeaderBeforeFirst(replyMessage) - } + replyMessage as MessageEntity + if (isNeedAddHeaderBeforeFirst(replyMessage)) { + addHeaderBeforeFirst(replyMessage) + } - addAsFirst(replyMessage) - sendReplyMessage(replyMessage) + addAsFirst(replyMessage) + sendReplyMessage(replyMessage) + } catch (exception: DomainException) { + showError(exception.message) + } } } private fun sendReplyMessage(replyMessage: OutgoingChatMessageEntity) { viewModelScope.launch { runCatching { - val sentMessage = SendForwardReplyMessageUseCase(replyMessage, dialog?.getDialogId().toString()).execute() + val sentMessage = + SendForwardReplyMessageUseCase(replyMessage, dialog?.getDialogId().toString()).execute() if (isExistMessage(sentMessage)) { updatedMessage(sentMessage) @@ -524,6 +556,22 @@ class PrivateChatViewModel : BaseViewModel() { } fun executeAIAnswerAssistant(dialogId: String, message: ForwardedRepliedMessageEntity) { + if (QuickBloxUiKit.isAIAnswerAssistantEnabledWithSmartChatAssistantId()) { + viewModelScope.launch { + try { + showLoading() + val answer = LoadAIAnswerAssistantWithSmartChatAssistantIdUseCase(dialogId, message).execute() + if (answer.isNotEmpty()) { + _aiAnswer.postValue(answer) + } + } catch (exception: DomainException) { + showError(exception.message) + } finally { + hideLoading() + } + } + } + if (QuickBloxUiKit.isAIAnswerAssistantEnabledWithOpenAIToken()) { viewModelScope.launch { try { @@ -568,6 +616,31 @@ class PrivateChatViewModel : BaseViewModel() { updateMessage = findMessageBy(message.getRelatedMessageId()) } addOrUpdateMessage(updateMessage) + return + } + + if (QuickBloxUiKit.isAITranslateEnabledWithSmartChatAssistantId()) { + viewModelScope.launch { + try { + var updateMessage: MessageEntity + val entity = + LoadAITranslateWithSmartChatAssistantIdUseCase(dialog?.getDialogId(), message).execute()!! + entity.setTranslated(true) + updateMessage = entity + + val isInternalMessage = !message.getRelatedMessageId().isNullOrEmpty() + if (isInternalMessage) { + val foundMessage = findMessageBy(entity.getRelatedMessageId()) + foundMessage.setForwardedRepliedMessages(listOf(entity as ForwardedRepliedMessageEntity)) + updateMessage = foundMessage + } + + addOrUpdateMessage(updateMessage) + } catch (exception: DomainException) { + showError(exception.message) + addOrUpdateMessage(message) + } + } } if (QuickBloxUiKit.isAITranslateEnabledWithOpenAIToken()) { diff --git a/ui-kit/src/test/java/com/quickblox/android_ui_kit/domain/entity/UserEntityTest.kt b/ui-kit/src/test/java/com/quickblox/android_ui_kit/domain/entity/UserEntityTest.kt index e1637d5..ac2e770 100644 --- a/ui-kit/src/test/java/com/quickblox/android_ui_kit/domain/entity/UserEntityTest.kt +++ b/ui-kit/src/test/java/com/quickblox/android_ui_kit/domain/entity/UserEntityTest.kt @@ -55,7 +55,7 @@ class UserEntityTest { entity.setExternalId("test_external_id") entity.setFacebookId("test_facebook_id") entity.setAvatarUrl("https://avatart.com/avatar.png") - entity.setTags(arrayListOf("test_tag")) + entity.setTags("test_tag") entity.setCustomData("test_custom_data") return entity @@ -83,7 +83,7 @@ class UserEntityTest { entity.setExternalId("test_external_id ${Random.nextInt(600, 700)}") entity.setFacebookId("test_facebook_id ${Random.nextInt(700, 800)}") entity.setAvatarUrl("") - entity.setTags(arrayListOf("test ${Random.nextInt(800, 900)}")) + entity.setTags("test ${Random.nextInt(800, 900)}") entity.setCustomData("test_custom_data ${Random.nextInt(900, 1000)}") return entity diff --git a/ui-kit/src/test/java/com/quickblox/android_ui_kit/domain/usecases/TypingEventUseCaseTest.kt b/ui-kit/src/test/java/com/quickblox/android_ui_kit/domain/usecases/TypingEventUseCaseTest.kt index db48c03..4ee77c5 100644 --- a/ui-kit/src/test/java/com/quickblox/android_ui_kit/domain/usecases/TypingEventUseCaseTest.kt +++ b/ui-kit/src/test/java/com/quickblox/android_ui_kit/domain/usecases/TypingEventUseCaseTest.kt @@ -320,6 +320,7 @@ class TypingEventUseCaseTest : BaseTest() { @Test @ExperimentalCoroutinesApi + @Ignore fun executeAndStartTyping3Times_wait7Seconds_receivedTyping() = runTest { val usersRepository = object : UsersRepositorySpy() { override fun getUsersFromRemote(userIds: Collection): Collection { diff --git a/ui-kit/src/test/java/com/quickblox/android_ui_kit/stub/entity/UserEntityStub.kt b/ui-kit/src/test/java/com/quickblox/android_ui_kit/stub/entity/UserEntityStub.kt index ca9decc..3adb892 100644 --- a/ui-kit/src/test/java/com/quickblox/android_ui_kit/stub/entity/UserEntityStub.kt +++ b/ui-kit/src/test/java/com/quickblox/android_ui_kit/stub/entity/UserEntityStub.kt @@ -88,11 +88,11 @@ open class UserEntityStub : UserEntity { throw RuntimeException() } - override fun getTags(): ArrayList? { + override fun getTags(): String? { throw RuntimeException() } - override fun setTags(tags: ArrayList?) { + override fun setTags(tags: String?) { throw RuntimeException() }