diff --git a/app/build.gradle b/app/build.gradle index fa8513c..bb2398c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -110,9 +110,12 @@ dependencies { implementation project(":imageloading") implementation project(":imageloading-glide") - implementation "com.arthurivanets.mvvm:mvvm-core:1.5.0" - implementation "com.arthurivanets.mvvm:mvvm-navigation:1.5.0" - implementation "com.arthurivanets.mvvm:mvvm-navigation-dagger:1.5.0" + implementation project(":mvvm") + implementation project(":mvvm-navigation") + implementation project(":mvvm-navigation-dagger") +// implementation "com.arthurivanets.mvvm:mvvm-core:1.5.0" +// implementation "com.arthurivanets.mvvm:mvvm-navigation:1.5.0" +// implementation "com.arthurivanets.mvvm:mvvm-navigation-dagger:1.5.0" testImplementation unitTestingDependencies androidTestImplementation instrumentationTestingDependencies diff --git a/app/release/app-release.apk b/app/release/app-release.apk deleted file mode 100644 index 2c15a58..0000000 Binary files a/app/release/app-release.apk and /dev/null differ diff --git a/app/src/main/java/com/arthurivanets/sample/ui/base/GeneralViewStates.kt b/app/src/main/java/com/arthurivanets/sample/ui/base/GeneralViewStates.kt index f6d207b..f94af00 100644 --- a/app/src/main/java/com/arthurivanets/sample/ui/base/GeneralViewStates.kt +++ b/app/src/main/java/com/arthurivanets/sample/ui/base/GeneralViewStates.kt @@ -16,9 +16,9 @@ package com.arthurivanets.sample.ui.base -import com.arthurivanets.mvvm.events.ViewState +import com.arthurivanets.mvvm.markers.ViewState -sealed class GeneralViewStates(payload : T? = null) : ViewState(payload) { +sealed class GeneralViewStates(val payload : T? = null) : ViewState { class Idle(payload : T? = null) : GeneralViewStates(payload) diff --git a/app/src/main/java/com/arthurivanets/sample/ui/base/MarvelRoutes.kt b/app/src/main/java/com/arthurivanets/sample/ui/base/MarvelRoutes.kt index fb207da..5bdc21b 100644 --- a/app/src/main/java/com/arthurivanets/sample/ui/base/MarvelRoutes.kt +++ b/app/src/main/java/com/arthurivanets/sample/ui/base/MarvelRoutes.kt @@ -16,17 +16,17 @@ package com.arthurivanets.sample.ui.base -import com.arthurivanets.mvvm.events.Route +import com.arthurivanets.mvvm.markers.Route import com.arthurivanets.sample.domain.entities.Character import com.arthurivanets.sample.domain.entities.Comics import com.arthurivanets.sample.domain.entities.Event -sealed class MarvelRoutes(payload : T? = null) : Route(payload) { +sealed class MarvelRoutes : Route { - class CharacterInfoScreen(character : Character) : MarvelRoutes(character) + class CharacterInfoScreen(val character : Character) : MarvelRoutes() - class ComicsInfoScreen(comics : Comics) : MarvelRoutes(comics) + class ComicsInfoScreen(val comics : Comics) : MarvelRoutes() - class EventInfoScreen(event : Event) : MarvelRoutes(event) + class EventInfoScreen(val event : Event) : MarvelRoutes() } \ No newline at end of file diff --git a/app/src/main/java/com/arthurivanets/sample/ui/characters/info/CharacterInfoFragment.kt b/app/src/main/java/com/arthurivanets/sample/ui/characters/info/CharacterInfoFragment.kt index 94980cc..c603206 100644 --- a/app/src/main/java/com/arthurivanets/sample/ui/characters/info/CharacterInfoFragment.kt +++ b/app/src/main/java/com/arthurivanets/sample/ui/characters/info/CharacterInfoFragment.kt @@ -28,8 +28,8 @@ import com.arthurivanets.adapster.ktx.isEmpty import com.arthurivanets.adapster.listeners.DatasetChangeListenerAdapter import com.arthurivanets.commons.ktx.getColorCompat import com.arthurivanets.commons.ktx.statusBarSize -import com.arthurivanets.mvvm.events.Route -import com.arthurivanets.mvvm.events.ViewState +import com.arthurivanets.mvvm.markers.Route +import com.arthurivanets.mvvm.markers.ViewState import com.arthurivanets.sample.R import com.arthurivanets.sample.adapters.comics.ComicsItem import com.arthurivanets.sample.adapters.comics.ComicsItemResources @@ -164,12 +164,12 @@ class CharacterInfoFragment : BaseMvvmFragment) { + override fun onViewStateChanged(state : ViewState) { when(state) { - is GeneralViewStates.Idle -> onIdleState() - is GeneralViewStates.Loading -> onLoadingState() - is GeneralViewStates.Success -> onSuccessState() - is GeneralViewStates.Error -> onErrorState() + is GeneralViewStates.Idle<*> -> onIdleState() + is GeneralViewStates.Loading<*> -> onLoadingState() + is GeneralViewStates.Success<*> -> onSuccessState() + is GeneralViewStates.Error<*> -> onErrorState() } } @@ -194,9 +194,9 @@ class CharacterInfoFragment : BaseMvvmFragment) { + override fun onRoute(route : Route) { when(route) { - is MarvelRoutes.ComicsInfoScreen -> route.payload?.let(::onOpenComicsInfoScreen) + is MarvelRoutes.ComicsInfoScreen -> onOpenComicsInfoScreen(route.comics) } } diff --git a/app/src/main/java/com/arthurivanets/sample/ui/characters/info/CharacterInfoViewModel.kt b/app/src/main/java/com/arthurivanets/sample/ui/characters/info/CharacterInfoViewModel.kt index 0d9b6a8..4c1e556 100644 --- a/app/src/main/java/com/arthurivanets/sample/ui/characters/info/CharacterInfoViewModel.kt +++ b/app/src/main/java/com/arthurivanets/sample/ui/characters/info/CharacterInfoViewModel.kt @@ -86,7 +86,7 @@ class CharacterInfoViewModel( isDataLoading = true - changeViewState(GeneralViewStates.Loading()) + viewState = GeneralViewStates.Loading() charactersRepository.getCharacterComics( character = character, @@ -103,7 +103,7 @@ class CharacterInfoViewModel( private fun onComicsLoadedSuccessfully(comics : List) { isDataLoading = false - changeViewState(GeneralViewStates.Success()) + viewState = GeneralViewStates.Success() comics.forEach { comicsItems.addOrUpdate(SmallComicsItem(it)) } } @@ -112,7 +112,7 @@ class CharacterInfoViewModel( private fun onComicsLoadingFailed(throwable : Throwable) { isDataLoading = false - changeViewState(GeneralViewStates.Error()) + viewState = GeneralViewStates.Error() // TODO the proper error handling should be done here throwable.printStackTrace() diff --git a/app/src/main/java/com/arthurivanets/sample/ui/characters/list/CharactersFragment.kt b/app/src/main/java/com/arthurivanets/sample/ui/characters/list/CharactersFragment.kt index 3316fc4..ad61527 100644 --- a/app/src/main/java/com/arthurivanets/sample/ui/characters/list/CharactersFragment.kt +++ b/app/src/main/java/com/arthurivanets/sample/ui/characters/list/CharactersFragment.kt @@ -21,8 +21,8 @@ import androidx.core.view.isVisible import androidx.navigation.fragment.FragmentNavigatorExtras import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView -import com.arthurivanets.mvvm.events.Route -import com.arthurivanets.mvvm.events.ViewState +import com.arthurivanets.mvvm.markers.Route +import com.arthurivanets.mvvm.markers.ViewState import com.arthurivanets.sample.R import com.arthurivanets.sample.adapters.characters.CharacterItem import com.arthurivanets.sample.adapters.characters.CharacterItemResources @@ -90,12 +90,12 @@ class CharactersFragment : BaseMvvmFragment) { + override fun onViewStateChanged(state : ViewState) { when(state) { - is GeneralViewStates.Idle -> onIdleState() - is GeneralViewStates.Loading -> onLoadingState() - is GeneralViewStates.Success -> onSuccessState() - is GeneralViewStates.Error -> onErrorState() + is GeneralViewStates.Idle<*> -> onIdleState() + is GeneralViewStates.Loading<*> -> onLoadingState() + is GeneralViewStates.Success<*> -> onSuccessState() + is GeneralViewStates.Error<*> -> onErrorState() } } @@ -120,9 +120,9 @@ class CharactersFragment : BaseMvvmFragment) { + override fun onRoute(route : Route) { when(route) { - is MarvelRoutes.CharacterInfoScreen -> route.payload?.let(::onOpenCharacterInfoScreen) + is MarvelRoutes.CharacterInfoScreen -> onOpenCharacterInfoScreen(route.character) } } diff --git a/app/src/main/java/com/arthurivanets/sample/ui/characters/list/CharactersViewModel.kt b/app/src/main/java/com/arthurivanets/sample/ui/characters/list/CharactersViewModel.kt index f605238..1a6d90e 100644 --- a/app/src/main/java/com/arthurivanets/sample/ui/characters/list/CharactersViewModel.kt +++ b/app/src/main/java/com/arthurivanets/sample/ui/characters/list/CharactersViewModel.kt @@ -65,7 +65,7 @@ class CharactersViewModel( isDataLoading = true - changeViewState(GeneralViewStates.Loading()) + viewState = GeneralViewStates.Loading() charactersRepository.getCharacters(0, DEFAULT_CHARACTER_LOADING_LIMIT) .resultOrError() @@ -78,7 +78,7 @@ class CharactersViewModel( private fun onLoadedSuccessfully(characters : List) { isDataLoading = false - changeViewState(GeneralViewStates.Success()) + viewState = GeneralViewStates.Success() characters.forEach { items.addOrUpdate(CharacterItem(it)) } } @@ -87,7 +87,7 @@ class CharactersViewModel( private fun onLoadingFailed(throwable : Throwable) { isDataLoading = false - changeViewState(GeneralViewStates.Error()) + viewState = GeneralViewStates.Error() // TODO the proper error handling should be done here throwable.printStackTrace() diff --git a/app/src/main/java/com/arthurivanets/sample/ui/comics/info/ComicsInfoFragment.kt b/app/src/main/java/com/arthurivanets/sample/ui/comics/info/ComicsInfoFragment.kt index 608d146..568bdaf 100644 --- a/app/src/main/java/com/arthurivanets/sample/ui/comics/info/ComicsInfoFragment.kt +++ b/app/src/main/java/com/arthurivanets/sample/ui/comics/info/ComicsInfoFragment.kt @@ -28,8 +28,8 @@ import com.arthurivanets.adapster.ktx.isEmpty import com.arthurivanets.adapster.listeners.DatasetChangeListenerAdapter import com.arthurivanets.commons.ktx.getColorCompat import com.arthurivanets.commons.ktx.statusBarSize -import com.arthurivanets.mvvm.events.Route -import com.arthurivanets.mvvm.events.ViewState +import com.arthurivanets.mvvm.markers.Route +import com.arthurivanets.mvvm.markers.ViewState import com.arthurivanets.sample.R import com.arthurivanets.sample.adapters.characters.CharacterItem import com.arthurivanets.sample.adapters.characters.CharacterItemResources @@ -164,12 +164,12 @@ class ComicsInfoFragment : BaseMvvmFragment) { + override fun onViewStateChanged(state : ViewState) { when(state) { - is GeneralViewStates.Idle -> onIdleState() - is GeneralViewStates.Loading -> onLoadingState() - is GeneralViewStates.Success -> onSuccessState() - is GeneralViewStates.Error -> onErrorState() + is GeneralViewStates.Idle<*> -> onIdleState() + is GeneralViewStates.Loading<*> -> onLoadingState() + is GeneralViewStates.Success<*> -> onSuccessState() + is GeneralViewStates.Error<*> -> onErrorState() } } @@ -194,9 +194,9 @@ class ComicsInfoFragment : BaseMvvmFragment) { + override fun onRoute(route : Route) { when(route) { - is MarvelRoutes.CharacterInfoScreen -> route.payload?.let(::onOpenCharacterInfoScreen) + is MarvelRoutes.CharacterInfoScreen -> onOpenCharacterInfoScreen(route.character) } } diff --git a/app/src/main/java/com/arthurivanets/sample/ui/comics/info/ComicsInfoViewModel.kt b/app/src/main/java/com/arthurivanets/sample/ui/comics/info/ComicsInfoViewModel.kt index 3c7e9fa..8c98b00 100644 --- a/app/src/main/java/com/arthurivanets/sample/ui/comics/info/ComicsInfoViewModel.kt +++ b/app/src/main/java/com/arthurivanets/sample/ui/comics/info/ComicsInfoViewModel.kt @@ -86,7 +86,7 @@ class ComicsInfoViewModel( isDataLoading = true - changeViewState(GeneralViewStates.Loading()) + viewState = GeneralViewStates.Loading() comicsRepository.getComicsCharacters( comics = comics, @@ -103,7 +103,7 @@ class ComicsInfoViewModel( private fun onCharactersLoadedSuccessfully(characters : List) { isDataLoading = false - changeViewState(GeneralViewStates.Success()) + viewState = GeneralViewStates.Success() characters.forEach { characterItems.addOrUpdate(SmallCharacterItem(it)) } } @@ -112,7 +112,7 @@ class ComicsInfoViewModel( private fun onCharacterLoadingFailed(throwable : Throwable) { isDataLoading = false - changeViewState(GeneralViewStates.Error()) + viewState = GeneralViewStates.Error() // TODO the proper error handling should be done here throwable.printStackTrace() diff --git a/app/src/main/java/com/arthurivanets/sample/ui/comics/list/ComicsFragment.kt b/app/src/main/java/com/arthurivanets/sample/ui/comics/list/ComicsFragment.kt index edf4c67..c1839bd 100644 --- a/app/src/main/java/com/arthurivanets/sample/ui/comics/list/ComicsFragment.kt +++ b/app/src/main/java/com/arthurivanets/sample/ui/comics/list/ComicsFragment.kt @@ -21,8 +21,8 @@ import androidx.core.view.isVisible import androidx.navigation.fragment.FragmentNavigatorExtras import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView -import com.arthurivanets.mvvm.events.Route -import com.arthurivanets.mvvm.events.ViewState +import com.arthurivanets.mvvm.markers.Route +import com.arthurivanets.mvvm.markers.ViewState import com.arthurivanets.sample.R import com.arthurivanets.sample.adapters.comics.ComicsItem import com.arthurivanets.sample.adapters.comics.ComicsItemResources @@ -90,12 +90,12 @@ class ComicsFragment : BaseMvvmFragment( } - override fun onViewStateChanged(state : ViewState<*>) { + override fun onViewStateChanged(state : ViewState) { when(state) { - is GeneralViewStates.Idle -> onIdleState() - is GeneralViewStates.Loading -> onLoadingState() - is GeneralViewStates.Success -> onSuccessState() - is GeneralViewStates.Error -> onErrorState() + is GeneralViewStates.Idle<*> -> onIdleState() + is GeneralViewStates.Loading<*> -> onLoadingState() + is GeneralViewStates.Success<*> -> onSuccessState() + is GeneralViewStates.Error<*> -> onErrorState() } } @@ -120,9 +120,9 @@ class ComicsFragment : BaseMvvmFragment( } - override fun onRoute(route : Route<*>) { + override fun onRoute(route : Route) { when(route) { - is MarvelRoutes.ComicsInfoScreen -> route.payload?.let(::onOpenComicsInfoScreen) + is MarvelRoutes.ComicsInfoScreen -> onOpenComicsInfoScreen(route.comics) } } diff --git a/app/src/main/java/com/arthurivanets/sample/ui/comics/list/ComicsViewModel.kt b/app/src/main/java/com/arthurivanets/sample/ui/comics/list/ComicsViewModel.kt index 37f64b0..6a0df11 100644 --- a/app/src/main/java/com/arthurivanets/sample/ui/comics/list/ComicsViewModel.kt +++ b/app/src/main/java/com/arthurivanets/sample/ui/comics/list/ComicsViewModel.kt @@ -65,7 +65,7 @@ class ComicsViewModel( isDataLoading = true - changeViewState(GeneralViewStates.Loading()) + viewState = GeneralViewStates.Loading() comicsRepository.getComics(0, DEFAULT_COMICS_LOADING_LIMIT) .resultOrError() @@ -78,7 +78,7 @@ class ComicsViewModel( private fun onLoadedSuccessfully(comics : List) { isDataLoading = false - changeViewState(GeneralViewStates.Success()) + viewState = GeneralViewStates.Success() comics.forEach { items.addOrUpdate(ComicsItem(it)) } } @@ -87,7 +87,7 @@ class ComicsViewModel( private fun onLoadingFailed(throwable : Throwable) { isDataLoading = false - changeViewState(GeneralViewStates.Error()) + viewState = GeneralViewStates.Error() // TODO the proper error handling should be done here throwable.printStackTrace() diff --git a/app/src/main/java/com/arthurivanets/sample/ui/events/info/EventInfoFragment.kt b/app/src/main/java/com/arthurivanets/sample/ui/events/info/EventInfoFragment.kt index 2068f96..1f8b56f 100644 --- a/app/src/main/java/com/arthurivanets/sample/ui/events/info/EventInfoFragment.kt +++ b/app/src/main/java/com/arthurivanets/sample/ui/events/info/EventInfoFragment.kt @@ -28,8 +28,8 @@ import com.arthurivanets.adapster.listeners.DatasetChangeListenerAdapter import com.arthurivanets.commons.ktx.getColorCompat import com.arthurivanets.commons.ktx.statusBarSize import com.arthurivanets.commons.ktx.updateLayoutParams -import com.arthurivanets.mvvm.events.Route -import com.arthurivanets.mvvm.events.ViewState +import com.arthurivanets.mvvm.markers.Route +import com.arthurivanets.mvvm.markers.ViewState import com.arthurivanets.sample.R import com.arthurivanets.sample.adapters.characters.CharacterItem import com.arthurivanets.sample.adapters.characters.CharacterItemResources @@ -210,12 +210,12 @@ class EventInfoFragment : BaseMvvmFragment) { + override fun onViewStateChanged(state : ViewState) { when(state) { - is GeneralViewStates.Idle -> onIdleState() - is GeneralViewStates.Loading -> onLoadingState() - is GeneralViewStates.Success -> onSuccessState() - is GeneralViewStates.Error -> onErrorState() + is GeneralViewStates.Idle<*> -> onIdleState() + is GeneralViewStates.Loading<*> -> onLoadingState() + is GeneralViewStates.Success<*> -> onSuccessState() + is GeneralViewStates.Error<*> -> onErrorState() } } @@ -240,10 +240,10 @@ class EventInfoFragment : BaseMvvmFragment) { + override fun onRoute(route : Route) { when(route) { - is MarvelRoutes.ComicsInfoScreen -> route.payload?.let(::onOpenComicsInfoScreen) - is MarvelRoutes.CharacterInfoScreen -> route.payload?.let(::onOpenCharacterInfoScreen) + is MarvelRoutes.ComicsInfoScreen -> onOpenComicsInfoScreen(route.comics) + is MarvelRoutes.CharacterInfoScreen -> onOpenCharacterInfoScreen(route.character) } } diff --git a/app/src/main/java/com/arthurivanets/sample/ui/events/info/EventInfoViewModel.kt b/app/src/main/java/com/arthurivanets/sample/ui/events/info/EventInfoViewModel.kt index f04fdd5..b904d82 100644 --- a/app/src/main/java/com/arthurivanets/sample/ui/events/info/EventInfoViewModel.kt +++ b/app/src/main/java/com/arthurivanets/sample/ui/events/info/EventInfoViewModel.kt @@ -98,7 +98,7 @@ class EventInfoViewModel( isDataLoading = true - changeViewState(GeneralViewStates.Loading()) + viewState = GeneralViewStates.Loading() Single.zip( eventsRepository.getEventComics( @@ -122,7 +122,7 @@ class EventInfoViewModel( private fun onDataLoadedSuccessfully(data : Pair, List>) { isDataLoading = false - changeViewState(GeneralViewStates.Success()) + viewState = GeneralViewStates.Success() data.first.forEach { comicsItems.addOrUpdate(SmallComicsItem(it)) } data.second.forEach { characterItems.addOrUpdate(SmallCharacterItem(it)) } @@ -132,7 +132,7 @@ class EventInfoViewModel( private fun onDataLoadingFailed(throwable : Throwable) { isDataLoading = false - changeViewState(GeneralViewStates.Error()) + viewState = GeneralViewStates.Error() // TODO the proper error handling should be done here throwable.printStackTrace() diff --git a/app/src/main/java/com/arthurivanets/sample/ui/events/list/EventsFragment.kt b/app/src/main/java/com/arthurivanets/sample/ui/events/list/EventsFragment.kt index 9bff8bd..de67846 100644 --- a/app/src/main/java/com/arthurivanets/sample/ui/events/list/EventsFragment.kt +++ b/app/src/main/java/com/arthurivanets/sample/ui/events/list/EventsFragment.kt @@ -21,8 +21,8 @@ import androidx.core.view.isVisible import androidx.navigation.fragment.FragmentNavigatorExtras import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import com.arthurivanets.mvvm.events.Route -import com.arthurivanets.mvvm.events.ViewState +import com.arthurivanets.mvvm.markers.Route +import com.arthurivanets.mvvm.markers.ViewState import com.arthurivanets.sample.R import com.arthurivanets.sample.adapters.events.EventItem import com.arthurivanets.sample.adapters.events.EventItemResources @@ -90,12 +90,12 @@ class EventsFragment : BaseMvvmFragment( } - override fun onViewStateChanged(state : ViewState<*>) { + override fun onViewStateChanged(state : ViewState) { when(state) { - is GeneralViewStates.Idle -> onIdleState() - is GeneralViewStates.Loading -> onLoadingState() - is GeneralViewStates.Success -> onSuccessState() - is GeneralViewStates.Error -> onErrorState() + is GeneralViewStates.Idle<*> -> onIdleState() + is GeneralViewStates.Loading<*> -> onLoadingState() + is GeneralViewStates.Success<*> -> onSuccessState() + is GeneralViewStates.Error<*> -> onErrorState() } } @@ -120,9 +120,9 @@ class EventsFragment : BaseMvvmFragment( } - override fun onRoute(route : Route<*>) { + override fun onRoute(route : Route) { when(route) { - is MarvelRoutes.EventInfoScreen -> route.payload?.let(::onOpenEventInfoScreen) + is MarvelRoutes.EventInfoScreen -> onOpenEventInfoScreen(route.event) } } diff --git a/app/src/main/java/com/arthurivanets/sample/ui/events/list/EventsViewModel.kt b/app/src/main/java/com/arthurivanets/sample/ui/events/list/EventsViewModel.kt index 3f0a1bb..da99c36 100644 --- a/app/src/main/java/com/arthurivanets/sample/ui/events/list/EventsViewModel.kt +++ b/app/src/main/java/com/arthurivanets/sample/ui/events/list/EventsViewModel.kt @@ -65,7 +65,7 @@ class EventsViewModel( isDataLoading = true - changeViewState(GeneralViewStates.Loading()) + viewState = GeneralViewStates.Loading() eventsRepository.getEvents(0, DEFAULT_EVENT_LOADING_LIMIT) .resultOrError() @@ -78,7 +78,7 @@ class EventsViewModel( private fun onLoadedSuccessfully(events : List) { isDataLoading = false - changeViewState(GeneralViewStates.Success()) + viewState = GeneralViewStates.Success() events.forEach { items.addOrUpdate(EventItem(it)) } } @@ -87,7 +87,7 @@ class EventsViewModel( private fun onLoadingFailed(throwable : Throwable) { isDataLoading = false - changeViewState(GeneralViewStates.Error()) + viewState = GeneralViewStates.Error() // TODO the proper error handling should be done here throwable.printStackTrace() diff --git a/build.gradle b/build.gradle index e9b3e16..33f0f9a 100644 --- a/build.gradle +++ b/build.gradle @@ -19,8 +19,8 @@ apply plugin: "com.github.ben-manes.versions" apply from: "common/constants.gradle" buildscript { - ext.kotlin_version = "1.3.61" - ext.nav_version = "2.2.1" + ext.kotlin_version = "1.3.72" + ext.nav_version = "2.3.0" repositories { google() @@ -28,12 +28,11 @@ buildscript { } dependencies { - classpath "com.android.tools.build:gradle:3.5.3" - classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4" + classpath "com.android.tools.build:gradle:4.0.1" + classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.5" classpath "com.github.dcendents:android-maven-gradle-plugin:2.1" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4" - classpath "com.github.ben-manes:gradle-versions-plugin:0.27.0" + classpath "com.github.ben-manes:gradle-versions-plugin:0.28.0" classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version" } } diff --git a/common/constants.gradle b/common/constants.gradle index 5e1ba93..65befb1 100644 --- a/common/constants.gradle +++ b/common/constants.gradle @@ -20,16 +20,16 @@ project.ext { appMinSdk = 21 libraryMinSdk = 18 - supportVersion = "1.1.0" - coreVersion = "1.1.0" + appCompatVersion = "1.1.0" + coreVersion = "1.3.0" coreTestsVersion = "1.0.0" multiDexVersion = "2.0.1" multiDexInstrumentationVersion = "2.0.0" recyclerViewVersion = "1.1.0" - fragmentVersion = "1.2.1" - navigationVersion = "2.2.1" + fragmentVersion = "1.2.5" + navigationVersion = "2.3.0" materialDesignVersion = "1.1.0" - recyclerViewVersion = "1.0.0" + recyclerViewVersion = "1.1.0" lifecycleVersion = "2.2.0" robolectricVersion = "4.0-alpha-3-SNAPSHOT" jUnitVersion = "4.13" @@ -37,25 +37,25 @@ project.ext { espressoVersion = "3.2.0" mockitoVersion = "3.2.4" constraintLayoutVersion = "1.1.3" - adapsterVersion = "1.0.12" + adapsterVersion = "1.0.13" glideVersion = "4.11.0" - daggerVersion = "2.26" - rxJavaVersion = "2.2.17" + daggerVersion = "2.28.1" + rxJavaVersion = "2.2.19" rxAndroidVersion = "2.1.1" rxBusVersion = "1.1.1" - jacksonVersion = "2.10.2" - retrofitVersion = "2.7.1" - okHttpVersion = "4.3.1" + jacksonVersion = "2.11.1" + retrofitVersion = "2.9.0" + okHttpVersion = "4.8.0" commonsVersion = "0.5.1" - roomVersion = "2.2.3" + roomVersion = "2.2.5" bottomSheetsVersion = "1.0.2" commonWidgetsVersion = "0.1.0" releaseRepoName = "maven" releaseUserOrg = "arthurlabs" releaseGroupId = "com.arthurivanets.mvvm" - releaseVersion = "1.6.0" - releaseVersionCode = 10 + releaseVersion = "1.7.0" + releaseVersionCode = 11 releaseWebsite = "https://github.com/arthur3486/android-mvvm" releaseLicense = ["Apache-2.0"] diff --git a/common/dependencies.gradle b/common/dependencies.gradle index 8777adc..0e54847 100644 --- a/common/dependencies.gradle +++ b/common/dependencies.gradle @@ -8,7 +8,8 @@ ext { // Android Core androidCoreDependencies = dependencies([ - appCompat: "androidx.appcompat:appcompat:${rootProject.supportVersion}", + appCompat: "androidx.appcompat:appcompat:${rootProject.appCompatVersion}", + core: "androidx.core:core:${rootProject.coreVersion}", coreKtx: "androidx.core:core-ktx:${rootProject.coreVersion}" ]) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 49b7370..1cc5a90 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sat Jul 27 01:14:49 EEST 2019 +#Fri Jul 17 01:37:45 EEST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip diff --git a/mvvm/src/main/java/com/arthurivanets/mvvm/AbstractViewModel.kt b/mvvm/src/main/java/com/arthurivanets/mvvm/AbstractViewModel.kt index 32bbed9..0f978cc 100644 --- a/mvvm/src/main/java/com/arthurivanets/mvvm/AbstractViewModel.kt +++ b/mvvm/src/main/java/com/arthurivanets/mvvm/AbstractViewModel.kt @@ -18,19 +18,18 @@ package com.arthurivanets.mvvm import android.os.Bundle import androidx.annotation.CallSuper +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel -import com.arthurivanets.mvvm.events.Command -import com.arthurivanets.mvvm.events.Route -import com.arthurivanets.mvvm.events.ViewState +import com.arthurivanets.mvvm.markers.Command +import com.arthurivanets.mvvm.markers.Route +import com.arthurivanets.mvvm.markers.ViewState import com.arthurivanets.mvvm.util.CompositeMapDisposable import com.arthurivanets.mvvm.util.adapt import com.arthurivanets.rxbus.BusEvent import com.arthurivanets.rxbus.EventSource -import com.arthurivanets.rxbus.RxBusFactory import io.reactivex.disposables.Disposable import io.reactivex.functions.Consumer -import io.reactivex.subjects.BehaviorSubject -import io.reactivex.subjects.PublishSubject /** * An abstract implementation of the [BaseViewModel]. @@ -40,13 +39,26 @@ import io.reactivex.subjects.PublishSubject * @param defaultViewState the default view state (will be delivered through the corresponding bus) */ abstract class AbstractViewModel( - defaultViewState : ViewState<*>? = null + defaultViewState : ViewState? = null ) : ViewModel(), BaseViewModel { - final override val commandBus = RxBusFactory.create>(PublishSubject.create()) - final override val viewStateBus = RxBusFactory.create>(defaultViewState?.let { BehaviorSubject.createDefault(it) } ?: BehaviorSubject.create()) - final override val routeBus = RxBusFactory.create>(PublishSubject.create()) + protected var viewState : ViewState? + set(value) { _viewStateHolder.value = value } + get() = _viewStateHolder.value + + final override val commandChannel : Channel + get() = _commandChannel + + final override val routeChannel : Channel + get() = _routeChannel + + final override val viewStateHolder : LiveData + get() = _viewStateHolder + + private val _commandChannel = BufferedChannel().asMainThreadChannel() + private val _routeChannel = BufferedChannel().asMainThreadChannel() + private val _viewStateHolder = (defaultViewState?.let { MutableLiveData(it) } ?: MutableLiveData()) private val shortLivingDisposables = CompositeMapDisposable() private val longLivingDisposables = CompositeMapDisposable() @@ -116,8 +128,8 @@ abstract class AbstractViewModel( *
* (The [Command] will be delivered if and only if the owning View has an active subscription to the current ViewModel) */ - protected fun dispatchCommand(command : Command<*>) { - commandBus.post(command) + protected fun dispatchCommand(command : Command) { + _commandChannel.post(command) } @@ -127,8 +139,8 @@ abstract class AbstractViewModel( *
* (The [ViewState] change event will be delivered if and only if the owning View has an active subscription to the current ViewModel) */ - protected fun changeViewState(newState : ViewState<*>) { - viewStateBus.post(newState) + protected fun postViewState(newState : ViewState) { + _viewStateHolder.postValue(newState) } @@ -137,8 +149,8 @@ abstract class AbstractViewModel( *
* (The [Route] event will be delivered if and only if the owning View has an active subscription to the current ViewModel) */ - protected fun route(destinationRoute : Route<*>) { - routeBus.post(destinationRoute) + protected fun route(destinationRoute : Route) { + _routeChannel.post(destinationRoute) } diff --git a/mvvm/src/main/java/com/arthurivanets/mvvm/BaseViewModel.kt b/mvvm/src/main/java/com/arthurivanets/mvvm/BaseViewModel.kt index 773cb95..2c145a5 100644 --- a/mvvm/src/main/java/com/arthurivanets/mvvm/BaseViewModel.kt +++ b/mvvm/src/main/java/com/arthurivanets/mvvm/BaseViewModel.kt @@ -16,13 +16,8 @@ package com.arthurivanets.mvvm -import com.arthurivanets.mvvm.events.Command -import com.arthurivanets.mvvm.events.Route -import com.arthurivanets.mvvm.events.ViewState -import com.arthurivanets.mvvm.markers.CanHandleBackPressEvents -import com.arthurivanets.mvvm.markers.CanManageState -import com.arthurivanets.mvvm.markers.ViewModelLifecycle -import com.arthurivanets.rxbus.RxBus +import androidx.lifecycle.LiveData +import com.arthurivanets.mvvm.markers.* /** * A base contract used to implement the concrete version of the View Model. @@ -30,21 +25,21 @@ import com.arthurivanets.rxbus.RxBus interface BaseViewModel : ViewModelLifecycle, CanManageState, CanHandleBackPressEvents { /** - * An [RxBus] that the ViewModel emits the [Command]s through. + * An [Channel] that the ViewModel emits the [Command]s through. * (The actual handling of the emitted [Command]s is completely up to the subscribing side (i.e. Activity, Fragment, View)) */ - val commandBus : RxBus> + val commandChannel : Channel /** - * An [RxBus] that the ViewModel notifies about [ViewState] changes through. - * (The actual handling of the [ViewState] changes is completely up to the subscribing side (i.e. Activity, Fragment, View)) + * An [Channel] that the ViewModel emits the [Route]s through. + * (The actual handling of the emitted [Route]s is completely up to the subscribing side (i.e. Activity, Fragment, View)) */ - val viewStateBus : RxBus> + val routeChannel : Channel /** - * An [RxBus] that the ViewModel emits the [Route]s through. - * (The actual handling of the emitted [Route]s is completely up to the subscribing side (i.e. Activity, Fragment, View)) + * An [LiveData] that the ViewModel notifies about [ViewState] changes through. + * (The actual handling of the [ViewState] changes is completely up to the subscribing side (i.e. Activity, Fragment, View)) */ - val routeBus : RxBus> + val viewStateHolder : LiveData } \ No newline at end of file diff --git a/mvvm/src/main/java/com/arthurivanets/mvvm/Channel.kt b/mvvm/src/main/java/com/arthurivanets/mvvm/Channel.kt new file mode 100644 index 0000000..9ee69f8 --- /dev/null +++ b/mvvm/src/main/java/com/arthurivanets/mvvm/Channel.kt @@ -0,0 +1,106 @@ +package com.arthurivanets.mvvm + +import android.os.Handler +import android.os.Looper +import java.util.* + + +typealias Consumer = (T) -> Unit + + +interface Channel { + + var consumer : Consumer? + +} + + +interface WritableChannel : Channel { + + fun post(payload : T) + +} + + +abstract class AbstractChannel internal constructor() : WritableChannel { + + + final override var consumer : Consumer? = null + set(value) { + field = value + + if (value != null) { + onConsumerAttached(value) + } else { + onConsumerDetached() + } + } + + protected val isConsumerAttached : Boolean + get() = (consumer != null) + + + protected open fun onConsumerAttached(consumer : Consumer) { + // to be overridden. + } + + + protected open fun onConsumerDetached() { + // to be overridden. + } + + +} + + +class BufferedChannel : AbstractChannel() { + + + private val bufferedEvents = ArrayDeque() + + + override fun onConsumerAttached(consumer : Consumer) { + while (bufferedEvents.isNotEmpty()) { + bufferedEvents.poll()?.let { consumer(it) } + } + } + + + override fun post(payload : T) { + if (isConsumerAttached) { + consumer?.invoke(payload) + } else { + bufferedEvents.add(payload) + } + } + + +} + + +private class MainThreadChannel(private val channel : WritableChannel) : WritableChannel { + + + private val handler = Handler(Looper.getMainLooper()) + + override var consumer : Consumer? + set(value) { channel.consumer = value } + get() = channel.consumer + + private var postAction : Runnable? = null + + + override fun post(payload : T) { + postAction?.let { handler.removeCallbacks(it) } + postAction = Runnable { channel.post(payload) } + + handler.post(postAction) + } + + +} + + +fun WritableChannel.asMainThreadChannel() : WritableChannel { + return MainThreadChannel(this) +} \ No newline at end of file diff --git a/mvvm/src/main/java/com/arthurivanets/mvvm/MvvmActivity.kt b/mvvm/src/main/java/com/arthurivanets/mvvm/MvvmActivity.kt index df50c27..92f0f0c 100644 --- a/mvvm/src/main/java/com/arthurivanets/mvvm/MvvmActivity.kt +++ b/mvvm/src/main/java/com/arthurivanets/mvvm/MvvmActivity.kt @@ -26,19 +26,12 @@ import androidx.databinding.DataBindingUtil import androidx.databinding.Observable import androidx.databinding.ObservableField import androidx.databinding.ViewDataBinding -import com.arthurivanets.mvvm.events.Command -import com.arthurivanets.mvvm.events.Route -import com.arthurivanets.mvvm.events.ViewState -import com.arthurivanets.mvvm.markers.CanFetchExtras -import com.arthurivanets.mvvm.markers.CanHandleNewIntent +import androidx.lifecycle.Observer +import com.arthurivanets.mvvm.markers.* import com.arthurivanets.mvvm.util.currentFragment import com.arthurivanets.mvvm.util.handleBackPressEvent import com.arthurivanets.mvvm.util.handleNewIntent import com.arthurivanets.mvvm.util.onPropertyChanged -import com.arthurivanets.rxbus.register -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.disposables.Disposable -import io.reactivex.functions.Consumer /** * A base MVVM Activity to be used in conjunction with the concrete implementations of the [BaseViewModel]. @@ -64,8 +57,6 @@ abstract class MvvmActivity( private var viewDataBinding : VDB? = null private var viewModel : VM? = null - private val viewStateSubscriptionDisposables = CompositeDisposable() - private val subscriptionDisposables = CompositeDisposable() private val registeredObservables = HashSet>() /** @@ -85,6 +76,7 @@ abstract class MvvmActivity( postInit() performDataBinding() subscribeViewStateObservers() + onRegisterObservables() onBind() } @@ -199,9 +191,8 @@ abstract class MvvmActivity( override fun onResume() { super.onResume() - subscribeEventConsumers() - onRegisterObservables() - + attachViewModelEventConsumers() + viewModel?.onStart() } @@ -220,8 +211,7 @@ abstract class MvvmActivity( viewModel?.onStop() - disposeSubscriptions() - unregisterFields() + detachViewModelEventConsumers() } @@ -287,7 +277,7 @@ abstract class MvvmActivity( final override fun onDestroy() { - disposeViewStateSubscriptions() + unregisterFields() onUnbind() onRecycle() super.onDestroy() @@ -318,7 +308,7 @@ abstract class MvvmActivity( * @param command the newly arrived [Command] */ @CallSuper - protected open fun onHandleCommand(command : Command<*>) { + protected open fun onHandleCommand(command : Command) { // to be overridden } @@ -331,7 +321,7 @@ abstract class MvvmActivity( * * @param state the new [ViewState] */ - protected open fun onViewStateChanged(state : ViewState<*>) { + protected open fun onViewStateChanged(state : ViewState) { // to be overridden } @@ -344,45 +334,29 @@ abstract class MvvmActivity( * * @param route the newly arrived [Route] */ - protected open fun onRoute(route : Route<*>) { + protected open fun onRoute(route : Route) { // to be overridden } private fun subscribeViewStateObservers() { - viewModel?.viewStateBus - ?.register(Consumer(::onViewStateChanged)) - ?.manageViewStateSubscription() - } - - - private fun disposeViewStateSubscriptions() { - viewStateSubscriptionDisposables.clear() - } - - - private fun subscribeEventConsumers() { - subscribeToViewModelCommandBus() - subscribeToViewModelRouteBus() + viewModel?.viewStateHolder?.observe(this, Observer(::onViewStateChanged)) } - private fun subscribeToViewModelCommandBus() { - viewModel?.commandBus - ?.register(Consumer(::onHandleCommand)) - ?.manageLifecycle() - } - - - private fun subscribeToViewModelRouteBus() { - viewModel?.routeBus - ?.register(Consumer(::onRoute)) - ?.manageLifecycle() + private fun attachViewModelEventConsumers() { + viewModel?.apply { + commandChannel.consumer = ::onHandleCommand + routeChannel.consumer = ::onRoute + } } - private fun disposeSubscriptions() { - subscriptionDisposables.clear() + private fun detachViewModelEventConsumers() { + viewModel?.apply { + commandChannel.consumer = null + routeChannel.consumer = null + } } @@ -392,16 +366,6 @@ abstract class MvvmActivity( } - private fun Disposable.manageViewStateSubscription() { - viewStateSubscriptionDisposables.add(this) - } - - - private fun Disposable.manageLifecycle() { - subscriptionDisposables.add(this) - } - - /** * Adds the specified [Observable.OnPropertyChangedCallback] to the registry of Lifecycle-aware Callbacks. *
diff --git a/mvvm/src/main/java/com/arthurivanets/mvvm/MvvmFragment.kt b/mvvm/src/main/java/com/arthurivanets/mvvm/MvvmFragment.kt index a17a449..e6d2acc 100644 --- a/mvvm/src/main/java/com/arthurivanets/mvvm/MvvmFragment.kt +++ b/mvvm/src/main/java/com/arthurivanets/mvvm/MvvmFragment.kt @@ -32,18 +32,10 @@ import androidx.databinding.Observable import androidx.databinding.ObservableField import androidx.databinding.ViewDataBinding import androidx.fragment.app.Fragment -import com.arthurivanets.mvvm.events.Command -import com.arthurivanets.mvvm.events.Route -import com.arthurivanets.mvvm.events.ViewState +import androidx.lifecycle.Observer import com.arthurivanets.mvvm.listeners.AnimationListenerAdapter -import com.arthurivanets.mvvm.markers.CanFetchExtras -import com.arthurivanets.mvvm.markers.CanHandleBackPressEvents -import com.arthurivanets.mvvm.markers.CanHandleNewIntent +import com.arthurivanets.mvvm.markers.* import com.arthurivanets.mvvm.util.* -import com.arthurivanets.rxbus.register -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.disposables.Disposable -import io.reactivex.functions.Consumer import kotlin.properties.Delegates /** @@ -74,8 +66,6 @@ abstract class MvvmFragment( private var viewDataBinding : VDB? = null private var viewModel : VM? = null - private val viewStateSubscriptionDisposables = CompositeDisposable() - private val subscriptionDisposables = CompositeDisposable() private val registeredObservables = HashSet>() /** @@ -158,6 +148,7 @@ abstract class MvvmFragment( performDataBinding() subscribeViewStateObservers() + onRegisterObservables() onBind() // performing the state restoring only in cases when the view was created for the first time @@ -374,8 +365,7 @@ abstract class MvvmFragment( override fun onResume() { super.onResume() - subscribeEventConsumers() - onRegisterObservables() + attachViewModelEventConsumers() viewModel?.onStart() } @@ -395,8 +385,7 @@ abstract class MvvmFragment( viewModel?.onStop() - disposeSubscriptions() - unregisterFields() + detachViewModelEventConsumers() } @@ -452,8 +441,8 @@ abstract class MvvmFragment( @CallSuper override fun onDestroyView() { super.onDestroyView() - - disposeViewStateSubscriptions() + + unregisterFields() onUnbind() } @@ -499,7 +488,7 @@ abstract class MvvmFragment( * @param command the newly arrived [Command] */ @CallSuper - protected open fun onHandleCommand(command : Command<*>) { + protected open fun onHandleCommand(command : Command) { // to be overridden } @@ -512,7 +501,7 @@ abstract class MvvmFragment( * * @param state the new [ViewState] */ - protected open fun onViewStateChanged(state : ViewState<*>) { + protected open fun onViewStateChanged(state : ViewState) { // to be overridden } @@ -525,7 +514,7 @@ abstract class MvvmFragment( * * @param route the newly arrived [Route] */ - protected open fun onRoute(route : Route<*>) { + protected open fun onRoute(route : Route) { // to be overridden } @@ -562,39 +551,23 @@ abstract class MvvmFragment( private fun subscribeViewStateObservers() { - viewModel?.viewStateBus - ?.register(Consumer(::onViewStateChanged)) - ?.manageViewStateSubscription() - } - - - private fun disposeViewStateSubscriptions() { - viewStateSubscriptionDisposables.clear() - } - - - private fun subscribeEventConsumers() { - subscribeToViewModelCommandBus() - subscribeToViewModelRouteBus() + viewModel?.viewStateHolder?.observe(viewLifecycleOwner, Observer(::onViewStateChanged)) } - private fun subscribeToViewModelCommandBus() { - viewModel?.commandBus - ?.register(Consumer(::onHandleCommand)) - ?.manageLifecycle() - } - - - private fun subscribeToViewModelRouteBus() { - viewModel?.routeBus - ?.register(Consumer(::onRoute)) - ?.manageLifecycle() + private fun attachViewModelEventConsumers() { + viewModel?.apply { + commandChannel.consumer = ::onHandleCommand + routeChannel.consumer = ::onRoute + } } - private fun disposeSubscriptions() { - subscriptionDisposables.clear() + private fun detachViewModelEventConsumers() { + viewModel?.apply { + commandChannel.consumer = null + routeChannel.consumer = null + } } @@ -603,16 +576,6 @@ abstract class MvvmFragment( registeredObservables.clear() } - - private fun Disposable.manageViewStateSubscription() { - viewStateSubscriptionDisposables.add(this) - } - - - private fun Disposable.manageLifecycle() { - subscriptionDisposables.add(this) - } - /** * Adds the specified [Observable.OnPropertyChangedCallback] to the registry of Lifecycle-aware Callbacks. diff --git a/mvvm/src/main/java/com/arthurivanets/mvvm/events/Command.kt b/mvvm/src/main/java/com/arthurivanets/mvvm/events/Command.kt deleted file mode 100644 index f9a48fd..0000000 --- a/mvvm/src/main/java/com/arthurivanets/mvvm/events/Command.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2018 Arthur Ivanets, arthur.ivanets.l@gmail.com - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.arthurivanets.mvvm.events - -import com.arthurivanets.rxbus.BusEvent - -/** - * A base class to be used for the concrete implementations of the custom Commands. - * - * (Common Commands might include such commands as FinishActivity, RestartApplication, PerformBackPress, etc.) - * - * @param T the type of the command payload - */ -abstract class Command(data : T? = null) : BusEvent(data) \ No newline at end of file diff --git a/mvvm/src/main/java/com/arthurivanets/mvvm/events/Route.kt b/mvvm/src/main/java/com/arthurivanets/mvvm/events/Route.kt deleted file mode 100644 index 1d8f01c..0000000 --- a/mvvm/src/main/java/com/arthurivanets/mvvm/events/Route.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2018 Arthur Ivanets, arthur.ivanets.l@gmail.com - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.arthurivanets.mvvm.events - -import com.arthurivanets.rxbus.BusEvent - -/** - * A base class to be used for the concrete implementations of the custom Routes. - * - * (You would generally want to create a Route for each of your app Screens (e.g. HomeScreen, ProfileScreen, etc.)) - * - * @param T the type of the route payload - */ -abstract class Route(payload : T? = null) : BusEvent(payload) \ No newline at end of file diff --git a/mvvm/src/main/java/com/arthurivanets/mvvm/events/ViewState.kt b/mvvm/src/main/java/com/arthurivanets/mvvm/events/ViewState.kt deleted file mode 100644 index 687f97d..0000000 --- a/mvvm/src/main/java/com/arthurivanets/mvvm/events/ViewState.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2018 Arthur Ivanets, arthur.ivanets.l@gmail.com - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.arthurivanets.mvvm.events - -import com.arthurivanets.rxbus.BusEvent - -/** - * A base class to be used for the concrete implementations of the custom View States. - * - * (Common View States include such states as Idle, Loading, Success, Error, etc.) - * - * @param T the type of the state payload - */ -abstract class ViewState(payload : T? = null) : BusEvent(payload) \ No newline at end of file diff --git a/mvvm/src/main/java/com/arthurivanets/mvvm/markers/Command.kt b/mvvm/src/main/java/com/arthurivanets/mvvm/markers/Command.kt new file mode 100644 index 0000000..8fab5e0 --- /dev/null +++ b/mvvm/src/main/java/com/arthurivanets/mvvm/markers/Command.kt @@ -0,0 +1,8 @@ +package com.arthurivanets.mvvm.markers + +/** + * A marker interface used to indicate that the implementor is a [com.arthurivanets.mvvm.BaseViewModel] command. + * + * (Common Commands might include such commands as FinishActivity, RestartApplication, PerformBackPress, etc.) + */ +interface Command \ No newline at end of file diff --git a/mvvm/src/main/java/com/arthurivanets/mvvm/markers/Route.kt b/mvvm/src/main/java/com/arthurivanets/mvvm/markers/Route.kt new file mode 100644 index 0000000..e9bd5d2 --- /dev/null +++ b/mvvm/src/main/java/com/arthurivanets/mvvm/markers/Route.kt @@ -0,0 +1,8 @@ +package com.arthurivanets.mvvm.markers + +/** + * A marker interface used to indicate that the implementor is a [com.arthurivanets.mvvm.BaseViewModel] route. + * + * (You would generally want to create a Route for each of your app Screens (e.g. HomeScreen, ProfileScreen, etc.)) + */ +interface Route \ No newline at end of file diff --git a/mvvm/src/main/java/com/arthurivanets/mvvm/markers/ViewState.kt b/mvvm/src/main/java/com/arthurivanets/mvvm/markers/ViewState.kt new file mode 100644 index 0000000..d15d7fa --- /dev/null +++ b/mvvm/src/main/java/com/arthurivanets/mvvm/markers/ViewState.kt @@ -0,0 +1,6 @@ +package com.arthurivanets.mvvm.markers + +/** + * A marker interface used to indicate that the implementor is a [com.arthurivanets.mvvm.BaseViewModel] ViewState. + */ +interface ViewState \ No newline at end of file diff --git a/mvvm/src/test/java/com/arthurivanets/mvvm/BufferedChannelTests.kt b/mvvm/src/test/java/com/arthurivanets/mvvm/BufferedChannelTests.kt new file mode 100644 index 0000000..872aa16 --- /dev/null +++ b/mvvm/src/test/java/com/arthurivanets/mvvm/BufferedChannelTests.kt @@ -0,0 +1,79 @@ +package com.arthurivanets.mvvm + +import junit.framework.TestCase.assertTrue +import org.junit.Test + +internal class BufferedChannelTests { + + + @Test + fun `verify that events are not getting buffered when the consumer is attached`() { + val consumer = AccumulatingConsumer() + val channel = BufferedChannel() + channel.consumer = consumer + + val events = listOf( + "A", + "B", + "C" + ) + + assertTrue(consumer.accumulatedEvents.isEmpty()) + + events.forEach { channel.post(it) } + + events.forEach { + assertTrue(it in consumer.accumulatedEvents) + } + } + + + @Test + fun `verify that events are getting buffered when the consumer is not attached and flushed once it gets attached`() { + val consumer1 = AccumulatingConsumer() + val consumer2 = AccumulatingConsumer() + val channel = BufferedChannel() + + val events = listOf( + "A", + "B", + "C" + ) + + // Initial State (consumed detached; events get emitted) + assertTrue(consumer1.accumulatedEvents.isEmpty()) + + events.forEach { channel.post(it) } + + assertTrue(consumer1.accumulatedEvents.isEmpty()) + + // Consumption State (consumer gets attached to the channel; buffered events get flushed) + channel.consumer = consumer1 + + events.forEach { + assertTrue(it in consumer1.accumulatedEvents) + } + + // Empty State (old consumer gets detached, the new one - attached; no events get emitted, as the channel's buffer is empty) + channel.consumer = null + channel.consumer = consumer2 + + assertTrue(consumer2.accumulatedEvents.isEmpty()) + } + + + private class AccumulatingConsumer : Consumer { + + val accumulatedEvents : Set + get() = _accumulatedEvents + + private val _accumulatedEvents = LinkedHashSet() + + override fun invoke(event : T) { + _accumulatedEvents += event + } + + } + + +} \ No newline at end of file diff --git a/mvvm/src/test/java/com/arthurivanets/mvvm/ExampleUnitTest.java b/mvvm/src/test/java/com/arthurivanets/mvvm/ExampleUnitTest.java deleted file mode 100644 index b3fab71..0000000 --- a/mvvm/src/test/java/com/arthurivanets/mvvm/ExampleUnitTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.arthurivanets.mvvm; - -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * Example local unit test, which will execute on the development machine (host). - * - * @see Testing documentation - */ -public class ExampleUnitTest { - @Test - public void addition_isCorrect() { - assertEquals(4, 2 + 2); - } -} \ No newline at end of file