Browse Source

Merge pull request #3038 from nextcloud/translate-message-mvvm-impl

rewriting translate feature to use MVVM
Marcel Hibbe 2 years ago
parent
commit
fd5fec937f

+ 1 - 1
app/src/main/AndroidManifest.xml

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

+ 1 - 1
app/src/main/java/com/nextcloud/talk/api/NcApi.java

@@ -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;

+ 1 - 1
app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt

@@ -155,8 +155,8 @@ 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.ui.TranslateActivity
 import com.nextcloud.talk.signaling.SignalingMessageSender
-import com.nextcloud.talk.translate.TranslateActivity
 import com.nextcloud.talk.ui.bottom.sheet.ProfileBottomSheet
 import com.nextcloud.talk.ui.dialog.AttachmentDialog
 import com.nextcloud.talk.ui.dialog.MessageActionsDialog

+ 8 - 0
app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt

@@ -47,6 +47,8 @@ import com.nextcloud.talk.repositories.unifiedsearch.UnifiedSearchRepository
 import com.nextcloud.talk.repositories.unifiedsearch.UnifiedSearchRepositoryImpl
 import com.nextcloud.talk.shareditems.repositories.SharedItemsRepository
 import com.nextcloud.talk.shareditems.repositories.SharedItemsRepositoryImpl
+import com.nextcloud.talk.translate.repositories.TranslateRepository
+import com.nextcloud.talk.translate.repositories.TranslateRepositoryImpl
 import com.nextcloud.talk.utils.DateUtils
 import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
 import dagger.Module
@@ -107,4 +109,10 @@ class RepositoryModule {
         RequestAssistanceRepository {
         return RequestAssistanceRepositoryImpl(ncApi, userProvider)
     }
+
+    @Provides
+    fun translateRepository(ncApi: NcApi):
+        TranslateRepository {
+        return TranslateRepositoryImpl(ncApi)
+    }
 }

+ 6 - 0
app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt

@@ -31,6 +31,7 @@ import com.nextcloud.talk.polls.viewmodels.PollVoteViewModel
 import com.nextcloud.talk.raisehand.viewmodel.RaiseHandViewModel
 import com.nextcloud.talk.remotefilebrowser.viewmodels.RemoteFileBrowserItemsViewModel
 import com.nextcloud.talk.shareditems.viewmodels.SharedItemsViewModel
+import com.nextcloud.talk.translate.viewmodels.TranslateViewModel
 import com.nextcloud.talk.viewmodels.CallRecordingViewModel
 import dagger.Binds
 import dagger.MapKey
@@ -101,4 +102,9 @@ abstract class ViewModelModule {
     @IntoMap
     @ViewModelKey(RaiseHandViewModel::class)
     abstract fun raiseHandViewModel(viewModel: RaiseHandViewModel): ViewModel
+
+    @Binds
+    @IntoMap
+    @ViewModelKey(TranslateViewModel::class)
+    abstract fun translateViewModel(viewModel: TranslateViewModel): ViewModel
 }

+ 14 - 0
app/src/main/java/com/nextcloud/talk/translate/repositories/TranslateRepository.kt

@@ -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>
+}

+ 18 - 0
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
+import javax.inject.Inject
+
+class TranslateRepositoryImpl @Inject constructor(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 }
+    }
+}

+ 1 - 1
app/src/main/java/com/nextcloud/talk/models/json/translations/TranslateData.kt → 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 <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

+ 1 - 1
app/src/main/java/com/nextcloud/talk/models/json/translations/TranslateOCS.kt → 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 <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

+ 1 - 1
app/src/main/java/com/nextcloud/talk/models/json/translations/TranslationsOverall.kt → 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 <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

+ 88 - 93
app/src/main/java/com/nextcloud/talk/translate/TranslateActivity.kt → app/src/main/java/com/nextcloud/talk/translate/ui/TranslateActivity.kt

@@ -19,68 +19,82 @@
  * 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.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
+    lateinit var userManager: UserManager
 
     @Inject
-    lateinit var userManager: UserManager
+    lateinit var viewModelFactory: ViewModelProvider.Factory
+
+    lateinit var viewModel: TranslateViewModel
+    lateinit var binding: ActivityTranslateBinding
 
-    private var fromLanguages = arrayOf<String>()
-    private var toLanguages = arrayOf<String>()
-    private var text: String? = null
+    private var toLanguages: Array<String>? = null
+    private var fromLanguages: Array<String>? = null
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
+
         NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
+
         binding = ActivityTranslateBinding.inflate(layoutInflater)
+        viewModel = ViewModelProvider(this, viewModelFactory)[TranslateViewModel::class.java]
+
+        viewModel.viewState.observe(this) { state ->
+            when (state) {
+                is TranslateViewModel.StartState -> {
+                    onStartState()
+                }
+
+                is TranslateViewModel.TranslatedState -> {
+                    onTranslatedState(state.msg)
+                }
+
+                is TranslateViewModel.ErrorState -> {
+                    onErrorState()
+                }
+            }
+        }
         setupActionBar()
         setContentView(binding.root)
         setupSystemColors()
         setupTextViews()
+        getLanguageOptions()
         setupSpinners()
         setupCopyButton()
-        getLanguageOptions()
 
         if (savedInstanceState == null) {
-            translate(null, Locale.getDefault().language)
+            val text = intent.extras!!.getString(BundleKeys.KEY_TRANSLATE_MESSAGE)
+            viewModel.translateMessage(Locale.getDefault().language, null, text!!)
         } else {
             binding.translatedMessageTextview.text = savedInstanceState.getString(BundleKeys.SAVED_TRANSLATED_MESSAGE)
         }
@@ -90,7 +104,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 +150,12 @@ 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 = intent.extras!!.getString(BundleKeys.KEY_TRANSLATE_MESSAGE)
+        binding.originalMessageTextview.text = 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 +170,8 @@ class TranslateActivity : BaseActivity() {
             fromLanguagesSet.add(current.getString(TO_LABEL))
         }
 
-        fromLanguages = fromLanguagesSet.toTypedArray()
         toLanguages = toLanguagesSet.toTypedArray()
-        fillSpinners()
+        fromLanguages = fromLanguagesSet.toTypedArray()
     }
 
     private fun enableSpinners(value: Boolean) {
@@ -169,63 +179,27 @@ 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
-        }
-
-        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
-                }
-
-                override fun onNext(translationOverall: TranslationsOverall) {
-                    binding.progressBar.visibility = View.GONE
-                    binding.translatedMessageContainer.visibility = View.VISIBLE
-                    binding.translatedMessageTextview.text = translationOverall.ocs?.data?.text
-                }
+    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()
+            }
 
-                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)
-                    )
-                }
+        viewThemeUtils.dialog.colorMaterialAlertDialogBackground(context, dialogBuilder)
 
-                override fun onComplete() {
-                    // nothing?
-                }
-            })
+        val dialog = dialogBuilder.show()
 
-        enableSpinners(true)
+        viewThemeUtils.platform.colorTextButtons(
+            dialog.getButton(AlertDialog.BUTTON_POSITIVE)
+        )
     }
 
     private fun getISOFromLanguage(language: String): String {
@@ -237,7 +211,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()) {
@@ -254,43 +228,64 @@ class TranslateActivity : BaseActivity() {
         viewThemeUtils.material.colorTextInputLayout(binding.fromLanguageInputLayout)
         viewThemeUtils.material.colorTextInputLayout(binding.toLanguageInputLayout)
         fillSpinners()
+        val text = intent.extras!!.getString(BundleKeys.KEY_TRANSLATE_MESSAGE)
 
         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, text!!)
         }
 
         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, text!!)
         }
     }
 
     private fun fillSpinners() {
         binding.fromLanguage.setAdapter(
-            ArrayAdapter(this, android.R.layout.simple_spinner_dropdown_item, fromLanguages)
+            ArrayAdapter(this, android.R.layout.simple_spinner_dropdown_item, fromLanguages!!)
         )
-        if (fromLanguages.isNotEmpty()) {
-            binding.fromLanguage.setText(fromLanguages[0])
+        if (fromLanguages!!.isNotEmpty()) {
+            binding.fromLanguage.setText(fromLanguages!![0])
         }
 
         binding.toLanguage.setAdapter(
-            ArrayAdapter(this, android.R.layout.simple_spinner_dropdown_item, toLanguages)
+            ArrayAdapter(this, android.R.layout.simple_spinner_dropdown_item, toLanguages!!)
         )
-        if (toLanguages.isNotEmpty()) {
-            binding.toLanguage.setText(toLanguages[0])
+        if (toLanguages!!.isNotEmpty()) {
+            binding.toLanguage.setText(toLanguages!![0])
         }
     }
 
     private fun setItems() {
-        binding.fromLanguage.setSimpleItems(fromLanguages)
-        binding.toLanguage.setSimpleItems(toLanguages)
+        binding.fromLanguage.setSimpleItems(fromLanguages!!)
+        binding.toLanguage.setSimpleItems(toLanguages!!)
+    }
+
+    private fun onStartState() {
+        enableSpinners(false)
+        binding.translatedMessageContainer.visibility = View.GONE
+        binding.progressBar.visibility = View.VISIBLE
+        binding.copyTranslatedMessage.visibility = View.GONE
+    }
+
+    private fun onTranslatedState(msg: String) {
+        binding.progressBar.visibility = View.GONE
+        binding.translatedMessageContainer.visibility = View.VISIBLE
+        binding.translatedMessageTextview.text = msg
+        binding.copyTranslatedMessage.visibility = View.VISIBLE
+        enableSpinners(true)
+    }
+
+    private fun onErrorState() {
+        binding.progressBar.visibility = View.GONE
+        enableSpinners(true)
+        showDialog()
     }
 
     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"

+ 71 - 0
app/src/main/java/com/nextcloud/talk/translate/viewmodels/TranslateViewModel.kt

@@ -0,0 +1,71 @@
+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,
+    private val userManager: UserManager
+) : ViewModel() {
+
+    sealed interface ViewState
+
+    object StartState : ViewState
+    class TranslatedState(val msg: String) : ViewState
+    object ErrorState : ViewState
+
+    private val _viewState: MutableLiveData<ViewState> = MutableLiveData(StartState)
+    val viewState: LiveData<ViewState>
+        get() = _viewState
+
+    fun translateMessage(toLanguage: String, fromLanguage: String?, text: 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 }
+        Log.i(TAG, "translateMessage Called")
+        repository.translateMessage(
+            authorization,
+            url,
+            text,
+            toLanguage,
+            calculatedFromLanguage
+        )
+            .subscribeOn(Schedulers.io())
+            ?.observeOn(AndroidSchedulers.mainThread())
+            ?.subscribe(TranslateObserver())
+    }
+
+    inner class TranslateObserver : Observer<String> {
+        override fun onSubscribe(d: Disposable) {
+            _viewState.value = StartState
+        }
+
+        override fun onNext(translatedMessage: String) {
+            _viewState.value = TranslatedState(translatedMessage)
+        }
+
+        override fun onError(e: Throwable) {
+            _viewState.value = ErrorState
+            Log.e(TAG, "Error while translating message", e)
+        }
+
+        override fun onComplete() {
+            // nothing?
+        }
+    }
+    companion object {
+        private val TAG = TranslateViewModel::class.simpleName
+    }
+}