From d2cb6eea8d0cf7d836df26c64b1cb0e04cf1ef11 Mon Sep 17 00:00:00 2001 From: Julius Linus Date: Wed, 4 Oct 2023 08:35:55 -0500 Subject: [PATCH] Adjusting Translation Provider - Added 4 new model classes - Added the new API function to NcApi - Implemented the changes in the Repository + ViewModel - Implemented the changes in the Activity - Added some helper functions --- .../java/com/nextcloud/talk/api/NcApi.java | 5 ++ .../repositories/TranslateRepository.kt | 6 ++ .../repositories/TranslateRepositoryImpl.kt | 5 ++ .../repositories/model/LanguageModel.kt | 27 +++++++ .../repositories/model/LanguagesData.kt | 38 ++++++++++ .../repositories/model/LanguagesOCS.kt | 38 ++++++++++ .../repositories/model/LanguagesOverall.kt | 35 ++++++++++ .../repositories/model/TranslateOCS.kt | 2 +- .../talk/translate/ui/TranslateActivity.kt | 70 ++++++++++--------- .../viewmodels/TranslateViewModel.kt | 39 ++++++++++- .../talk/ui/dialog/MessageActionsDialog.kt | 4 +- .../com/nextcloud/talk/utils/ApiUtils.java | 4 ++ .../database/user/CapabilitiesUtilNew.kt | 17 +---- app/src/main/res/values/strings.xml | 2 + 14 files changed, 239 insertions(+), 53 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/talk/translate/repositories/model/LanguageModel.kt create mode 100644 app/src/main/java/com/nextcloud/talk/translate/repositories/model/LanguagesData.kt create mode 100644 app/src/main/java/com/nextcloud/talk/translate/repositories/model/LanguagesOCS.kt create mode 100644 app/src/main/java/com/nextcloud/talk/translate/repositories/model/LanguagesOverall.kt diff --git a/app/src/main/java/com/nextcloud/talk/api/NcApi.java b/app/src/main/java/com/nextcloud/talk/api/NcApi.java index 6dad5fefc6c..2f18c3dba2d 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApi.java +++ b/app/src/main/java/com/nextcloud/talk/api/NcApi.java @@ -49,6 +49,7 @@ import com.nextcloud.talk.models.json.userprofile.UserProfileFieldsOverall; import com.nextcloud.talk.models.json.userprofile.UserProfileOverall; import com.nextcloud.talk.polls.repositories.model.PollOverall; +import com.nextcloud.talk.translate.repositories.model.LanguagesOverall; import com.nextcloud.talk.translate.repositories.model.TranslationsOverall; import java.util.List; @@ -673,6 +674,10 @@ Observable translateMessage(@Header("Authorization") String @Query("toLanguage") String toLanguage, @Nullable @Query("fromLanguage") String fromLanguage); + @GET + Observable getLanguages(@Header("Authorization") String authorization, + @Url String url); + @GET Observable getReminder(@Header("Authorization") String authorization, @Url String url); diff --git a/app/src/main/java/com/nextcloud/talk/translate/repositories/TranslateRepository.kt b/app/src/main/java/com/nextcloud/talk/translate/repositories/TranslateRepository.kt index 0e925aee831..180f89c4b44 100644 --- a/app/src/main/java/com/nextcloud/talk/translate/repositories/TranslateRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/translate/repositories/TranslateRepository.kt @@ -1,5 +1,6 @@ package com.nextcloud.talk.translate.repositories +import com.nextcloud.talk.translate.repositories.model.LanguageModel import io.reactivex.Observable interface TranslateRepository { @@ -11,4 +12,9 @@ interface TranslateRepository { toLanguage: String, fromLanguage: String? ): Observable + + fun getLanguages( + authorization: String, + url: String + ): Observable> } diff --git a/app/src/main/java/com/nextcloud/talk/translate/repositories/TranslateRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/translate/repositories/TranslateRepositoryImpl.kt index 9a81d9248f4..b9426c61b9e 100644 --- a/app/src/main/java/com/nextcloud/talk/translate/repositories/TranslateRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/translate/repositories/TranslateRepositoryImpl.kt @@ -1,6 +1,7 @@ package com.nextcloud.talk.translate.repositories import com.nextcloud.talk.api.NcApi +import com.nextcloud.talk.translate.repositories.model.LanguageModel import io.reactivex.Observable import javax.inject.Inject @@ -15,4 +16,8 @@ class TranslateRepositoryImpl @Inject constructor(private val ncApi: NcApi) : Tr ): Observable { return ncApi.translateMessage(authorization, url, text, toLanguage, fromLanguage).map { it.ocs?.data!!.text } } + + override fun getLanguages(authorization: String, url: String): Observable> { + return ncApi.getLanguages(authorization, url).map { it.ocs?.data?.languages } + } } diff --git a/app/src/main/java/com/nextcloud/talk/translate/repositories/model/LanguageModel.kt b/app/src/main/java/com/nextcloud/talk/translate/repositories/model/LanguageModel.kt new file mode 100644 index 00000000000..6d925651b10 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/translate/repositories/model/LanguageModel.kt @@ -0,0 +1,27 @@ +/* + * Nextcloud Talk application + * + * @author Julius Linus + * Copyright (C) 2023 Julius Linus + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.nextcloud.talk.translate.repositories.model + +data class LanguageModel( + var from: String?, + var fromLabel: String?, + var to: String?, + var toLabel: String? +) diff --git a/app/src/main/java/com/nextcloud/talk/translate/repositories/model/LanguagesData.kt b/app/src/main/java/com/nextcloud/talk/translate/repositories/model/LanguagesData.kt new file mode 100644 index 00000000000..4660a222a37 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/translate/repositories/model/LanguagesData.kt @@ -0,0 +1,38 @@ +/* + * Nextcloud Talk application + * + * @author Julius Linus + * Copyright (C) 2023 Julius Linus + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.nextcloud.talk.translate.repositories.model + +import android.os.Parcelable +import com.bluelinelabs.logansquare.annotation.JsonField +import com.bluelinelabs.logansquare.annotation.JsonObject +import kotlinx.parcelize.Parcelize +import kotlinx.parcelize.RawValue + +@Parcelize +@JsonObject +data class LanguagesData( + @JsonField(name = ["languageDetection"]) + var languageDetection: Boolean?, + @JsonField(name = ["languages"]) + var languages: Array<@RawValue LanguageModel>? +) : Parcelable { + // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' + constructor() : this(null, null) +} diff --git a/app/src/main/java/com/nextcloud/talk/translate/repositories/model/LanguagesOCS.kt b/app/src/main/java/com/nextcloud/talk/translate/repositories/model/LanguagesOCS.kt new file mode 100644 index 00000000000..c6eff84f25c --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/translate/repositories/model/LanguagesOCS.kt @@ -0,0 +1,38 @@ +/* + * Nextcloud Talk application + * + * @author Julius Linus + * Copyright (C) 2023 Julius Linus + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.nextcloud.talk.translate.repositories.model + +import android.os.Parcelable +import com.bluelinelabs.logansquare.annotation.JsonField +import com.bluelinelabs.logansquare.annotation.JsonObject +import com.nextcloud.talk.models.json.generic.GenericMeta +import kotlinx.parcelize.Parcelize + +@Parcelize +@JsonObject +data class LanguagesOCS( + @JsonField(name = ["meta"]) + var meta: GenericMeta?, + @JsonField(name = ["data"]) + var data: LanguagesData? +) : Parcelable { + // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' + constructor() : this(null, LanguagesData()) +} diff --git a/app/src/main/java/com/nextcloud/talk/translate/repositories/model/LanguagesOverall.kt b/app/src/main/java/com/nextcloud/talk/translate/repositories/model/LanguagesOverall.kt new file mode 100644 index 00000000000..1e8a35b9001 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/translate/repositories/model/LanguagesOverall.kt @@ -0,0 +1,35 @@ +/* + * Nextcloud Talk application + * + * @author Julius Linus + * Copyright (C) 2023 Julius Linus + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.nextcloud.talk.translate.repositories.model + +import android.os.Parcelable +import com.bluelinelabs.logansquare.annotation.JsonField +import com.bluelinelabs.logansquare.annotation.JsonObject +import kotlinx.parcelize.Parcelize + +@Parcelize +@JsonObject +class LanguagesOverall( + @JsonField(name = ["ocs"]) + var ocs: LanguagesOCS? +) : Parcelable { + // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' + constructor() : this(null) +} diff --git a/app/src/main/java/com/nextcloud/talk/translate/repositories/model/TranslateOCS.kt b/app/src/main/java/com/nextcloud/talk/translate/repositories/model/TranslateOCS.kt index 66cde16d3b7..617eb20a18a 100644 --- a/app/src/main/java/com/nextcloud/talk/translate/repositories/model/TranslateOCS.kt +++ b/app/src/main/java/com/nextcloud/talk/translate/repositories/model/TranslateOCS.kt @@ -27,7 +27,7 @@ import kotlinx.parcelize.Parcelize @Parcelize @JsonObject -data class TranslateOCS( // TODO finish this model +data class TranslateOCS( @JsonField(name = ["meta"]) var meta: GenericMeta?, @JsonField(name = ["data"]) diff --git a/app/src/main/java/com/nextcloud/talk/translate/ui/TranslateActivity.kt b/app/src/main/java/com/nextcloud/talk/translate/ui/TranslateActivity.kt index a65dd45db7a..f13b170ff00 100644 --- a/app/src/main/java/com/nextcloud/talk/translate/ui/TranslateActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/translate/ui/TranslateActivity.kt @@ -38,11 +38,10 @@ import com.nextcloud.talk.R import com.nextcloud.talk.activities.BaseActivity import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.databinding.ActivityTranslateBinding +import com.nextcloud.talk.translate.repositories.model.LanguageModel import com.nextcloud.talk.translate.viewmodels.TranslateViewModel import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.utils.bundle.BundleKeys -import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew -import org.json.JSONArray import java.util.Locale import javax.inject.Inject @@ -60,6 +59,7 @@ class TranslateActivity : BaseActivity() { private var toLanguages: Array? = null private var fromLanguages: Array? = null + private var languages: Array? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -79,8 +79,17 @@ class TranslateActivity : BaseActivity() { onTranslatedState(state.msg) } - is TranslateViewModel.ErrorState -> { - onErrorState() + is TranslateViewModel.TranslationErrorState -> { + onTranslationErrorState() + } + + is TranslateViewModel.LanguagesErrorState -> { + onLanguagesErrorState() + } + + is TranslateViewModel.LanguagesRetrievedState -> { + languages = state.array + onLanguagesRetrieved() } } } @@ -88,7 +97,7 @@ class TranslateActivity : BaseActivity() { setContentView(binding.root) setupSystemColors() setupTextViews() - getLanguageOptions() + viewModel.getLanguages() setupSpinners() setupCopyButton() @@ -154,20 +163,17 @@ class TranslateActivity : BaseActivity() { binding.originalMessageTextview.text = text } - private fun getLanguageOptions() { - val currentUser = userManager.currentUser.blockingGet() - val json = JSONArray((CapabilitiesUtilNew.getLanguages(currentUser) as ArrayList<*>).toArray()) - + private fun onLanguagesRetrieved() { val fromLanguagesSet = mutableSetOf(resources.getString(R.string.translation_detect_language)) val toLanguagesSet = mutableSetOf(resources.getString(R.string.translation_device_settings)) - for (i in 0 until json.length()) { - val current = json.getJSONObject(i) - if (current.getString(FROM_ID) != Locale.getDefault().language) { - toLanguagesSet.add(current.getString(FROM_LABEL)) + for (i in languages!!.indices) { + val current = languages!![i] + if (current.from != Locale.getDefault().language) { + current.fromLabel?.let { toLanguagesSet.add(it) } } - fromLanguagesSet.add(current.getString(TO_LABEL)) + current.toLabel?.let { fromLanguagesSet.add(it) } } toLanguages = toLanguagesSet.toTypedArray() @@ -179,7 +185,7 @@ class TranslateActivity : BaseActivity() { binding.toLanguageInputLayout.isEnabled = value } - private fun showDialog() { + private fun showDialog(errorTitleRes: Int, errorMessageRes: Int) { val dialogBuilder = MaterialAlertDialogBuilder(this@TranslateActivity) .setIcon( viewThemeUtils.dialog.colorMaterialAlertDialogIcon( @@ -187,8 +193,8 @@ class TranslateActivity : BaseActivity() { R.drawable.ic_warning_white ) ) - .setTitle(R.string.translation_error_title) - .setMessage(R.string.translation_error_message) + .setTitle(errorTitleRes) + .setMessage(errorMessageRes) .setPositiveButton(R.string.nc_ok) { dialog, _ -> dialog.dismiss() } @@ -203,25 +209,19 @@ class TranslateActivity : BaseActivity() { } private fun getISOFromLanguage(language: String): String { + var result = "" + if (resources.getString(R.string.translation_device_settings) == language) { - return Locale.getDefault().language + result = Locale.getDefault().language } - return getISOFromServer(language) - } - - private fun getISOFromServer(language: String): String { - val currentUser = userManager.currentUser.blockingGet() - val json = JSONArray((CapabilitiesUtilNew.getLanguages(currentUser) as ArrayList<*>).toArray()) - - for (i in 0 until json.length()) { - val current = json.getJSONObject(i) - if (current.getString(FROM_LABEL) == language) { - return current.getString(FROM_ID) + for (current in languages!!) { + if (current.fromLabel == language) { + result = current.from!! } } - return "" + return result } private fun setupSpinners() { @@ -279,10 +279,16 @@ class TranslateActivity : BaseActivity() { enableSpinners(true) } - private fun onErrorState() { + private fun onTranslationErrorState() { + binding.progressBar.visibility = View.GONE + enableSpinners(true) + showDialog(R.string.translation_error_title, R.string.translation_error_message) + } + + private fun onLanguagesErrorState() { binding.progressBar.visibility = View.GONE enableSpinners(true) - showDialog() + showDialog(R.string.languages_error_title, R.string.languages_error_message) } companion object { diff --git a/app/src/main/java/com/nextcloud/talk/translate/viewmodels/TranslateViewModel.kt b/app/src/main/java/com/nextcloud/talk/translate/viewmodels/TranslateViewModel.kt index a8952fde91f..920716f4c66 100644 --- a/app/src/main/java/com/nextcloud/talk/translate/viewmodels/TranslateViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/translate/viewmodels/TranslateViewModel.kt @@ -6,6 +6,7 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.translate.repositories.TranslateRepository +import com.nextcloud.talk.translate.repositories.model.LanguageModel import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.utils.ApiUtils import io.reactivex.Observer @@ -21,9 +22,14 @@ class TranslateViewModel @Inject constructor( sealed interface ViewState - object StartState : ViewState + data object StartState : ViewState class TranslatedState(val msg: String) : ViewState - object ErrorState : ViewState + + class LanguagesRetrievedState(val array: Array) : ViewState + + data object LanguagesErrorState : ViewState + + data object TranslationErrorState : ViewState private val _viewState: MutableLiveData = MutableLiveData(StartState) val viewState: LiveData @@ -47,6 +53,33 @@ class TranslateViewModel @Inject constructor( ?.subscribe(TranslateObserver()) } + fun getLanguages() { + val currentUser: User = userManager.currentUser.blockingGet() + val authorization: String = ApiUtils.getCredentials(currentUser.username, currentUser.token) + val url: String = ApiUtils.getUrlForLanguages(currentUser.baseUrl) + repository.getLanguages(authorization, url) + .subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(object : Observer> { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onError(e: Throwable) { + _viewState.value = LanguagesErrorState + Log.e(TAG, "Error while retrieving languages") + } + + override fun onComplete() { + // unused atm + } + + override fun onNext(array: Array) { + _viewState.value = LanguagesRetrievedState(array) + } + }) + } + inner class TranslateObserver : Observer { override fun onSubscribe(d: Disposable) { _viewState.value = StartState @@ -57,7 +90,7 @@ class TranslateViewModel @Inject constructor( } override fun onError(e: Throwable) { - _viewState.value = ErrorState + _viewState.value = TranslationErrorState Log.e(TAG, "Error while translating message", e) } diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/MessageActionsDialog.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/MessageActionsDialog.kt index 9fd90fbcaae..6a2eafde543 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/MessageActionsDialog.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/MessageActionsDialog.kt @@ -57,7 +57,6 @@ import io.reactivex.Observer import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers -import org.json.JSONArray import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) @@ -94,8 +93,7 @@ class MessageActionsDialog( initMenuItemTranslate( !message.isDeleted && ChatMessage.MessageType.REGULAR_TEXT_MESSAGE == message.getCalculateMessageType() && - CapabilitiesUtilNew.isTranslationsSupported(user) && - JSONArray((CapabilitiesUtilNew.getLanguages(user) as ArrayList<*>).toArray()).length() > 0 + CapabilitiesUtilNew.isTranslationsSupported(user) ) initMenuReplyToMessage(message.replyable && hasChatPermission) initMenuReplyPrivately( diff --git a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java index 4b0ec13d9df..e400de42d0e 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java @@ -531,6 +531,10 @@ public static String getUrlForTranslation(String baseUrl) { return baseUrl + ocsApiVersion + "/translation/translate"; } + public static String getUrlForLanguages(String baseUrl) { + return baseUrl + ocsApiVersion + "/translation/languages"; + } + public static String getUrlForReminder(User user, String roomToken, String messageId, int version) { String url = ApiUtils.getUrlForChatMessage(version, user.getBaseUrl(), roomToken, messageId); return url + "/reminder"; diff --git a/app/src/main/java/com/nextcloud/talk/utils/database/user/CapabilitiesUtilNew.kt b/app/src/main/java/com/nextcloud/talk/utils/database/user/CapabilitiesUtilNew.kt index 5816fd356ad..50432c06178 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/database/user/CapabilitiesUtilNew.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/database/user/CapabilitiesUtilNew.kt @@ -214,12 +214,8 @@ object CapabilitiesUtilNew { @JvmStatic fun isLinkPreviewAvailable(user: User): Boolean { - if (user.capabilities?.coreCapability?.referenceApi != null && + return user.capabilities?.coreCapability?.referenceApi != null && user.capabilities?.coreCapability?.referenceApi == "true" - ) { - return true - } - return false } fun isTranslationsSupported(user: User?): Boolean { @@ -227,20 +223,13 @@ object CapabilitiesUtilNew { val capabilities = user.capabilities return capabilities?.spreedCapability?.config?.containsKey("chat") == true && capabilities.spreedCapability!!.config!!["chat"] != null && - capabilities.spreedCapability!!.config!!["chat"]!!.containsKey("translations") + capabilities.spreedCapability!!.config!!["chat"]!!.containsKey("has-translation-providers") && + capabilities.spreedCapability!!.config!!["chat"]!!["has-translation-providers"] == true } return false } - fun getLanguages(user: User?): Any? { - return if (isTranslationsSupported(user)) { - user!!.capabilities!!.spreedCapability!!.config!!["chat"]!!["translations"] - } else { - null - } - } - fun isRemindSupported(user: User?): Boolean { if (user?.capabilities != null) { val capabilities = user.capabilities diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8fa0ef657e9..1761849be1b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -710,5 +710,7 @@ How to translate with transifex: Audio Call started a call Error 429 Too Many Requests + Retrieval failed + Languages could not be retrieved