diff --git a/app/src/main/kotlin/com/adesso/movee/base/BaseAndroidViewModel.kt b/app/src/main/kotlin/com/adesso/movee/base/BaseAndroidViewModel.kt index 0f22f94..cf7c2c2 100644 --- a/app/src/main/kotlin/com/adesso/movee/base/BaseAndroidViewModel.kt +++ b/app/src/main/kotlin/com/adesso/movee/base/BaseAndroidViewModel.kt @@ -14,10 +14,7 @@ import com.adesso.movee.internal.popup.PopupUiModel import com.adesso.movee.internal.util.Event import com.adesso.movee.internal.util.Failure import com.adesso.movee.navigation.NavigationCommand -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.withContext @Suppress("ConvertSecondaryConstructorToPrimary") @SuppressLint("StaticFieldLeak") @@ -34,9 +31,6 @@ abstract class BaseAndroidViewModel(application: Application) : AndroidViewModel private val viewModelJob = SupervisorJob() - protected val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob) - protected val bgScope = CoroutineScope(Dispatchers.Default + viewModelJob) - protected open fun handleFailure(failure: Failure) { val (title, message) = when (failure) { is Failure.NoConnectivityError -> Pair( @@ -100,18 +94,6 @@ abstract class BaseAndroidViewModel(application: Application) : AndroidViewModel _navigation.value = Event(NavigationCommand.Back) } - protected suspend fun onUIThread(block: suspend CoroutineScope.() -> Unit) { - withContext(uiScope.coroutineContext) { - block.invoke(this) - } - } - - protected suspend fun onBackgroundThread(block: suspend CoroutineScope.() -> T): T { - return withContext(bgScope.coroutineContext) { - block.invoke(this) - } - } - protected fun getString(@StringRes resId: Int): String { return getApplication().getString(resId) } diff --git a/app/src/main/kotlin/com/adesso/movee/internal/util/UseCase.kt b/app/src/main/kotlin/com/adesso/movee/internal/util/UseCase.kt index 525b5ba..f1e7dec 100644 --- a/app/src/main/kotlin/com/adesso/movee/internal/util/UseCase.kt +++ b/app/src/main/kotlin/com/adesso/movee/internal/util/UseCase.kt @@ -1,12 +1,15 @@ package com.adesso.movee.internal.util import com.adesso.movee.internal.util.functional.Either +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.async +import kotlinx.coroutines.launch abstract class UseCase where Type : Any { protected abstract suspend fun buildUseCase(params: Params): Type - suspend fun run(params: Params): Either { + private suspend fun run(params: Params): Either { return try { Either.Right(buildUseCase(params)) } catch (failure: Failure) { @@ -14,6 +17,15 @@ abstract class UseCase where Type : Any { } } + open operator fun invoke( + scope: CoroutineScope, + params: Params, + onResult: (Either) -> Unit = {} + ) { + val backgroundJob = scope.async { run(params) } + scope.launch { onResult(backgroundJob.await()) } + } + object None { override fun toString() = "UseCase.None" } diff --git a/app/src/main/kotlin/com/adesso/movee/scene/login/LoginViewModel.kt b/app/src/main/kotlin/com/adesso/movee/scene/login/LoginViewModel.kt index a5d0cfe..add3559 100644 --- a/app/src/main/kotlin/com/adesso/movee/scene/login/LoginViewModel.kt +++ b/app/src/main/kotlin/com/adesso/movee/scene/login/LoginViewModel.kt @@ -4,11 +4,11 @@ import android.app.Application import android.net.Uri import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.viewModelScope import com.adesso.movee.base.BaseAndroidViewModel import com.adesso.movee.domain.LoginUseCase import com.adesso.movee.internal.util.Event import javax.inject.Inject -import kotlinx.coroutines.launch class LoginViewModel @Inject constructor( private val loginUseCase: LoginUseCase, @@ -28,19 +28,13 @@ class LoginViewModel @Inject constructor( } fun onLoginClick() { - uiScope.launch { - val username = username.value ?: return@launch - val password = password.value ?: return@launch - - _loginInProgress.value = true - - val loginResult = onBackgroundThread { - loginUseCase.run(LoginUseCase.Params(username, password)) - } - - loginResult.either(::handleFailure, ::navigateHome) + _loginInProgress.value = true + val username = username.value ?: return + val password = password.value ?: return + loginUseCase.invoke(viewModelScope, LoginUseCase.Params(username, password)) { _loginInProgress.value = false + it.either(::handleFailure, ::navigateHome) } } diff --git a/app/src/main/kotlin/com/adesso/movee/scene/movie/MovieViewModel.kt b/app/src/main/kotlin/com/adesso/movee/scene/movie/MovieViewModel.kt index 6339de4..dc8791d 100644 --- a/app/src/main/kotlin/com/adesso/movee/scene/movie/MovieViewModel.kt +++ b/app/src/main/kotlin/com/adesso/movee/scene/movie/MovieViewModel.kt @@ -4,6 +4,7 @@ import android.app.Application import androidx.annotation.StringRes import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.viewModelScope import com.adesso.movee.R import com.adesso.movee.base.BaseAndroidViewModel import com.adesso.movee.domain.FetchNowPlayingMoviesUseCase @@ -18,7 +19,6 @@ import com.adesso.movee.uimodel.MovieUiModel import com.adesso.movee.uimodel.ShowHeaderUiModel import com.adesso.movee.uimodel.ShowUiModel import javax.inject.Inject -import kotlinx.coroutines.launch class MovieViewModel @Inject constructor( private val fetchPopularMoviesUseCase: FetchPopularMoviesUseCase, @@ -49,12 +49,8 @@ class MovieViewModel @Inject constructor( } private fun fetchNowPlayingMovies() { - bgScope.launch { - val nowPlayingMoviesResult = fetchNowPlayingMoviesUseCase.run(UseCase.None) - - onUIThread { - nowPlayingMoviesResult.either(::handleFailure, ::postNowPlayingMovieList) - } + fetchNowPlayingMoviesUseCase.invoke(viewModelScope, UseCase.None) { + it.either(::handleFailure, ::postNowPlayingMovieList) } } @@ -63,12 +59,8 @@ class MovieViewModel @Inject constructor( } private fun fetchPopularMovies() { - bgScope.launch { - val popularMoviesResult = fetchPopularMoviesUseCase.run(UseCase.None) - - onUIThread { - popularMoviesResult.either(::handleFailure, ::postPopularMovieList) - } + fetchPopularMoviesUseCase.invoke(viewModelScope, UseCase.None) { + it.either(::handleFailure, ::postPopularMovieList) } } diff --git a/app/src/main/kotlin/com/adesso/movee/scene/moviedetail/MovieDetailViewModel.kt b/app/src/main/kotlin/com/adesso/movee/scene/moviedetail/MovieDetailViewModel.kt index 099a2e5..83d9d0e 100644 --- a/app/src/main/kotlin/com/adesso/movee/scene/moviedetail/MovieDetailViewModel.kt +++ b/app/src/main/kotlin/com/adesso/movee/scene/moviedetail/MovieDetailViewModel.kt @@ -3,13 +3,13 @@ package com.adesso.movee.scene.moviedetail import android.app.Application import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.viewModelScope import com.adesso.movee.base.BaseAndroidViewModel import com.adesso.movee.domain.FetchMovieCreditsUseCase import com.adesso.movee.domain.FetchMovieDetailUseCase import com.adesso.movee.uimodel.MovieCreditUiModel import com.adesso.movee.uimodel.MovieDetailUiModel import javax.inject.Inject -import kotlinx.coroutines.launch class MovieDetailViewModel @Inject constructor( private val fetchMovieDetailUseCase: FetchMovieDetailUseCase, @@ -24,13 +24,8 @@ class MovieDetailViewModel @Inject constructor( fun fetchMovieDetails(id: Long) { if (_movieDetails.value == null) { - bgScope.launch { - val movieDetailResult = - fetchMovieDetailUseCase.run(FetchMovieDetailUseCase.Params(id)) - - onUIThread { - movieDetailResult.either(::handleFailure, ::postMovieDetail) - } + fetchMovieDetailUseCase.invoke(viewModelScope, FetchMovieDetailUseCase.Params(id)) { + it.either(::handleFailure, ::postMovieDetail) } } } @@ -41,13 +36,8 @@ class MovieDetailViewModel @Inject constructor( fun fetchMovieCredits(id: Long) { if (_movieCredits.value == null) { - bgScope.launch { - val movieCreditsResult = - fetchMovieCreditsUseCase.run(FetchMovieCreditsUseCase.Params(id)) - - onUIThread { - movieCreditsResult.either(::handleFailure, ::postMovieCredits) - } + fetchMovieCreditsUseCase.invoke(viewModelScope, FetchMovieCreditsUseCase.Params(id)) { + it.either(::handleFailure, ::postMovieCredits) } } } diff --git a/app/src/main/kotlin/com/adesso/movee/scene/persondetail/PersonDetailViewModel.kt b/app/src/main/kotlin/com/adesso/movee/scene/persondetail/PersonDetailViewModel.kt index 0c6b2ef..797a1f5 100644 --- a/app/src/main/kotlin/com/adesso/movee/scene/persondetail/PersonDetailViewModel.kt +++ b/app/src/main/kotlin/com/adesso/movee/scene/persondetail/PersonDetailViewModel.kt @@ -3,6 +3,7 @@ package com.adesso.movee.scene.persondetail import android.app.Application import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.viewModelScope import com.adesso.movee.base.BaseAndroidViewModel import com.adesso.movee.domain.FetchPersonDetailsUseCase import com.adesso.movee.internal.util.AppBarStateChangeListener @@ -11,7 +12,6 @@ import com.adesso.movee.internal.util.AppBarStateChangeListener.State.EXPANDED import com.adesso.movee.internal.util.AppBarStateChangeListener.State.IDLE import com.adesso.movee.uimodel.PersonDetailUiModel import javax.inject.Inject -import kotlinx.coroutines.launch class PersonDetailViewModel @Inject constructor( private val fetchPersonDetailsUseCase: FetchPersonDetailsUseCase, @@ -25,13 +25,11 @@ class PersonDetailViewModel @Inject constructor( fun fetchPersonDetails(personId: Long) { if (_personDetails.value == null) { - bgScope.launch { - val personDetailResult = - fetchPersonDetailsUseCase.run(FetchPersonDetailsUseCase.Params(personId)) - - onUIThread { - personDetailResult.either(::handleFailure, ::postPersonDetails) - } + fetchPersonDetailsUseCase.invoke( + viewModelScope, + FetchPersonDetailsUseCase.Params(personId) + ) { + it.either(::handleFailure, ::postPersonDetails) } } } diff --git a/app/src/main/kotlin/com/adesso/movee/scene/profile/ProfileViewModel.kt b/app/src/main/kotlin/com/adesso/movee/scene/profile/ProfileViewModel.kt index bc53346..af3aeef 100644 --- a/app/src/main/kotlin/com/adesso/movee/scene/profile/ProfileViewModel.kt +++ b/app/src/main/kotlin/com/adesso/movee/scene/profile/ProfileViewModel.kt @@ -4,6 +4,7 @@ import android.app.Application import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.Transformations +import androidx.lifecycle.viewModelScope import com.adesso.movee.base.BaseAndroidViewModel import com.adesso.movee.domain.FetchUserDetailsUseCase import com.adesso.movee.domain.GetLoginStateUseCase @@ -11,7 +12,6 @@ import com.adesso.movee.internal.util.UseCase import com.adesso.movee.uimodel.LoginState import com.adesso.movee.uimodel.UserDetailUiModel import javax.inject.Inject -import kotlinx.coroutines.launch class ProfileViewModel @Inject constructor( private val fetchUserDetailsUseCase: FetchUserDetailsUseCase, @@ -32,12 +32,8 @@ class ProfileViewModel @Inject constructor( } private fun getLoginState() { - bgScope.launch { - val loginStateResult = getLoginStateUseCase.run(UseCase.None) - - onUIThread { - loginStateResult.either(::handleFailure, ::handleLoginStateSuccess) - } + getLoginStateUseCase.invoke(viewModelScope, UseCase.None) { + it.either(::handleFailure, ::handleLoginStateSuccess) } } @@ -58,12 +54,8 @@ class ProfileViewModel @Inject constructor( } private fun fetchUserDetails() { - bgScope.launch { - val userDetailsResult = fetchUserDetailsUseCase.run(UseCase.None) - - onUIThread { - userDetailsResult.either(::handleFailure, ::postUserDetails) - } + fetchUserDetailsUseCase.invoke(viewModelScope, UseCase.None) { + it.either(::handleFailure, ::postUserDetails) } } diff --git a/app/src/main/kotlin/com/adesso/movee/scene/search/SearchViewModel.kt b/app/src/main/kotlin/com/adesso/movee/scene/search/SearchViewModel.kt index 7188dfd..0610538 100644 --- a/app/src/main/kotlin/com/adesso/movee/scene/search/SearchViewModel.kt +++ b/app/src/main/kotlin/com/adesso/movee/scene/search/SearchViewModel.kt @@ -3,6 +3,7 @@ package com.adesso.movee.scene.search import android.app.Application import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.viewModelScope import com.adesso.movee.base.BaseAndroidViewModel import com.adesso.movee.domain.MultiSearchUseCase import com.adesso.movee.internal.util.Failure @@ -13,7 +14,6 @@ import com.adesso.movee.uimodel.TvShowMultiSearchUiModel import javax.inject.Inject import kotlinx.coroutines.CancellationException import kotlinx.coroutines.Job -import kotlinx.coroutines.launch class SearchViewModel @Inject constructor( private val multiSearchUseCase: MultiSearchUseCase, @@ -31,11 +31,12 @@ class SearchViewModel @Inject constructor( val query = text?.trim() ?: return if (query.length > MIN_SEARCHABLE_LENGTH) { multiSearchJob?.cancel() - multiSearchJob = bgScope.launch { - val searchResult = multiSearchUseCase.run(MultiSearchUseCase.Params(query)) - onUIThread { - searchResult.either(::handleSearchFailure, ::postMultiSearchResult) - } + + multiSearchUseCase.invoke( + viewModelScope, + MultiSearchUseCase.Params(query) + ) { + it.either(::handleSearchFailure, ::postMultiSearchResult) } } } diff --git a/app/src/main/kotlin/com/adesso/movee/scene/tvshow/TvShowViewModel.kt b/app/src/main/kotlin/com/adesso/movee/scene/tvshow/TvShowViewModel.kt index e195984..2f15397 100644 --- a/app/src/main/kotlin/com/adesso/movee/scene/tvshow/TvShowViewModel.kt +++ b/app/src/main/kotlin/com/adesso/movee/scene/tvshow/TvShowViewModel.kt @@ -3,6 +3,7 @@ package com.adesso.movee.scene.tvshow import android.app.Application import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.viewModelScope import com.adesso.movee.R import com.adesso.movee.base.BaseAndroidViewModel import com.adesso.movee.domain.FetchNowPlayingTvShowsUseCase @@ -17,7 +18,6 @@ import com.adesso.movee.uimodel.ShowHeaderUiModel import com.adesso.movee.uimodel.ShowUiModel import com.adesso.movee.uimodel.TvShowUiModel import javax.inject.Inject -import kotlinx.coroutines.launch class TvShowViewModel @Inject constructor( private val fetchTopRatedTvShowsUseCase: FetchTopRatedTvShowsUseCase, @@ -48,12 +48,8 @@ class TvShowViewModel @Inject constructor( } private fun fetchTopRatedTvShows() { - bgScope.launch { - val topRatedTvShowsResult = fetchTopRatedTvShowsUseCase.run(UseCase.None) - - onUIThread { - topRatedTvShowsResult.either(::handleFailure, ::postTopRatedTvShows) - } + fetchTopRatedTvShowsUseCase.invoke(viewModelScope, UseCase.None) { + it.either(::handleFailure, ::postTopRatedTvShows) } } @@ -62,12 +58,8 @@ class TvShowViewModel @Inject constructor( } private fun fetchNowPlayingTvShows() { - bgScope.launch { - val nowPlayingTvShowsResult = fetchNowPlayingTvShowsUseCase.run(UseCase.None) - - onUIThread { - nowPlayingTvShowsResult.either(::handleFailure, ::postNowPlayingTvShows) - } + fetchNowPlayingTvShowsUseCase.invoke(viewModelScope, UseCase.None) { + it.either(::handleFailure, ::postNowPlayingTvShows) } } diff --git a/app/src/main/kotlin/com/adesso/movee/scene/tvshowdetail/TvShowDetailViewModel.kt b/app/src/main/kotlin/com/adesso/movee/scene/tvshowdetail/TvShowDetailViewModel.kt index a10b045..b1977bf 100644 --- a/app/src/main/kotlin/com/adesso/movee/scene/tvshowdetail/TvShowDetailViewModel.kt +++ b/app/src/main/kotlin/com/adesso/movee/scene/tvshowdetail/TvShowDetailViewModel.kt @@ -4,6 +4,7 @@ import android.app.Application import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.Transformations +import androidx.lifecycle.viewModelScope import com.adesso.movee.base.BaseAndroidViewModel import com.adesso.movee.domain.FetchTvShowCreditsUseCase import com.adesso.movee.domain.FetchTvShowDetailUseCase @@ -11,7 +12,6 @@ import com.adesso.movee.uimodel.TvShowCastUiModel import com.adesso.movee.uimodel.TvShowCreditUiModel import com.adesso.movee.uimodel.TvShowDetailUiModel import javax.inject.Inject -import kotlinx.coroutines.launch class TvShowDetailViewModel @Inject constructor( private val fetchTvShowDetailUseCase: FetchTvShowDetailUseCase, @@ -29,13 +29,8 @@ class TvShowDetailViewModel @Inject constructor( fun fetchTvShowDetail(id: Long) { if (_tvShowDetails.value == null) { - bgScope.launch { - val tvShowDetailResult = - fetchTvShowDetailUseCase.run(FetchTvShowDetailUseCase.Params(id)) - - onUIThread { - tvShowDetailResult.either(::handleFailure, ::postTvShowDetail) - } + fetchTvShowDetailUseCase.invoke(viewModelScope, FetchTvShowDetailUseCase.Params(id)) { + it.either(::handleFailure, ::postTvShowDetail) } } } @@ -46,13 +41,8 @@ class TvShowDetailViewModel @Inject constructor( fun fetchTvShowCredits(id: Long) { if (_tvShowCredits.value == null) { - bgScope.launch { - val tvShowCreditResult = - fetchTvShowCreditsUseCase.run(FetchTvShowCreditsUseCase.Params(id)) - - onUIThread { - tvShowCreditResult.either(::handleFailure, ::postTvShowCredits) - } + fetchTvShowCreditsUseCase.invoke(viewModelScope, FetchTvShowCreditsUseCase.Params(id)) { + it.either(::handleFailure, ::postTvShowCredits) } } }