Skip to content

Commit

Permalink
Refactor state success to handle data (#69)
Browse files Browse the repository at this point in the history
  • Loading branch information
odaridavid authored May 10, 2020
1 parent 714dd01 commit 4cc189a
Show file tree
Hide file tree
Showing 13 changed files with 50 additions and 55 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

# The-Force

An Android app consuming [a Star Wars API](https://swapi.py4e.com/documentation) to display Movie Characters
An Android app consuming [a Star Wars API](https://swapi.dev/) to display Movie Characters
it has been built with clean architecture principles, Repository Pattern and MVVM
pattern as well as Architecture Components.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.rule.ActivityTestRule
import com.k0d4black.theforce.features.character_details.CharacterDetailActivity
import com.k0d4black.theforce.models.StarWarsCharacterUiModel
import com.k0d4black.theforce.utils.CHARACTER_PARCEL_KEY
import com.k0d4black.theforce.commons.CHARACTER_PARCEL_KEY
import org.junit.After
import org.junit.Rule
import org.junit.Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
package com.k0d4black.theforce.utils
package com.k0d4black.theforce.commons

const val CHARACTER_PARCEL_KEY = "character_id"
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.k0d4black.theforce.utils
package com.k0d4black.theforce.commons

import java.math.BigDecimal
import java.math.RoundingMode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ package com.k0d4black.theforce.commons
sealed class UiState

object Loading : UiState()
object Success : UiState()
class Success<T>(val data: T) : UiState()
class Error(val error: Throwable) : UiState()
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.k0d4black.theforce.utils
package com.k0d4black.theforce.commons

import android.content.Context
import android.view.View
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,6 @@ open class StarWarsApiModule {

@Provides
@Named("baseUrl")
open fun provideBaseUrl(): String = "https://swapi.py4e.com/api/"
open fun provideBaseUrl(): String = "https://swapi.dev/api/"

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,9 @@ import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import com.k0d4black.theforce.R
import com.k0d4black.theforce.commons.AnimatorListener
import com.k0d4black.theforce.commons.Error
import com.k0d4black.theforce.commons.Loading
import com.k0d4black.theforce.commons.Success
import com.k0d4black.theforce.commons.*
import com.k0d4black.theforce.databinding.ActivityCharacterDetailBinding
import com.k0d4black.theforce.models.StarWarsCharacterUiModel
import com.k0d4black.theforce.utils.*
import dagger.android.AndroidInjection
import kotlinx.android.synthetic.main.activity_character_detail.*
import javax.inject.Inject
Expand Down Expand Up @@ -97,12 +93,18 @@ class CharacterDetailActivity : AppCompatActivity() {
characterDetailViewModel.uiState.observe(this, Observer {
val anchor = character_details_layout
when (it) {
is Success -> {
showSnackbar(anchor, getString(R.string.info_loading_complete))
is Success<*> -> {
showSnackbar(
anchor,
getString(R.string.info_loading_complete)
)
binding.loadingCharacterProgressBar.hide()
}
is Error -> displayErrorState(it.error)
is Loading -> showSnackbar(anchor, getString(R.string.info_loading_status))
is Loading -> showSnackbar(
anchor,
getString(R.string.info_loading_status)
)
}
})
}
Expand All @@ -111,7 +113,10 @@ class CharacterDetailActivity : AppCompatActivity() {
private fun displayErrorState(exception: Throwable) {
binding.loadingCharacterProgressBar.hide()
binding.loadingErrorTextView.show()
showSnackbar(character_details_layout, "${exception.message}")
showSnackbar(
character_details_layout,
"${exception.message}"
)
}

//Synthetics upcasting to View , Define type explicitly
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class CharacterDetailViewModel @Inject constructor(
getStarWarsCharacterSpeciesUseCase(characterUrl).collect {
_characterSpecies.value = it.map { species -> species.toPresentation() }
}
_uiState.value = Success
_uiState.value = Success(Unit)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
package com.k0d4black.theforce.features.character_search

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.k0d4black.theforce.commons.Loading
import com.k0d4black.theforce.commons.Success
import com.k0d4black.theforce.commons.UiStateViewModel
import com.k0d4black.theforce.domain.usecases.SearchStarWarsCharacterUseCase
import com.k0d4black.theforce.mappers.toPresentation
import com.k0d4black.theforce.models.StarWarsCharacterUiModel
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import javax.inject.Inject
Expand All @@ -17,20 +14,20 @@ class CharacterSearchViewModel @Inject constructor(
private val searchStarWarsCharacterUseCase: SearchStarWarsCharacterUseCase
) : UiStateViewModel() {

val searchResultsStarWars: LiveData<List<StarWarsCharacterUiModel>>
get() = _searchResultsStarWars

private var _searchResultsStarWars: MutableLiveData<List<StarWarsCharacterUiModel>> =
MutableLiveData()
// val searchResultsStarWars: LiveData<List<StarWarsCharacterUiModel>>
// get() = _searchResultsStarWars
//
// private var _searchResultsStarWars: MutableLiveData<List<StarWarsCharacterUiModel>> =
// MutableLiveData()

fun executeCharacterSearch(params: String) {
_uiState.value = Loading
viewModelScope.launch(handler) {
searchStarWarsCharacterUseCase(params).collect { results ->
_searchResultsStarWars.value = results.map { it.toPresentation() }
_uiState.value = Success(results.map { it.toPresentation() })
}
}
_uiState.value = Success

}
}

Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,10 @@ import androidx.core.view.isVisible
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import com.k0d4black.theforce.R
import com.k0d4black.theforce.commons.AnimatorListener
import com.k0d4black.theforce.commons.Error
import com.k0d4black.theforce.commons.Loading
import com.k0d4black.theforce.commons.Success
import com.k0d4black.theforce.commons.*
import com.k0d4black.theforce.features.character_details.CharacterDetailActivity
import com.k0d4black.theforce.features.settings.SettingsActivity
import com.k0d4black.theforce.models.StarWarsCharacterUiModel
import com.k0d4black.theforce.utils.*
import dagger.android.AndroidInjection
import jp.wasabeef.recyclerview.adapters.ScaleInAnimationAdapter
import kotlinx.android.synthetic.main.activity_search.*
Expand Down Expand Up @@ -46,28 +42,23 @@ class SearchActivity : AppCompatActivity() {
setContentView(R.layout.activity_search)

observeUiState()
observeSearchResults()
}

@Suppress("UNCHECKED_CAST")
private fun observeUiState() {
characterSearchViewModel.uiState.observe(this, Observer {
when (it) {
is Success -> showSnackbar(
search_results_recycler_view,
getString(R.string.info_search_done)
)
is Success<*> -> {
val data = it.data as List<StarWarsCharacterUiModel>
showSnackbar(search_results_recycler_view, getString(R.string.info_search_done))
displaySearchResults(data)
}
is Error -> displayErrorState(it.error)
is Loading -> displayLoadingState()
}
})
}

private fun observeSearchResults() {
characterSearchViewModel.searchResultsStarWars.observe(this, Observer {
displaySearchResults(it)
})
}

private fun displayLoadingState() {
search_tip_text_view.animate()
.alpha(0f)
Expand All @@ -78,20 +69,23 @@ class SearchActivity : AppCompatActivity() {
}

private fun displaySearchResults(searchResultStarWars: List<StarWarsCharacterUiModel>) {
loading_search_results_progress_bar.animate().alpha(0f)
loading_search_results_progress_bar.animate()
.alpha(0f)
.setListener(AnimatorListener(onEnd = { loading_search_results_progress_bar.hide() }))

if (searchResultStarWars.isNotEmpty()) {

if (search_tip_text_view.isVisible) search_tip_text_view.hide()

search_results_recycler_view.apply {
adapter =
ScaleInAnimationAdapter(searchResultAdapter.apply {
submitList(
searchResultStarWars
)
})
adapter = ScaleInAnimationAdapter(searchResultAdapter.apply {
submitList(searchResultStarWars)
})
initRecyclerViewWithLineDecoration(this@SearchActivity)
}

search_results_recycler_view.show()

} else displayNoSearchResults()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import com.k0d4black.theforce.models.StarWarsCharacterFilmsUiModel
import com.k0d4black.theforce.models.StarWarsCharacterPlanetUiModel
import com.k0d4black.theforce.models.StarWarsCharacterSpeciesUiModel
import com.k0d4black.theforce.models.StarWarsCharacterUiModel
import com.k0d4black.theforce.utils.convertToInches
import com.k0d4black.theforce.commons.convertToInches


fun StarWarsCharacter.toPresentation(): StarWarsCharacterUiModel {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ package com.k0d4black.theforce.viewmodels

import com.google.common.truth.Truth
import com.k0d4black.theforce.BaseViewModelTest
import com.k0d4black.theforce.commons.Success
import com.k0d4black.theforce.domain.usecases.SearchStarWarsCharacterUseCase
import com.k0d4black.theforce.features.character_search.CharacterSearchViewModel
import com.k0d4black.theforce.mappers.toPresentation
import com.k0d4black.theforce.utils.SampleData
import com.k0d4black.theforce.utils.observeOnce
import kotlinx.coroutines.ExperimentalCoroutinesApi
Expand Down Expand Up @@ -38,9 +38,8 @@ internal class StarWarsCharacterSearchViewModelTest : BaseViewModelTest() {
runBlockingTest {
setMockAnswer()
characterSearchViewModel.executeCharacterSearch(searchParams)
characterSearchViewModel.searchResultsStarWars.observeOnce {
Truth.assertThat(it)
.isEqualTo(SampleData.searchResults.map { character -> character.toPresentation() })
characterSearchViewModel.uiState.observeOnce { state ->
Truth.assertThat(state).isInstanceOf(Success::class.java)
}
}
}
Expand Down

0 comments on commit 4cc189a

Please sign in to comment.