Skip to content

Commit

Permalink
change liveData to stateFlow
Browse files Browse the repository at this point in the history
  • Loading branch information
quanda-0562 committed May 11, 2023
1 parent b43944f commit 884c726
Show file tree
Hide file tree
Showing 16 changed files with 122 additions and 136 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ fun HomeScreen(
navController: NavController,
viewModel: PopularMovieViewModel = hiltViewModel()
) {
val movieList by viewModel.itemList.observeAsState(listOf())
val refreshing by viewModel.isRefreshing.observeAsState(false)
val movieList by viewModel.itemList.collectAsState(listOf())
val refreshing by viewModel.isRefreshing.collectAsState(false)
val pullRefreshState = rememberPullRefreshState(refreshing, { viewModel.doRefresh() })
val scrollState = rememberLazyGridState()
val endOfListReached by remember {
Expand Down
21 changes: 12 additions & 9 deletions app/src/main/java/com/example/moviedb/ui/base/BaseActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ import androidx.annotation.LayoutRes
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding
import androidx.lifecycle.lifecycleScope
import com.example.moviedb.BR
import com.example.moviedb.R
import com.example.moviedb.utils.dismissLLoadingDialog
import com.example.moviedb.utils.showDialog
import com.example.moviedb.utils.showLoadingDialog
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch

abstract class BaseActivity<ViewBinding : ViewDataBinding, ViewModel : BaseViewModel> :
AppCompatActivity() {
Expand All @@ -33,26 +36,26 @@ abstract class BaseActivity<ViewBinding : ViewDataBinding, ViewModel : BaseViewM
}

private fun observeErrorEvent() {
viewModel.apply {
isLoading.observe(this@BaseActivity) {
handleLoading(it == true)
lifecycleScope.launch {
viewModel.isLoading.collectLatest {
handleLoading(it)
}
errorMessage.observe(this@BaseActivity) {
viewModel.errorMessage.collectLatest {
handleErrorMessage(it)
}
noInternetConnectionEvent.observe(this@BaseActivity) {
viewModel.noInternetConnectionEvent.collectLatest {
handleErrorMessage(getString(R.string.no_internet_connection))
}
connectTimeoutEvent.observe(this@BaseActivity) {
viewModel.connectTimeoutEvent.collectLatest {
handleErrorMessage(getString(R.string.connect_timeout))
}
forceUpdateAppEvent.observe(this@BaseActivity) {
viewModel.forceUpdateAppEvent.collectLatest {
handleErrorMessage(getString(R.string.force_update_app))
}
serverMaintainEvent.observe(this@BaseActivity) {
viewModel.serverMaintainEvent.collectLatest {
handleErrorMessage(getString(R.string.server_maintain_message))
}
unknownErrorEvent.observe(this@BaseActivity) {
viewModel.unknownErrorEvent.collectLatest {
handleErrorMessage(getString(R.string.unknown_error))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@ import androidx.databinding.ViewDataBinding
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentTransaction
import androidx.lifecycle.lifecycleScope
import com.example.moviedb.BR
import com.example.moviedb.R
import com.example.moviedb.utils.dismissLLoadingDialog
import com.example.moviedb.utils.showDialog
import com.example.moviedb.utils.showLoadingDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch

abstract class BaseBottomSheetDialogFragment<ViewBinding : ViewDataBinding, ViewModel : BaseViewModel> :
BottomSheetDialogFragment() {
Expand Down Expand Up @@ -47,26 +50,26 @@ abstract class BaseBottomSheetDialogFragment<ViewBinding : ViewDataBinding, View
}

private fun observerEvents() {
viewModel.apply {
isLoading.observe(viewLifecycleOwner) {
handleLoading(it == true)
lifecycleScope.launch {
viewModel.isLoading.collectLatest {
handleLoading(it)
}
errorMessage.observe(viewLifecycleOwner) {
viewModel.errorMessage.collectLatest {
handleErrorMessage(it)
}
noInternetConnectionEvent.observe(viewLifecycleOwner) {
viewModel.noInternetConnectionEvent.collectLatest {
handleErrorMessage(getString(R.string.no_internet_connection))
}
connectTimeoutEvent.observe(viewLifecycleOwner) {
viewModel.connectTimeoutEvent.collectLatest {
handleErrorMessage(getString(R.string.connect_timeout))
}
forceUpdateAppEvent.observe(viewLifecycleOwner) {
viewModel.forceUpdateAppEvent.collectLatest {
handleErrorMessage(getString(R.string.force_update_app))
}
serverMaintainEvent.observe(viewLifecycleOwner) {
viewModel.serverMaintainEvent.collectLatest {
handleErrorMessage(getString(R.string.server_maintain_message))
}
unknownErrorEvent.observe(viewLifecycleOwner) {
viewModel.unknownErrorEvent.collectLatest {
handleErrorMessage(getString(R.string.unknown_error))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ import androidx.databinding.ViewDataBinding
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentTransaction
import androidx.lifecycle.lifecycleScope
import com.example.moviedb.BR
import com.example.moviedb.R
import com.example.moviedb.utils.dismissLLoadingDialog
import com.example.moviedb.utils.showDialog
import com.example.moviedb.utils.showLoadingDialog
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch

abstract class BaseDialogFragment<ViewBinding : ViewDataBinding, ViewModel : BaseViewModel> :
DialogFragment() {
Expand Down Expand Up @@ -46,26 +49,26 @@ abstract class BaseDialogFragment<ViewBinding : ViewDataBinding, ViewModel : Bas
}

private fun observerEvents() {
viewModel.apply {
isLoading.observe(viewLifecycleOwner) {
handleLoading(it == true)
lifecycleScope.launch {
viewModel.isLoading.collectLatest {
handleLoading(it)
}
errorMessage.observe(viewLifecycleOwner) {
viewModel.errorMessage.collectLatest {
handleErrorMessage(it)
}
noInternetConnectionEvent.observe(viewLifecycleOwner) {
viewModel.noInternetConnectionEvent.collectLatest {
handleErrorMessage(getString(R.string.no_internet_connection))
}
connectTimeoutEvent.observe(viewLifecycleOwner) {
viewModel.connectTimeoutEvent.collectLatest {
handleErrorMessage(getString(R.string.connect_timeout))
}
forceUpdateAppEvent.observe(viewLifecycleOwner) {
viewModel.forceUpdateAppEvent.collectLatest {
handleErrorMessage(getString(R.string.force_update_app))
}
serverMaintainEvent.observe(viewLifecycleOwner) {
viewModel.serverMaintainEvent.collectLatest {
handleErrorMessage(getString(R.string.server_maintain_message))
}
unknownErrorEvent.observe(viewLifecycleOwner) {
viewModel.unknownErrorEvent.collectLatest {
handleErrorMessage(getString(R.string.unknown_error))
}
}
Expand Down
21 changes: 12 additions & 9 deletions app/src/main/java/com/example/moviedb/ui/base/BaseFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@ import androidx.databinding.ViewDataBinding
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentTransaction
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavController
import androidx.navigation.fragment.NavHostFragment
import com.example.moviedb.BR
import com.example.moviedb.R
import com.example.moviedb.utils.dismissLLoadingDialog
import com.example.moviedb.utils.showDialog
import com.example.moviedb.utils.showLoadingDialog
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch

abstract class BaseFragment<ViewBinding : ViewDataBinding, ViewModel : BaseViewModel> : Fragment() {

Expand Down Expand Up @@ -47,26 +50,26 @@ abstract class BaseFragment<ViewBinding : ViewDataBinding, ViewModel : BaseViewM
}

private fun observerEvents() {
viewModel.apply {
isLoading.observe(viewLifecycleOwner) {
handleLoading(it == true)
lifecycleScope.launch {
viewModel.isLoading.collectLatest {
handleLoading(it)
}
errorMessage.observe(viewLifecycleOwner) {
viewModel.errorMessage.collectLatest {
handleErrorMessage(it)
}
noInternetConnectionEvent.observe(viewLifecycleOwner) {
viewModel.noInternetConnectionEvent.collectLatest {
handleErrorMessage(getString(R.string.no_internet_connection))
}
connectTimeoutEvent.observe(viewLifecycleOwner) {
viewModel.connectTimeoutEvent.collectLatest {
handleErrorMessage(getString(R.string.connect_timeout))
}
forceUpdateAppEvent.observe(viewLifecycleOwner) {
viewModel.forceUpdateAppEvent.collectLatest {
handleErrorMessage(getString(R.string.force_update_app))
}
serverMaintainEvent.observe(viewLifecycleOwner) {
viewModel.serverMaintainEvent.collectLatest {
handleErrorMessage(getString(R.string.server_maintain_message))
}
unknownErrorEvent.observe(viewLifecycleOwner) {
viewModel.unknownErrorEvent.collectLatest {
handleErrorMessage(getString(R.string.unknown_error))
}
}
Expand Down
39 changes: 22 additions & 17 deletions app/src/main/java/com/example/moviedb/ui/base/BaseViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ package com.example.moviedb.ui.base
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.moviedb.data.remote.toBaseException
import com.example.moviedb.utils.SingleLiveData
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.plus
import java.net.ConnectException
Expand All @@ -15,17 +16,17 @@ import java.net.UnknownHostException
open class BaseViewModel : ViewModel() {

// loading flag
val isLoading by lazy { SingleLiveData<Boolean>().apply { value = false } }
val isLoading by lazy { MutableStateFlow(false) }

// error message
val errorMessage by lazy { SingleLiveData<String>() }
val errorMessage by lazy { MutableSharedFlow<String>() }

// optional flags
val noInternetConnectionEvent by lazy { SingleLiveData<Unit>() }
val connectTimeoutEvent by lazy { SingleLiveData<Unit>() }
val forceUpdateAppEvent by lazy { SingleLiveData<Unit>() }
val serverMaintainEvent by lazy { SingleLiveData<Unit>() }
val unknownErrorEvent by lazy { SingleLiveData<Unit>() }
val noInternetConnectionEvent by lazy { MutableSharedFlow<Unit>() }
val connectTimeoutEvent by lazy { MutableSharedFlow<Unit>() }
val forceUpdateAppEvent by lazy { MutableSharedFlow<Unit>() }
val serverMaintainEvent by lazy { MutableSharedFlow<Unit>() }
val unknownErrorEvent by lazy { MutableSharedFlow<Unit>() }

// exception handler for coroutine
private val exceptionHandler by lazy {
Expand All @@ -40,40 +41,44 @@ open class BaseViewModel : ViewModel() {
/**
* handle throwable when load fail
*/
protected open fun onError(throwable: Throwable) {
protected open suspend fun onError(throwable: Throwable) {
when (throwable) {
// case no internet connection
is UnknownHostException -> {
noInternetConnectionEvent.call()
noInternetConnectionEvent.emit(Unit)
}

is ConnectException -> {
noInternetConnectionEvent.call()
noInternetConnectionEvent.emit(Unit)
}
// case request time out
is SocketTimeoutException -> {
connectTimeoutEvent.call()
connectTimeoutEvent.emit(Unit)
}

else -> {
// convert throwable to base exception to get error information
val baseException = throwable.toBaseException()
when (baseException.httpCode) {
HttpURLConnection.HTTP_UNAUTHORIZED -> {
errorMessage.value = baseException.message
errorMessage.emit(baseException.message ?: "")
}

HttpURLConnection.HTTP_INTERNAL_ERROR -> {
errorMessage.value = baseException.message
errorMessage.emit(baseException.message ?: "")
}

else -> {
unknownErrorEvent.call()
unknownErrorEvent.emit(Unit)
}
}
}
}
hideLoading()
}

open fun showError(e: Throwable) {
errorMessage.value = e.message
open suspend fun showError(e: Throwable) {
errorMessage.emit(e.message ?: "")
}

fun showLoading() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ package com.example.moviedb.ui.base.loadmorerefresh
import android.os.Bundle
import android.view.View
import androidx.databinding.ViewDataBinding
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.example.moviedb.R
import com.example.moviedb.ui.base.BaseFragment
import com.example.moviedb.ui.base.BaseListAdapter
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch

/**
* should use paging 3
Expand Down Expand Up @@ -39,11 +42,11 @@ abstract class BaseLoadMoreRefreshFragment<ViewBinding : ViewDataBinding, ViewMo
recyclerView?.layoutManager = getLayoutManager()
recyclerView?.adapter = listAdapter
recyclerView?.setHasFixedSize(true)
viewModel.apply {
itemList.observe(viewLifecycleOwner) {
lifecycleScope.launch {
viewModel.itemList.collectLatest {
listAdapter.submitList(it)
}
firstLoad()
viewModel.firstLoad()
}
}

Expand Down
Loading

0 comments on commit 884c726

Please sign in to comment.