WIP rewriting translate feature to use MVVM

Signed-off-by: Julius Linus <julius.linus@nextcloud.com>
This commit is contained in:
rapterjet2004 2023-05-19 17:12:37 -05:00
parent fa7cd52b6e
commit c62a904c82
10 changed files with 187 additions and 94 deletions

View File

@ -190,7 +190,7 @@
android:theme="@style/AppTheme" />
<activity
android:name=".translate.TranslateActivity"
android:name=".translate.ui.TranslateActivity"
android:theme="@style/AppTheme" />
<activity

View File

@ -44,7 +44,7 @@ import com.nextcloud.talk.models.json.search.ContactsByNumberOverall;
import com.nextcloud.talk.models.json.signaling.SignalingOverall;
import com.nextcloud.talk.models.json.signaling.settings.SignalingSettingsOverall;
import com.nextcloud.talk.models.json.status.StatusOverall;
import com.nextcloud.talk.models.json.translations.TranslationsOverall;
import com.nextcloud.talk.translate.repositories.model.TranslationsOverall;
import com.nextcloud.talk.models.json.unifiedsearch.UnifiedSearchOverall;
import com.nextcloud.talk.models.json.userprofile.UserProfileFieldsOverall;
import com.nextcloud.talk.models.json.userprofile.UserProfileOverall;

View File

@ -150,7 +150,7 @@ import com.nextcloud.talk.remotefilebrowser.activities.RemoteFileBrowserActivity
import com.nextcloud.talk.repositories.reactions.ReactionsRepository
import com.nextcloud.talk.shareditems.activities.SharedItemsActivity
import com.nextcloud.talk.signaling.SignalingMessageReceiver
import com.nextcloud.talk.translate.TranslateActivity
import com.nextcloud.talk.translate.ui.TranslateActivity
import com.nextcloud.talk.ui.bottom.sheet.ProfileBottomSheet
import com.nextcloud.talk.ui.dialog.AttachmentDialog
import com.nextcloud.talk.ui.dialog.MessageActionsDialog

View File

@ -0,0 +1,14 @@
package com.nextcloud.talk.translate.repositories
import io.reactivex.Observable
interface TranslateRepository {
fun translateMessage(
authorization : String,
url: String,
text: String,
toLanguage: String,
fromLanguage: String?
) : Observable<String>
}

View File

@ -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<String> {
return ncApi.translateMessage(authorization, url, text, toLanguage, fromLanguage).map { it?.ocs?.data!!.text}
}
}

View File

@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.models.json.translations
package com.nextcloud.talk.translate.repositories.model
import android.os.Parcelable
import com.bluelinelabs.logansquare.annotation.JsonField

View File

@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.models.json.translations
package com.nextcloud.talk.translate.repositories.model
import android.os.Parcelable
import com.bluelinelabs.logansquare.annotation.JsonField

View File

@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.models.json.translations
package com.nextcloud.talk.translate.repositories.model
import android.os.Parcelable
import com.bluelinelabs.logansquare.annotation.JsonField

View File

@ -19,68 +19,83 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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<String>()
private var toLanguages = arrayOf<String>()
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<TranslateViewModel.TranslateUiState> { 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<TranslationsOverall> {
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"
}
}

View File

@ -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<String>? = null,
var fromLanguages : Array<String>? = null
)
private val _viewState = MutableLiveData(TranslateUiState())
val viewState : LiveData<TranslateUiState>
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<String> {
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
}
}