From c62a904c82ccef5e05acc05eb30e97383a4c8582 Mon Sep 17 00:00:00 2001 From: rapterjet2004 Date: Fri, 19 May 2023 17:12:37 -0500 Subject: [PATCH] WIP rewriting translate feature to use MVVM Signed-off-by: Julius Linus --- app/src/main/AndroidManifest.xml | 2 +- .../java/com/nextcloud/talk/api/NcApi.java | 2 +- .../com/nextcloud/talk/chat/ChatActivity.kt | 2 +- .../repositories/TranslateRepository.kt | 14 ++ .../repositories/TranslateRepositoryImpl.kt | 18 ++ .../repositories/model}/TranslateData.kt | 2 +- .../repositories/model}/TranslateOCS.kt | 2 +- .../model}/TranslationsOverall.kt | 2 +- .../translate/{ => ui}/TranslateActivity.kt | 158 ++++++++---------- .../viewmodels/TranslateViewModel.kt | 79 +++++++++ 10 files changed, 187 insertions(+), 94 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/talk/translate/repositories/TranslateRepository.kt create mode 100644 app/src/main/java/com/nextcloud/talk/translate/repositories/TranslateRepositoryImpl.kt rename app/src/main/java/com/nextcloud/talk/{models/json/translations => translate/repositories/model}/TranslateData.kt (95%) rename app/src/main/java/com/nextcloud/talk/{models/json/translations => translate/repositories/model}/TranslateOCS.kt (96%) rename app/src/main/java/com/nextcloud/talk/{models/json/translations => translate/repositories/model}/TranslationsOverall.kt (95%) rename app/src/main/java/com/nextcloud/talk/translate/{ => ui}/TranslateActivity.kt (65%) create mode 100644 app/src/main/java/com/nextcloud/talk/translate/viewmodels/TranslateViewModel.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 05ab0ba8e..bacda5cf3 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -190,7 +190,7 @@ android:theme="@style/AppTheme" /> +} \ No newline at end of file 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 new file mode 100644 index 000000000..8fa5b780d --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/translate/repositories/TranslateRepositoryImpl.kt @@ -0,0 +1,18 @@ +package com.nextcloud.talk.translate.repositories + +import com.nextcloud.talk.api.NcApi +import io.reactivex.Observable + +class TranslateRepositoryImpl(private val ncApi: NcApi) : TranslateRepository { + + override fun translateMessage( + authorization: String, + url: String, + text: String, + toLanguage: String, + fromLanguage: String? + ): Observable { + return ncApi.translateMessage(authorization, url, text, toLanguage, fromLanguage).map { it?.ocs?.data!!.text} + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/models/json/translations/TranslateData.kt b/app/src/main/java/com/nextcloud/talk/translate/repositories/model/TranslateData.kt similarity index 95% rename from app/src/main/java/com/nextcloud/talk/models/json/translations/TranslateData.kt rename to app/src/main/java/com/nextcloud/talk/translate/repositories/model/TranslateData.kt index 9ebfdd971..22bc1073d 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/translations/TranslateData.kt +++ b/app/src/main/java/com/nextcloud/talk/translate/repositories/model/TranslateData.kt @@ -17,7 +17,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.nextcloud.talk.models.json.translations +package com.nextcloud.talk.translate.repositories.model import android.os.Parcelable import com.bluelinelabs.logansquare.annotation.JsonField diff --git a/app/src/main/java/com/nextcloud/talk/models/json/translations/TranslateOCS.kt b/app/src/main/java/com/nextcloud/talk/translate/repositories/model/TranslateOCS.kt similarity index 96% rename from app/src/main/java/com/nextcloud/talk/models/json/translations/TranslateOCS.kt rename to app/src/main/java/com/nextcloud/talk/translate/repositories/model/TranslateOCS.kt index 74ba31327..66cde16d3 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/translations/TranslateOCS.kt +++ b/app/src/main/java/com/nextcloud/talk/translate/repositories/model/TranslateOCS.kt @@ -17,7 +17,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.nextcloud.talk.models.json.translations +package com.nextcloud.talk.translate.repositories.model import android.os.Parcelable import com.bluelinelabs.logansquare.annotation.JsonField diff --git a/app/src/main/java/com/nextcloud/talk/models/json/translations/TranslationsOverall.kt b/app/src/main/java/com/nextcloud/talk/translate/repositories/model/TranslationsOverall.kt similarity index 95% rename from app/src/main/java/com/nextcloud/talk/models/json/translations/TranslationsOverall.kt rename to app/src/main/java/com/nextcloud/talk/translate/repositories/model/TranslationsOverall.kt index 1b832e9cd..577c0e9ec 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/translations/TranslationsOverall.kt +++ b/app/src/main/java/com/nextcloud/talk/translate/repositories/model/TranslationsOverall.kt @@ -17,7 +17,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.nextcloud.talk.models.json.translations +package com.nextcloud.talk.translate.repositories.model import android.os.Parcelable import com.bluelinelabs.logansquare.annotation.JsonField diff --git a/app/src/main/java/com/nextcloud/talk/translate/TranslateActivity.kt b/app/src/main/java/com/nextcloud/talk/translate/ui/TranslateActivity.kt similarity index 65% rename from app/src/main/java/com/nextcloud/talk/translate/TranslateActivity.kt rename to app/src/main/java/com/nextcloud/talk/translate/ui/TranslateActivity.kt index f08e1697b..e37b7cf9f 100644 --- a/app/src/main/java/com/nextcloud/talk/translate/TranslateActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/translate/ui/TranslateActivity.kt @@ -19,68 +19,83 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.nextcloud.talk.translate +package com.nextcloud.talk.translate.ui +import android.app.AlertDialog import android.content.ClipData import android.content.ClipboardManager import android.content.Context import android.graphics.drawable.ColorDrawable import android.os.Bundle import android.text.method.ScrollingMovementMethod -import android.util.Log import android.view.View import android.widget.AdapterView import android.widget.ArrayAdapter -import androidx.appcompat.app.AlertDialog +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProvider import autodagger.AutoInjector import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.nextcloud.talk.R import com.nextcloud.talk.activities.BaseActivity -import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.application.NextcloudTalkApplication -import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.databinding.ActivityTranslateBinding -import com.nextcloud.talk.models.json.translations.TranslationsOverall +import com.nextcloud.talk.translate.viewmodels.TranslateViewModel import com.nextcloud.talk.users.UserManager -import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.bundle.BundleKeys import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew -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 java.util.Locale import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) class TranslateActivity : BaseActivity() { - private lateinit var binding: ActivityTranslateBinding - - @Inject - lateinit var ncApi: NcApi @Inject lateinit var userManager: UserManager - private var fromLanguages = arrayOf() - private var toLanguages = arrayOf() - private var text: String? = null + @Inject + lateinit var viewModelFactory: ViewModelProvider.Factory + + private lateinit var viewModel: TranslateViewModel + private lateinit var binding: ActivityTranslateBinding + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) + binding = ActivityTranslateBinding.inflate(layoutInflater) + viewModel = ViewModelProvider(this, viewModelFactory)[TranslateViewModel::class.java] // fixme bug here + + setupActionBar() setContentView(binding.root) setupSystemColors() setupTextViews() + getLanguageOptions() setupSpinners() setupCopyButton() - getLanguageOptions() + + val translateViewModelObserver = Observer { state -> + enableSpinners(state.enableSpinners) + + binding.progressBar.visibility = if(state.showProgressBar) View.VISIBLE else View.GONE + + binding.translatedMessageContainer.visibility = + if(state.showTranslatedMessageContainer) View.VISIBLE else View.GONE + + if(state.translatedMessageText != null) + binding.translatedMessageTextview.text = state.translatedMessageText + + if(state.errorOccurred) + showDialog() + } + + viewModel.viewState.observe(this, translateViewModelObserver) if (savedInstanceState == null) { - translate(null, Locale.getDefault().language) + viewModel.translateMessage(Locale.getDefault().language, null) } else { binding.translatedMessageTextview.text = savedInstanceState.getString(BundleKeys.SAVED_TRANSLATED_MESSAGE) } @@ -90,7 +105,6 @@ class TranslateActivity : BaseActivity() { super.onResume() setItems() } - override fun onSaveInstanceState(outState: Bundle) { outState.run { putString(BundleKeys.SAVED_TRANSLATED_MESSAGE, binding.translatedMessageTextview.text.toString()) @@ -137,14 +151,14 @@ class TranslateActivity : BaseActivity() { binding.originalMessageTextview.movementMethod = ScrollingMovementMethod() binding.translatedMessageTextview.movementMethod = ScrollingMovementMethod() - val bundle = intent.extras - binding.originalMessageTextview.text = bundle?.getString(BundleKeys.KEY_TRANSLATE_MESSAGE) - text = bundle?.getString(BundleKeys.KEY_TRANSLATE_MESSAGE) + val text = bundle?.getString(BundleKeys.KEY_TRANSLATE_MESSAGE) + binding.originalMessageTextview.text = text + viewModel.viewState.value!!.originalMessageText = text } private fun getLanguageOptions() { - val currentUser: User = userManager.currentUser.blockingGet() + val currentUser = userManager.currentUser.blockingGet() val json = JSONArray(CapabilitiesUtilNew.getLanguages(currentUser).toString()) val fromLanguagesSet = mutableSetOf(resources.getString(R.string.translation_detect_language)) @@ -159,9 +173,8 @@ class TranslateActivity : BaseActivity() { fromLanguagesSet.add(current.getString(TO_LABEL)) } - fromLanguages = fromLanguagesSet.toTypedArray() - toLanguages = toLanguagesSet.toTypedArray() - fillSpinners() + viewModel.viewState.value!!.toLanguages = toLanguagesSet.toTypedArray() + viewModel.viewState.value!!.fromLanguages = fromLanguagesSet.toTypedArray() } private fun enableSpinners(value: Boolean) { @@ -169,65 +182,31 @@ class TranslateActivity : BaseActivity() { binding.toLanguageInputLayout.isEnabled = value } - private fun translate(fromLanguage: String?, toLanguage: String) { - val currentUser: User = userManager.currentUser.blockingGet() - val credentials: String = ApiUtils.getCredentials(currentUser.username, currentUser.token) - val translateURL = ApiUtils.getUrlForTranslation(currentUser.baseUrl) - val calculatedFromLanguage = if (fromLanguage == null || fromLanguage == "") { - null - } else { - fromLanguage - } + private fun showDialog() { + val dialogBuilder = MaterialAlertDialogBuilder(this@TranslateActivity) + .setIcon( + viewThemeUtils.dialog.colorMaterialAlertDialogIcon( + context, + R.drawable.ic_warning_white + ) + ) + .setTitle(R.string.translation_error_title) + .setMessage(R.string.translation_error_message) + .setPositiveButton(R.string.nc_ok) { dialog, _ -> + dialog.dismiss() + } - ncApi.translateMessage(credentials, translateURL, text, toLanguage, calculatedFromLanguage) - ?.subscribeOn(Schedulers.io()) - ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - enableSpinners(false) - binding.translatedMessageContainer.visibility = View.GONE - binding.progressBar.visibility = View.VISIBLE - } + viewThemeUtils.dialog.colorMaterialAlertDialogBackground(context, dialogBuilder) - override fun onNext(translationOverall: TranslationsOverall) { - binding.progressBar.visibility = View.GONE - binding.translatedMessageContainer.visibility = View.VISIBLE - binding.translatedMessageTextview.text = translationOverall.ocs?.data?.text - } + val dialog = dialogBuilder.show() - override fun onError(e: Throwable) { - Log.w(TAG, "Error while translating message", e) - binding.progressBar.visibility = View.GONE - val dialogBuilder = MaterialAlertDialogBuilder(this@TranslateActivity) - .setIcon( - viewThemeUtils.dialog.colorMaterialAlertDialogIcon( - context, - R.drawable.ic_warning_white - ) - ) - .setTitle(R.string.translation_error_title) - .setMessage(R.string.translation_error_message) - .setPositiveButton(R.string.nc_ok) { dialog, _ -> - dialog.dismiss() - } - - viewThemeUtils.dialog.colorMaterialAlertDialogBackground(context, dialogBuilder) - - val dialog = dialogBuilder.show() - - viewThemeUtils.platform.colorTextButtons( - dialog.getButton(AlertDialog.BUTTON_POSITIVE) - ) - } - - override fun onComplete() { - // nothing? - } - }) - - enableSpinners(true) + viewThemeUtils.platform.colorTextButtons( + dialog.getButton(AlertDialog.BUTTON_POSITIVE) + ) } + + private fun getISOFromLanguage(language: String): String { if (resources.getString(R.string.translation_device_settings) == language) { return Locale.getDefault().language @@ -237,7 +216,7 @@ class TranslateActivity : BaseActivity() { } private fun getISOFromServer(language: String): String { - val currentUser: User = userManager.currentUser.blockingGet() + val currentUser = userManager.currentUser.blockingGet() val json = JSONArray(CapabilitiesUtilNew.getLanguages(currentUser).toString()) for (i in 0 until json.length()) { @@ -258,17 +237,20 @@ class TranslateActivity : BaseActivity() { binding.fromLanguage.onItemClickListener = AdapterView.OnItemClickListener { parent, _, position, _ -> val fromLabel: String = getISOFromLanguage(parent.getItemAtPosition(position).toString()) val toLabel: String = getISOFromLanguage(binding.toLanguage.text.toString()) - translate(fromLabel, toLabel) + viewModel.translateMessage(toLabel, fromLabel) } binding.toLanguage.onItemClickListener = AdapterView.OnItemClickListener { parent, _, position, _ -> val toLabel: String = getISOFromLanguage(parent.getItemAtPosition(position).toString()) val fromLabel: String = getISOFromLanguage(binding.fromLanguage.text.toString()) - translate(fromLabel, toLabel) + viewModel.translateMessage(toLabel, fromLabel) } } private fun fillSpinners() { + val fromLanguages = viewModel.viewState.value!!.fromLanguages!! + val toLanguages = viewModel.viewState.value!!.toLanguages!! + binding.fromLanguage.setAdapter( ArrayAdapter(this, android.R.layout.simple_spinner_dropdown_item, fromLanguages) ) @@ -285,14 +267,14 @@ class TranslateActivity : BaseActivity() { } private fun setItems() { - binding.fromLanguage.setSimpleItems(fromLanguages) - binding.toLanguage.setSimpleItems(toLanguages) + binding.fromLanguage.setSimpleItems(viewModel.viewState.value!!.fromLanguages!!) + binding.toLanguage.setSimpleItems(viewModel.viewState.value!!.toLanguages!!) } companion object { - private val TAG = TranslateActivity::class.simpleName private const val FROM_ID = "from" private const val FROM_LABEL = "fromLabel" private const val TO_LABEL = "toLabel" } + } 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 new file mode 100644 index 000000000..8b352146b --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/translate/viewmodels/TranslateViewModel.kt @@ -0,0 +1,79 @@ +package com.nextcloud.talk.translate.viewmodels + +import android.util.Log +import androidx.lifecycle.LiveData +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.users.UserManager +import com.nextcloud.talk.utils.ApiUtils +import io.reactivex.Observer +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.Disposable +import io.reactivex.schedulers.Schedulers +import javax.inject.Inject + +class TranslateViewModel @Inject constructor(private val repository: TranslateRepository) : ViewModel() { + + @Inject + lateinit var userManager: UserManager + + data class TranslateUiState( + var enableSpinners : Boolean = false, + var showProgressBar : Boolean = false, + var showTranslatedMessageContainer : Boolean = false, + var translatedMessageText : String? = null, + var originalMessageText : String? = null, + var errorOccurred : Boolean = false, + var toLanguages : Array? = null, + var fromLanguages : Array? = null + ) + + private val _viewState = MutableLiveData(TranslateUiState()) + val viewState : LiveData + get() = _viewState + + fun translateMessage(toLanguage: String, fromLanguage: String?) { + val currentUser: User = userManager.currentUser.blockingGet() + val authorization: String = ApiUtils.getCredentials(currentUser.username, currentUser.token) + val url: String = ApiUtils.getUrlForTranslation(currentUser.baseUrl) + val calculatedFromLanguage = if (fromLanguage == null || fromLanguage == "") { null } else { fromLanguage } + + repository.translateMessage(authorization, url,_viewState.value!!.originalMessageText!!,toLanguage, + calculatedFromLanguage) + .subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(TranslateObserver()) + } + + inner class TranslateObserver() : Observer { + override fun onSubscribe(d: Disposable) { + _viewState.value!!.enableSpinners = false + _viewState.value!!.showTranslatedMessageContainer = false + _viewState.value!!.showProgressBar = true + } + + override fun onNext(translatedMessage: String) { + _viewState.value!!.showProgressBar = false + _viewState.value!!.showTranslatedMessageContainer = true + _viewState.value!!.translatedMessageText = translatedMessage + _viewState.value!!.enableSpinners = true + } + + override fun onError(e: Throwable) { + _viewState.value!!.showProgressBar = false + _viewState.value!!.errorOccurred = true + _viewState.value!!.enableSpinners = true + Log.w(TAG, "Error while translating message", e) + } + + override fun onComplete() { + // nothing? + } + } + companion object { + private val TAG = TranslateViewModel::class.simpleName + } + +} \ No newline at end of file