Bläddra i källkod

add Event.kt for at-once proccessing value in liveData
refoactoring all files in presentation layer in feature_auth

MrOzOn 5 år sedan
förälder
incheckning
cdaff1b8f5

+ 1 - 1
app/src/main/java/com/mrozon/healthdiary/presentation/main/MainActivity.kt

@@ -89,7 +89,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>(){
     override fun subscribeUi() {
 
         viewModel.cleared.observe(this, Observer { cleared ->
-            if (cleared) {
+            cleared.getContentIfNotHandled()?.let {
                 navController.navigate(R.id.action_global_loginFragment)
             }
         })

+ 4 - 3
app/src/main/java/com/mrozon/healthdiary/presentation/main/MainActivityViewModel.kt

@@ -5,6 +5,7 @@ import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
 import com.mrozon.core_api.entity.User
 import com.mrozon.healthdiary.data.UserRepository
+import com.mrozon.utils.Event
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
@@ -19,15 +20,15 @@ class MainActivityViewModel @Inject constructor(
 
     val currentUser = userRepository.getLocalUser()
 
-    private val _cleared = MutableLiveData<Boolean>(false)
-    val cleared: LiveData<Boolean>
+    private val _cleared = MutableLiveData<Event<Unit>>()
+    val cleared: LiveData<Event<Unit>>
         get() = _cleared
 
     fun logoutUser(user: User) {
         CoroutineScope(Dispatchers.IO).launch(getJobErrorHandler()) {
             userRepository.clearLocalUser(user)
             withContext(Dispatchers.Main){
-                _cleared.value = true
+                _cleared.value = Event(Unit)
             }
         }
     }

+ 17 - 14
feature_auth/src/main/java/com/mrozon/feature_auth/presentation/LoginFragment.kt

@@ -70,23 +70,26 @@ class LoginFragment : BaseFragment<FragmentLoginBinding>() {
             }
         })
 
-        viewModel.loggedUser.observe(viewLifecycleOwner, Observer { result ->
-            if(result!=null){
-                when (result.status) {
-                    Result.Status.LOADING -> {
-                        binding?.progressBar?.visible(true)
-                    }
-                    Result.Status.SUCCESS -> {
-                        binding?.progressBar?.visible(false)
-                        navigator.navigateToListPerson(findNavController())
-                    }
-                    Result.Status.ERROR -> {
-                        binding?.progressBar?.visible(false)
-                        binding?.btnLogin?.shake()
-                        showError(result.message!!)
+        viewModel.loggedUser.observe(viewLifecycleOwner, Observer { event ->
+            event.getContentIfNotHandled().let { result ->
+                if(result!=null){
+                    when (result.status) {
+                        Result.Status.LOADING -> {
+                            binding?.progressBar?.visible(true)
+                        }
+                        Result.Status.SUCCESS -> {
+                            binding?.progressBar?.visible(false)
+                            navigator.navigateToListPerson(findNavController())
+                        }
+                        Result.Status.ERROR -> {
+                            binding?.progressBar?.visible(false)
+                            binding?.btnLogin?.shake()
+                            showError(result.message!!)
+                        }
                     }
                 }
             }
+
         })
 
     }

+ 4 - 3
feature_auth/src/main/java/com/mrozon/feature_auth/presentation/LoginFragmentViewModel.kt

@@ -8,6 +8,7 @@ import com.mrozon.core_api.entity.User
 import com.mrozon.core_api.providers.CoroutineContextProvider
 import com.mrozon.feature_auth.data.UserAuthRepository
 import com.mrozon.feature_auth.data.UserAuthRepositoryImpl
+import com.mrozon.utils.Event
 import com.mrozon.utils.base.BaseViewModel
 import com.mrozon.utils.network.Result
 //import com.mrozon.utils.extension.asFlow
@@ -23,8 +24,8 @@ class LoginFragmentViewModel @Inject constructor(
     private val coroutineContextProvider: CoroutineContextProvider
 ): BaseViewModel() {
 
-    private val _loggedUser = MutableLiveData<Result<User>?>(null)
-    val loggedUser: LiveData<Result<User>?>
+    private val _loggedUser = MutableLiveData<Event<Result<User>>>()
+    val loggedUser: LiveData<Event<Result<User>>>
         get() = _loggedUser
 
     @ExperimentalCoroutinesApi
@@ -66,7 +67,7 @@ class LoginFragmentViewModel @Inject constructor(
         viewModelScope.launch(coroutineContextProvider.IO){
             repository.loginUser(userName,psw).collect {
                 withContext(coroutineContextProvider.Main) {
-                    _loggedUser.value = it
+                    _loggedUser.value = Event(it)
                 }
             }
         }

+ 7 - 6
feature_auth/src/main/java/com/mrozon/feature_auth/presentation/RegistrationFragment.kt

@@ -66,13 +66,14 @@ class RegistrationFragment: BaseFragment<FragmentRegistrationBinding>() {
     @FlowPreview
     override fun subscribeUi() {
 
-        viewModel.error.observe(viewLifecycleOwner, Observer {error ->
-            if(error!=null)
+        viewModel.error.observe(viewLifecycleOwner, Observer {event ->
+            event.getContentIfNotHandled()?.let { error ->
                 showError(error) {}
+            }
         })
 
-        viewModel.registeredUser.observe(viewLifecycleOwner, Observer { result ->
-            if(result!=null){
+        viewModel.registeredUser.observe(viewLifecycleOwner, Observer { event ->
+            event.getContentIfNotHandled()?.let { result ->
                 when (result.status) {
                     Result.Status.LOADING -> {
                         binding?.progressBar?.visible(true)
@@ -80,8 +81,8 @@ class RegistrationFragment: BaseFragment<FragmentRegistrationBinding>() {
                     Result.Status.SUCCESS -> {
                         binding?.progressBar?.visible(false)
                         showInfo(getString(R.string.userRegistered)) {
-                                navigator.navigateToLoginUser(findNavController(), result.data?.email?:"")
-                            }
+                            navigator.navigateToLoginUser(findNavController(), result.data?.email?:"")
+                        }
                     }
                     Result.Status.ERROR -> {
                         binding?.progressBar?.visible(false)

+ 7 - 5
feature_auth/src/main/java/com/mrozon/feature_auth/presentation/RegistrationFragmentViewModel.kt

@@ -1,5 +1,6 @@
 package com.mrozon.feature_auth.presentation
 
+import android.util.EventLog
 import android.util.Patterns.EMAIL_ADDRESS
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
@@ -8,6 +9,7 @@ import com.mrozon.core_api.entity.User
 import com.mrozon.core_api.providers.CoroutineContextProvider
 import com.mrozon.feature_auth.data.UserAuthRepository
 import com.mrozon.feature_auth.data.UserAuthRepositoryImpl
+import com.mrozon.utils.Event
 import com.mrozon.utils.base.BaseViewModel
 import com.mrozon.utils.network.Result
 //import com.mrozon.utils.extension.asFlow
@@ -24,12 +26,12 @@ class RegistrationFragmentViewModel @Inject constructor(
     private val coroutineContextProvider: CoroutineContextProvider
 ): BaseViewModel() {
 
-    private val _error = MutableLiveData<String?>()
-    val error: LiveData<String?>
+    private val _error = MutableLiveData<Event<String>>()
+    val error: LiveData<Event<String>>
         get() = _error
 
-    private val _registeredUser = MutableLiveData<Result<User>?>(null)
-    val registeredUser: LiveData<Result<User>?>
+    private val _registeredUser = MutableLiveData<Event<Result<User>>>()
+    val registeredUser: LiveData<Event<Result<User>>>
         get() = _registeredUser
 
     @ExperimentalCoroutinesApi
@@ -94,7 +96,7 @@ class RegistrationFragmentViewModel @Inject constructor(
         viewModelScope.launch(coroutineContextProvider.IO) {
             repository.registerUser(user,psw).collect {
                 withContext(coroutineContextProvider.Main) {
-                    _registeredUser.value = it
+                    _registeredUser.value = Event(it)
                 }
             }
         }

+ 5 - 1
feature_auth/src/test/java/com/mrozon/feature_auth/data/UserAuthRepositoryImplTest.kt

@@ -3,6 +3,7 @@ package com.mrozon.feature_auth.data
 import com.mrozon.core_api.db.HealthDiaryDao
 import com.mrozon.core_api.network.HealthDiaryService
 import com.mrozon.core_api.network.model.*
+import com.mrozon.core_api.security.SecurityTokenService
 import com.mrozon.utils.network.Result.Companion.loading
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -37,6 +38,9 @@ class UserAuthRepositoryImplTest {
     @Mock
     lateinit var dao: HealthDiaryDao
 
+    @Mock
+    lateinit var securityTokenService: SecurityTokenService
+
     lateinit var repository: UserAuthRepository
 
     @get:Rule
@@ -45,7 +49,7 @@ class UserAuthRepositoryImplTest {
     @Before
     fun setUp() {
         val source = UserAuthRemoteDataSource(apiService)
-        repository = UserAuthRepositoryImpl(source, dao)
+        repository = UserAuthRepositoryImpl(source, dao, securityTokenService)
     }
 
     @Test

+ 6 - 5
feature_auth/src/test/java/com/mrozon/feature_auth/presentation/LoginFragmentViewModelTest.kt

@@ -5,6 +5,7 @@ import androidx.lifecycle.Observer
 import com.mrozon.core_api.entity.User
 import com.mrozon.feature_auth.data.CoroutineTestRule
 import com.mrozon.feature_auth.data.UserAuthRepository
+import com.mrozon.utils.Event
 import com.mrozon.utils.network.Result
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.FlowPreview
@@ -40,7 +41,7 @@ class LoginFragmentViewModelTest {
     lateinit var repository: UserAuthRepository
 
     @Mock
-    lateinit var observer: Observer<Result<User>?>
+    lateinit var observer: Observer<Event<Result<User>>>
 
     @Mock
     lateinit var validateObserver: Observer<Boolean>
@@ -72,8 +73,8 @@ class LoginFragmentViewModelTest {
 
         viewModel.loginUser()
 
-        Mockito.verify(observer).onChanged(Result.loading())
-        Mockito.verify(observer).onChanged(Result.success(user))
+        Mockito.verify(observer).onChanged(Event(Result.loading()))
+        Mockito.verify(observer).onChanged(Event(Result.success(user)))
     }
 
     @Test
@@ -89,8 +90,8 @@ class LoginFragmentViewModelTest {
 
         viewModel.loginUser()
 
-        Mockito.verify(observer).onChanged(Result.loading())
-        Mockito.verify(observer).onChanged(Result.error(error))
+        Mockito.verify(observer).onChanged(Event(Result.loading()))
+        Mockito.verify(observer).onChanged(Event(Result.error(error)))
     }
 
     @FlowPreview

+ 6 - 5
feature_auth/src/test/java/com/mrozon/feature_auth/presentation/RegistrationFragmentViewModelTest.kt

@@ -6,6 +6,7 @@ import com.mrozon.core_api.entity.User
 import com.mrozon.core_api.providers.CoroutineContextProvider
 import com.mrozon.feature_auth.data.CoroutineTestRule
 import com.mrozon.feature_auth.data.UserAuthRepository
+import com.mrozon.utils.Event
 import com.mrozon.utils.network.Result
 import kotlinx.coroutines.*
 import kotlinx.coroutines.flow.flowOf
@@ -38,7 +39,7 @@ class RegistrationFragmentViewModelTest {
     lateinit var repository: UserAuthRepository
 
     @Mock
-    lateinit var observer: Observer<Result<User>?>
+    lateinit var observer: Observer<Event<Result<User>>>
 
     @Mock
     lateinit var validateObserver: Observer<ValidateDataError>
@@ -75,8 +76,8 @@ class RegistrationFragmentViewModelTest {
 
         viewModel.registerUser()
 
-        Mockito.verify(observer).onChanged(Result.loading())
-        Mockito.verify(observer).onChanged(Result.success(user))
+        Mockito.verify(observer).onChanged(Event(Result.loading()))
+        Mockito.verify(observer).onChanged(Event(Result.success(user)))
     }
 
     @Test
@@ -98,8 +99,8 @@ class RegistrationFragmentViewModelTest {
 
         viewModel.registerUser()
 
-        Mockito.verify(observer).onChanged(Result.loading())
-        Mockito.verify(observer).onChanged(Result.error(error))
+        Mockito.verify(observer).onChanged(Event(Result.loading()))
+        Mockito.verify(observer).onChanged(Event(Result.error(error)))
     }
 
     @FlowPreview

+ 44 - 0
utils/src/main/java/com/mrozon/utils/Event.kt

@@ -0,0 +1,44 @@
+package com.mrozon.utils
+
+import java.util.Objects.toString
+
+/**
+ * Used as a wrapper for data that is exposed via a LiveData that represents an event.
+ */
+open class Event<out T> (private val content: T) {
+
+    var hasBeenHandled = false
+        private set
+
+    /**
+     * Returns the content and prevents its use again.
+     */
+    fun getContentIfNotHandled(): T? {
+        return if (hasBeenHandled) {
+            null
+        } else {
+            hasBeenHandled = true
+            content
+        }
+    }
+
+    /**
+     * Returns the content, even if it's already been handled.
+     */
+    fun peekContent(): T = content
+
+    override fun toString(): String {
+        return "Event --> $content"
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other?.javaClass != javaClass) return false
+        val event = other as Event<*>
+        return content==event.content
+    }
+
+    override fun hashCode(): Int {
+        return content.hashCode()
+    }
+}