diff --git a/app/build.gradle b/app/build.gradle index 4e4da7e..3ec5990 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -91,8 +91,9 @@ dependencies { implementation deps.mvp.mosby_view_state implementation deps.mvp.mosby_queuing - // Reactivex - implementation deps.rx.binding + // Coroutines + implementation deps.coroutines.core + implementation deps.coroutines.android // Permissions implementation deps.permissions.no @@ -139,6 +140,7 @@ dependencies { androidTestImplementation deps.test.dexmaker_mockito androidTestImplementation deps.test.androidx_runner androidTestImplementation deps.test.androidx_rules + } // Final plugins diff --git a/app/src/main/java/pl/valueadd/restcountries/presentation/base/BasePresenter.kt b/app/src/main/java/pl/valueadd/restcountries/presentation/base/BasePresenter.kt index 9afade5..e50bc41 100644 --- a/app/src/main/java/pl/valueadd/restcountries/presentation/base/BasePresenter.kt +++ b/app/src/main/java/pl/valueadd/restcountries/presentation/base/BasePresenter.kt @@ -1,27 +1,20 @@ package pl.valueadd.restcountries.presentation.base import com.hannesdorfmann.mosby3.mvp.MvpQueuingBasePresenter -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.disposables.Disposable +import kotlinx.coroutines.CoroutineExceptionHandler +import kotlinx.coroutines.MainScope +import pl.valueadd.restcountries.utility.coroutines.cancelSafe abstract class BasePresenter : MvpQueuingBasePresenter() { - /** - * Contains disposable subscription of streams. - */ - protected val disposables: CompositeDisposable - by lazy { CompositeDisposable() } + protected val scope by lazy { MainScope() } - /** - * Add subscription to composite. - */ - protected fun addDisposable(disposable: Disposable): Boolean = - disposables.add(disposable) + fun exceptionHandler(method: (Throwable) -> Unit): CoroutineExceptionHandler { + return CoroutineExceptionHandler { _, throwable -> method(throwable) } + } - /** - * Clear all subscriptions. - */ - fun clearDisposables() { - disposables.clear() + override fun detachView() { + scope.cancelSafe() + super.detachView() } } \ No newline at end of file diff --git a/app/src/main/java/pl/valueadd/restcountries/presentation/base/activity/BaseMVPActivity.kt b/app/src/main/java/pl/valueadd/restcountries/presentation/base/activity/BaseMVPActivity.kt index 07a5215..bf49272 100644 --- a/app/src/main/java/pl/valueadd/restcountries/presentation/base/activity/BaseMVPActivity.kt +++ b/app/src/main/java/pl/valueadd/restcountries/presentation/base/activity/BaseMVPActivity.kt @@ -10,8 +10,8 @@ import androidx.annotation.IdRes import androidx.annotation.LayoutRes import androidx.core.content.ContextCompat import com.hannesdorfmann.mosby3.mvp.MvpActivity -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.disposables.Disposable +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.MainScope import me.yokeyword.fragmentation.ExtraTransaction import me.yokeyword.fragmentation.ISupportFragment import me.yokeyword.fragmentation.SupportActivityDelegate @@ -21,6 +21,7 @@ import pl.valueadd.restcountries.R import pl.valueadd.restcountries.presentation.base.BasePresenter import pl.valueadd.restcountries.presentation.base.BaseView import pl.valueadd.restcountries.presentation.base.fragment.base.IBaseFragment +import pl.valueadd.restcountries.utility.coroutines.cancelSafe import pl.valueadd.restcountries.utility.dependencyinjection.DependencyUtil import pl.valueadd.restcountries.utility.view.snackbar.SnackbarUtil import javax.inject.Inject @@ -30,7 +31,8 @@ abstract class BaseMVPActivity>( ) : MvpActivity(), IBaseActivity, - BaseView { + BaseView, + CoroutineScope by MainScope() { private val delegate: SupportActivityDelegate by lazy { SupportActivityDelegate(this) } @@ -45,25 +47,6 @@ abstract class BaseMVPActivity>( protected open val menuLayout: Int get() = R.menu.main_menu - /** - * Contains disposable subscriptions of streams. - */ - protected val disposables: CompositeDisposable - by lazy { CompositeDisposable() } - - /** - * Add subscription to composite. - */ - protected fun addDisposable(disposable: Disposable): Boolean = - disposables.add(disposable) - - /** - * Clear all subscriptions. - */ - protected fun clearDisposables() { - disposables.clear() - } - protected fun showError(error: String, view: View) = snackBarUtil.showMessage(view, error, ContextCompat.getColor(this, R.color.red)) @@ -98,7 +81,7 @@ abstract class BaseMVPActivity>( } override fun onDestroy() { - clearDisposables() + cancelSafe() delegate.onDestroy() super.onDestroy() diff --git a/app/src/main/java/pl/valueadd/restcountries/presentation/base/fragment/base/BaseFragment.kt b/app/src/main/java/pl/valueadd/restcountries/presentation/base/fragment/base/BaseFragment.kt index 731ae17..d9c6ce4 100644 --- a/app/src/main/java/pl/valueadd/restcountries/presentation/base/fragment/base/BaseFragment.kt +++ b/app/src/main/java/pl/valueadd/restcountries/presentation/base/fragment/base/BaseFragment.kt @@ -10,8 +10,6 @@ import androidx.annotation.LayoutRes import androidx.fragment.app.Fragment import pl.valueadd.restcountries.presentation.base.activity.IBaseActivity import pl.valueadd.restcountries.utility.dependencyinjection.DependencyUtil -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.disposables.Disposable import me.yokeyword.fragmentation.ExtraTransaction import me.yokeyword.fragmentation.ISupportFragment import me.yokeyword.fragmentation.SupportFragmentDelegate @@ -23,25 +21,6 @@ abstract class BaseFragment(@LayoutRes protected val layoutId: Int) : Fragment() private val fragmentDelegate: SupportFragmentDelegate by lazy { SupportFragmentDelegate(this) } - /** - * Contains disposable subscriptions of streams. - */ - protected val disposables: CompositeDisposable - by lazy { CompositeDisposable() } - - /** - * Add subscription to composite. - */ - protected fun addDisposable(disposable: Disposable): Boolean = - disposables.add(disposable) - - /** - * Clear all subscriptions. - */ - protected fun clearDisposables() { - disposables.clear() - } - /* Life cycle */ override fun onCreateView( diff --git a/app/src/main/java/pl/valueadd/restcountries/presentation/base/fragment/base/BaseMVPFragment.kt b/app/src/main/java/pl/valueadd/restcountries/presentation/base/fragment/base/BaseMVPFragment.kt index 909095d..bb25997 100644 --- a/app/src/main/java/pl/valueadd/restcountries/presentation/base/fragment/base/BaseMVPFragment.kt +++ b/app/src/main/java/pl/valueadd/restcountries/presentation/base/fragment/base/BaseMVPFragment.kt @@ -10,14 +10,18 @@ import pl.valueadd.restcountries.utility.view.snackbar.SnackbarUtil import com.hannesdorfmann.mosby3.mvp.delegate.FragmentMvpDelegate import com.hannesdorfmann.mosby3.mvp.delegate.FragmentMvpDelegateImpl import com.hannesdorfmann.mosby3.mvp.delegate.MvpDelegateCallback +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.MainScope import pl.valueadd.restcountries.presentation.base.BasePresenter import pl.valueadd.restcountries.presentation.base.BaseView +import pl.valueadd.restcountries.utility.coroutines.cancelSafe import javax.inject.Inject abstract class BaseMVPFragment>(@LayoutRes layoutId: Int) : BaseFragment(layoutId), MvpDelegateCallback, - BaseView { + BaseView, + CoroutineScope by MainScope() { @Inject lateinit var snackBarUtil: SnackbarUtil @@ -105,8 +109,7 @@ abstract class BaseMVPFragment>(@LayoutRes la } override fun onDetach() { - presenter.clearDisposables() - clearDisposables() + cancelSafe() super.onDetach() mvpDelegate.onDestroy() diff --git a/app/src/main/java/pl/valueadd/restcountries/presentation/main/countries/details/CountryDetailsFragment.kt b/app/src/main/java/pl/valueadd/restcountries/presentation/main/countries/details/CountryDetailsFragment.kt index 6e3ed7c..70e0397 100644 --- a/app/src/main/java/pl/valueadd/restcountries/presentation/main/countries/details/CountryDetailsFragment.kt +++ b/app/src/main/java/pl/valueadd/restcountries/presentation/main/countries/details/CountryDetailsFragment.kt @@ -23,10 +23,9 @@ import pl.valueadd.restcountries.domain.model.country.CountryModel import pl.valueadd.restcountries.presentation.base.fragment.viewstate.back.BackMVPViewStateFragment import pl.valueadd.restcountries.utility.common.merge import pl.valueadd.restcountries.utility.image.loadSVGImage -import pl.valueadd.restcountries.utility.reactivex.onSuccess -import pl.valueadd.restcountries.utility.reactivex.throttleClicks import pl.valueadd.restcountries.utility.view.getChildAtOrNull import pl.valueadd.restcountries.utility.view.setVisible +import pl.valueadd.restcountries.utility.view.throttleClicks import pl.valueadd.restcountries.utility.view.toolbar.CollapsingToolbarState import pl.valueadd.restcountries.utility.view.toolbar.onCollapsingToolbarStateChanged import pl.valueadd.restcountries.view.chip.BorderChip @@ -89,10 +88,9 @@ class CountryDetailsFragment : BackMVPViewStateFragment diff --git a/app/src/main/java/pl/valueadd/restcountries/presentation/main/countries/list/CountryListFragment.kt b/app/src/main/java/pl/valueadd/restcountries/presentation/main/countries/list/CountryListFragment.kt index 99b1a0d..d74a396 100644 --- a/app/src/main/java/pl/valueadd/restcountries/presentation/main/countries/list/CountryListFragment.kt +++ b/app/src/main/java/pl/valueadd/restcountries/presentation/main/countries/list/CountryListFragment.kt @@ -22,9 +22,8 @@ import pl.valueadd.restcountries.presentation.main.countries.list.item.ClickCoun import pl.valueadd.restcountries.presentation.main.countries.list.item.CountryHeaderAdapter import pl.valueadd.restcountries.presentation.main.root.RootFragment import pl.valueadd.restcountries.utility.context.createBottomSheetDialog -import pl.valueadd.restcountries.utility.reactivex.onSuccess -import pl.valueadd.restcountries.utility.reactivex.throttleClicks import pl.valueadd.restcountries.utility.view.applyAligmentToTheRight +import pl.valueadd.restcountries.utility.view.throttleClicks import pl.valueadd.restcountries.view.decorator.CountryItemDecoration import javax.inject.Inject @@ -136,12 +135,10 @@ class CountryListFragment : BackMVPViewStateFragment = Filters.NameAsc) { - countriesDisposable = countryManager - .observeCountries(query, filter) - .apply { - if (isDelayed) { - debounce(DELAY, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread()) - } + observeCountriesJob = scope.launch(exceptionHandler(::onObserveAllCountriesFailed)) { + var countriesFlow = countryManager.observeCountries(query, filter) + if (isDelayed) { + countriesFlow = countriesFlow.debounce(DELAY) } - .observeOnMain() - .subscribe( - ::onObserveAllCountriesSuccess, - ::onObserveAllCountriesFailed - ) - .addTo(disposables) + countriesFlow.collectLatest { + onObserveAllCountriesSuccess(it) + } + } } private fun onObserveAllCountriesSuccess(list: List) = onceViewAttached { view -> diff --git a/dependencies.gradle b/dependencies.gradle index d89dd58..6891232 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -20,6 +20,7 @@ versions.androidx_card_view = "1.0.0" versions.androidx_grid_layout = "1.0.0" versions.androidx_annotation = "1.1.0" versions.androidx_palette = "1.0.0" +versions.coroutines = "1.3.0" versions.lifecycle = "2.0.0" versions.kotlin = "1.3.41" versions.ktlint = "0.32.0" @@ -30,15 +31,8 @@ versions.material_dialogs = "2.6.0" versions.toothpick = "2.1.0" versions.toothpick_testing = "1.1.3" versions.dropwizard_testing = "1.3.13" -versions.rx_java = "2.1.13" -versions.rx_android = "2.0.2" -versions.rx_kotlin = "2.4.0" -versions.rx_network = "3.0.3" -versions.rx_realm_exitensions = "2.5.0" -versions.rx_binding = "3.0.0-alpha2" -versions.retrofit = "2.6.0" +versions.retrofit = "2.6.1" versions.okhttp_logging_interceptor = "4.0.0" -versions.rx_java_extensions = "0.20.10" versions.crashlytics = "2.10.1@aar" versions.joda_time = "2.10.2" versions.mapstruct = "1.3.0.Final" @@ -56,7 +50,7 @@ versions.robolectric = "4.0.2" versions.glide = "4.9.0" versions.realm = "5.12.0" versions.apache_lang = "3.9" -versions.room = "2.1.0" +versions.room = "2.2.0-beta01" versions.no_permissions = "1.1.2" versions.convalida = "3.1.0" versions.easy_validation = "1.0.1" @@ -177,15 +171,10 @@ deps.di = di /** * Reactivex variables. */ -def rx = [:] -rx.java = "io.reactivex.rxjava2:rxjava:$versions.rx_java" -rx.android = "io.reactivex.rxjava2:rxandroid:$versions.rx_android" -rx.kotlin = "io.reactivex.rxjava2:rxkotlin:$versions.rx_android" -rx.java_extensions = "com.github.akarnokd:rxjava2-extensions:$versions.rx_java_extensions" -rx.realm_extensions = "com.github.vicpinm:krealmextensions:$versions.rx_realm_exitensions" -rx.room = "androidx.room:room-rxjava2:$versions.room" -rx.binding = "com.jakewharton.rxbinding3:rxbinding:$versions.rx_binding" -deps.rx = rx +def coroutines = [:] +coroutines.core = "org.jetbrains.kotlinx:kotlinx-coroutines-core:$versions.coroutines" +coroutines.android = "org.jetbrains.kotlinx:kotlinx-coroutines-android:$versions.coroutines" +deps.coroutines = coroutines /** * Room variables. @@ -202,9 +191,7 @@ deps.persistence = persistence def network = [:] network.gson = "com.squareup.retrofit2:converter-gson:$versions.retrofit" network.logging = "com.squareup.okhttp3:logging-interceptor:$versions.okhttp_logging_interceptor" -network.reactive = "com.github.pwittchen:reactivenetwork-rx2:$versions.rx_network" network.runtime = "com.squareup.retrofit2:retrofit:$versions.retrofit" -network.rxjava2 = "com.squareup.retrofit2:adapter-rxjava2:$versions.retrofit" deps.network = network /** diff --git a/domain/build.gradle b/domain/build.gradle index ea2d623..7f495ec 100644 --- a/domain/build.gradle +++ b/domain/build.gradle @@ -72,6 +72,9 @@ dependencies { implementation project(":persistence") implementation project(":utility") + // Coroutines + implementation deps.coroutines.core + // Utility implementation deps.utility.mapstruct diff --git a/domain/src/main/java/pl/valueadd/restcountries/domain/manager/CountryDomainManager.kt b/domain/src/main/java/pl/valueadd/restcountries/domain/manager/CountryDomainManager.kt index 9a9fec8..9829939 100644 --- a/domain/src/main/java/pl/valueadd/restcountries/domain/manager/CountryDomainManager.kt +++ b/domain/src/main/java/pl/valueadd/restcountries/domain/manager/CountryDomainManager.kt @@ -1,9 +1,9 @@ package pl.valueadd.restcountries.domain.manager -import io.reactivex.Completable -import io.reactivex.Flowable -import io.reactivex.Single -import io.reactivex.functions.Function9 +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.conflate +import kotlinx.coroutines.flow.flowOn import pl.valueadd.restcountries.domain.mapper.CountryMapper import pl.valueadd.restcountries.domain.model.country.CountryFlatModel import pl.valueadd.restcountries.domain.model.country.CountryModel @@ -13,11 +13,13 @@ import pl.valueadd.restcountries.domain.model.language.LanguageModel import pl.valueadd.restcountries.domain.model.region.RegionalBlocModel import pl.valueadd.restcountries.network.dto.country.CountryDto import pl.valueadd.restcountries.network.manager.CountryNetworkManager +import pl.valueadd.restcountries.persistence.entity.AltSpellingEntity +import pl.valueadd.restcountries.persistence.entity.CallingCodeEntity import pl.valueadd.restcountries.persistence.entity.CountryEntity import pl.valueadd.restcountries.persistence.entity.CurrencyEntity import pl.valueadd.restcountries.persistence.entity.LanguageEntity import pl.valueadd.restcountries.persistence.entity.RegionalBlocEntity -import pl.valueadd.restcountries.persistence.entity.TimeZoneEntity +import pl.valueadd.restcountries.persistence.entity.TopLevelDomainEntity import pl.valueadd.restcountries.persistence.entity.join.CountryBorderJoin import pl.valueadd.restcountries.persistence.entity.join.CountryCurrencyJoin import pl.valueadd.restcountries.persistence.entity.join.CountryLanguageJoin @@ -31,11 +33,12 @@ import pl.valueadd.restcountries.persistence.manager.LanguagePersistenceManager import pl.valueadd.restcountries.persistence.manager.RegionalBlocPersistenceManager import pl.valueadd.restcountries.persistence.manager.TimeZonePersistenceManager import pl.valueadd.restcountries.persistence.manager.TopLevelDomainPersistenceManager -import pl.valueadd.restcountries.persistence.model.Border -import pl.valueadd.restcountries.utility.reactivex.immediateSingle +import pl.valueadd.restcountries.utility.common.map +import pl.valueadd.restcountries.utility.coroutines.combineTransform import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton +import kotlin.system.measureTimeMillis @Singleton class CountryDomainManager @Inject constructor( @@ -51,46 +54,45 @@ class CountryDomainManager @Inject constructor( private val mapper: CountryMapper ) { - fun observeCountry(countryId: String): Flowable = - Flowable.combineLatest( - observeCountryById(countryId), - observeAltSpellings(countryId), - observeBorders(countryId), - observeCallingCodes(countryId), - observeCurrencies(countryId), - observeLanguages(countryId), - observeRegionalBlocs(countryId), - observeTimeZones(countryId), - observeTopLevelDomains(countryId), - Function9 { country, - altSpellings, - borders, - callingCodes, - currencies, - languages, - regionalBlocs, - timezones, - topLevelDomains -> - - country.also { - it.altSpellings = altSpellings - it.borders = borders - it.callingCodes = callingCodes - it.currencies = currencies - it.languages = languages - it.regionalBlocs = regionalBlocs - it.timezones = timezones - it.topLevelDomains = topLevelDomains - } - } - ) + fun observeCountry(countryId: String): Flow = combineTransform( + observeCountryById(countryId), + observeAltSpellings(countryId), + observeBorders(countryId), + observeCallingCodes(countryId), + observeCurrencies(countryId), + observeLanguages(countryId), + observeRegionalBlocs(countryId), + observeTimeZones(countryId), + observeTopLevelDomains(countryId) + ) { country, + altSpellings, + borders, + callingCodes, + currencies, + languages, + regionalBlocs, + timezones, + topLevelDomains -> + country.also { + it.altSpellings = altSpellings + it.borders = borders + it.callingCodes = callingCodes + it.currencies = currencies + it.languages = languages + it.regionalBlocs = regionalBlocs + it.timezones = timezones + it.topLevelDomains = topLevelDomains + } + emit(country) + }.conflate() + .flowOn(Dispatchers.IO) - fun observeAllCountries(): Flowable> = + fun observeAllCountries(): Flow> = persistence .observeAllCountries() .map(mapper::mapCountryEntitiesToModels) - fun observeCountries(query: String, filter: Filter): Flowable> = + fun observeCountries(query: String, filter: Filter): Flow> = persistence .observeCountries(query, filter.isAscending) .map(mapper::mapCountryEntitiesToModels) @@ -100,145 +102,161 @@ class CountryDomainManager @Inject constructor( } else list } - fun downloadAllCountries(): Completable = - network - .getAllCountries() - .flatMapCompletable(::saveAllCountries) + suspend fun downloadAllCountries() { + val countries = network.getAllCountries() + val savingTime = measureTimeMillis { + saveAllCountries(countries) + } + Timber.d("savingTime: " + savingTime) + } /* Save all Countries */ - private fun saveAllCountries(list: List): Completable { - val saveEntities: MutableList = mutableListOf() - val saveRelations: MutableList = mutableListOf() - - return immediateSingle { - mutableListOf().also { entities -> - for (dto in list) { - - val entity: CountryEntity = mapper.mapCountryDtoToEntity(dto) - - saveEntities.add(saveTimeZonesFor(entity.id, mapper.mapTimeZoneDtosToEntities(dto.timezones))) - - mapper.mapCallingCodeDtosToEntities(dto.callingCodes, entity.id).let { - saveEntities.add(callingCodePersistence.saveCallingCodes(it)) + private suspend fun saveAllCountries(list: List) { + val timezonesRelations = mutableListOf() + val callingCodes = hashSetOf() + val altSpellings = hashSetOf() + val topLevelDomains = hashSetOf() + val regionalBlocks = hashSetOf() + val regionalBlocksRelations = mutableListOf() + val languages = hashSetOf() + val languagesRelations = mutableListOf() + val currencies = hashSetOf() + val currenciesRelations = mutableListOf() + val bordersRelations = mutableListOf() + + var entities = emptyList() + val mappingTime = measureTimeMillis { + entities = list.map { dto -> + val entity: CountryEntity = mapper.mapCountryDtoToEntity(dto) + + val timeZoneEntities = mapper.mapTimeZoneDtosToEntities(dto.timezones) // TODO saving timezones improvement + timezonesRelations += timeZonePersistence.saveTimezonesIds(timeZoneEntities) + .map { id -> + CountryTimeZoneJoin(entity.id, id) } - mapper.mapAltSpellingDtosToEntities(dto.altSpellings, entity.id).let { - saveEntities.add(altSpellingPersistence.saveAltSpellings(it)) - } + mapper.mapCallingCodeDtosToEntities(dto.callingCodes, entity.id).let { + callingCodes += it + } - mapper.mapTopLevelDomainDtosToEntities(dto.topLevelDomains, entity.id).let { - saveEntities.add(topLevelDomainPersistence.saveTopLevelDomains(it)) - } + mapper.mapAltSpellingDtosToEntities(dto.altSpellings, entity.id).let { + altSpellings += it + } - mapper.mapRegionalBlocDtosToEntities(dto.regionalBlocs).let { - saveEntities.add(regionalBlocPersistence.saveRegionalBlocs(it)) - saveRelations.add(saveRegionalBlocsFor(entity.id, it)) - } + mapper.mapTopLevelDomainDtosToEntities(dto.topLevelDomains, entity.id).let { + topLevelDomains += it + } - mapper.mapLanguageDtosToEntities(dto.languages).let { - saveEntities.add(languagePersistence.saveLanguages(it)) - saveRelations.add(saveLanguageJoinsFor(entity.id, it)) - } + mapper.mapRegionalBlocDtosToEntities(dto.regionalBlocs).let { + regionalBlocks += it + regionalBlocksRelations += it.map { CountryRegionalBlocJoin(entity.id, it.id) } + } - mapper.mapCurrencyDtosToEntities(dto.currencies).let { - saveEntities.add(currencyPersistence.saveCurrencies(it)) - saveRelations.add(saveCurrencyJoinsFor(entity.id, it)) - } + mapper.mapLanguageDtosToEntities(dto.languages).let { + languages += it + languagesRelations += it.map { CountryLanguageJoin(entity.id, it.id) } + } - mapper.mapBorderDtosToEntities(dto.borders).let { - saveRelations.add(saveBorderJoinsFor(entity.id, it)) - } + mapper.mapCurrencyDtosToEntities(dto.currencies).let { + currencies += it + currenciesRelations += it.map { CountryCurrencyJoin(entity.id, it.id) } + } - entities.add(entity) + mapper.mapBorderDtosToEntities(dto.borders).let { + bordersRelations += it.map { CountryBorderJoin(entity.id, it.id) } } - } - }.flatMapCompletable(persistence::saveCountries) - .andThen(Completable.merge(saveEntities)) - .andThen(Completable.merge(saveRelations)) - } - private fun saveCurrencyJoinsFor(countryId: String, entities: List): Completable { - val map: Single> = immediateSingle { - entities.map { CountryCurrencyJoin(countryId, it.id) } + return@map entity + } } - - return map.flatMapCompletable(persistence::saveCountryCurrencyJoins) - } - - private fun saveLanguageJoinsFor(countryId: String, entities: List): Completable { - val map: Single> = immediateSingle { - entities.map { CountryLanguageJoin(countryId, it.id) } + Timber.d("mappingTime: " + mappingTime) // TODO remove time measuring and split this big method to small + val timePersistence = measureTimeMillis { + persistence.saveCountries(entities) } + Timber.d("timePersistence: " + timePersistence) + val timeEntitiesJobs = measureTimeMillis { + saveTimeZonesFor(timezonesRelations) + callingCodePersistence.saveCallingCodes(callingCodes.toList()) + altSpellingPersistence.saveAltSpellings(altSpellings.toList()) + topLevelDomainPersistence.saveTopLevelDomains(topLevelDomains.toList()) + regionalBlocPersistence.saveRegionalBlocs(regionalBlocks.toList()) + languagePersistence.saveLanguages(languages.toList()) + currencyPersistence.saveCurrencies(currencies.toList()) + } + Timber.d("timeEntitiesJobs: " + timeEntitiesJobs) + val timeRelationsJobs = measureTimeMillis { + saveRegionalBlocsFor(regionalBlocksRelations) + saveLanguageJoinsFor(languagesRelations) + saveCurrencyJoinsFor(currenciesRelations) + saveBorderJoinsFor(bordersRelations) + } + Timber.d("timeRelationsJobs: " + timeRelationsJobs) + } - return map.flatMapCompletable(persistence::saveCountryLanguageJoins) + private suspend fun saveCurrencyJoinsFor(joins: List) { + persistence.saveCountryCurrencyJoins(joins) } - private fun saveBorderJoinsFor(countryId: String, entities: List): Completable { - val map: Single> = immediateSingle { - entities.map { CountryBorderJoin(countryId, it.id) } - } - Timber.d("Count of borders to save: ${entities.size} for countryId = $countryId") - return map.flatMapCompletable(persistence::saveCountryBorderJoins) + private suspend fun saveLanguageJoinsFor(joins: List) { + persistence.saveCountryLanguageJoins(joins) } - private fun saveRegionalBlocsFor(countryId: String, entities: List): Completable { - val map: Single> = immediateSingle { - entities.map { CountryRegionalBlocJoin(countryId, it.id) } - } + private suspend fun saveBorderJoinsFor(joins: List) { + persistence.saveCountryBorderJoins(joins) + } - return map.flatMapCompletable(persistence::saveCountryRegionalBlocJoins) + private suspend fun saveRegionalBlocsFor(joins: List) { + persistence.saveCountryRegionalBlocJoins(joins) } - private fun saveTimeZonesFor(countryId: String, entities: List): Completable = - timeZonePersistence.saveTimezonesIds(entities) - .map { list -> - list.map { CountryTimeZoneJoin(countryId, it) } - }.flatMapCompletable(persistence::saveCountryTimeZoneJoins) + private suspend fun saveTimeZonesFor(joins: List) { + persistence.saveCountryTimeZoneJoins(joins) + } /* Fetch Country */ - private fun observeCountryById(countryId: String): Flowable = + private fun observeCountryById(countryId: String): Flow = persistence .observeCountry(countryId) .map(mapper::mapCountryEntityToModel) - private fun observeAltSpellings(countryId: String): Flowable> = + private fun observeAltSpellings(countryId: String): Flow> = altSpellingPersistence .observeAltSpellings(countryId) .map(mapper::mapAltSpellingEntitiesToModels) - private fun observeBorders(countryId: String): Flowable> = + private fun observeBorders(countryId: String): Flow> = persistence .observeBorders(countryId) .map(mapper::mapCountriesFlatToModels) - private fun observeCallingCodes(countryId: String): Flowable> = + private fun observeCallingCodes(countryId: String): Flow> = callingCodePersistence .observeCallingCodes(countryId) .map(mapper::mapCallingCodeEntitiesToModels) - private fun observeCurrencies(countryId: String): Flowable> = + private fun observeCurrencies(countryId: String): Flow> = currencyPersistence .observeCurrencies(countryId) .map(mapper::mapCurrencyEntitiesToModels) - private fun observeLanguages(countryId: String): Flowable> = + private fun observeLanguages(countryId: String): Flow> = languagePersistence .observeLanguages(countryId) .map(mapper::mapLanguageEntitiesToModels) - private fun observeRegionalBlocs(countryId: String): Flowable> = + private fun observeRegionalBlocs(countryId: String): Flow> = regionalBlocPersistence .observeRegionalBlocs(countryId) .map(mapper::mapRegionalBlocEntitiesToModels) - private fun observeTimeZones(countryId: String): Flowable> = + private fun observeTimeZones(countryId: String): Flow> = timeZonePersistence .observeTimeZones(countryId) .map(mapper::mapTimeZoneEntitiesToModels) - private fun observeTopLevelDomains(countryId: String): Flowable> = + private fun observeTopLevelDomains(countryId: String): Flow> = topLevelDomainPersistence .observeTopLevelDomains(countryId) .map(mapper::mapTopLevelDomainEntitiesToModels) diff --git a/network/build.gradle b/network/build.gradle index f47588e..a5ba775 100644 --- a/network/build.gradle +++ b/network/build.gradle @@ -72,8 +72,6 @@ dependencies { implementation deps.network.gson implementation deps.network.logging implementation deps.network.runtime - implementation deps.network.reactive - implementation deps.network.rxjava2 implementation deps.network.gson // Unit tests diff --git a/network/src/main/java/pl/valueadd/restcountries/network/NetworkModule.kt b/network/src/main/java/pl/valueadd/restcountries/network/NetworkModule.kt index ee6b669..e8ddc6c 100644 --- a/network/src/main/java/pl/valueadd/restcountries/network/NetworkModule.kt +++ b/network/src/main/java/pl/valueadd/restcountries/network/NetworkModule.kt @@ -14,7 +14,6 @@ import pl.valueadd.restcountries.network.definition.OkHttpBuilder import pl.valueadd.restcountries.network.definition.OkHttpClientInstance import pl.valueadd.restcountries.network.definition.RetrofitBuilder import pl.valueadd.restcountries.network.definition.ServerUrl -import pl.valueadd.restcountries.network.http.CallAdapterFactoryProvider import pl.valueadd.restcountries.network.http.ConverterFactoryProvider import pl.valueadd.restcountries.network.http.GsonProvider import pl.valueadd.restcountries.network.http.OkHttpClientBuilderProvider @@ -50,7 +49,6 @@ class NetworkModule : Module() { bind(CallAdapter.Factory::class.java) .withName(CallAdapterFactory::class.java) - .toProviderInstance(provideCallAdapterFactoryProvider()) bind(Gson::class.java) .withName(GsonInstance::class.java) @@ -83,9 +81,6 @@ class NetworkModule : Module() { internal fun provideConverterFactoryProvider(): ConverterFactoryProvider = ConverterFactoryProvider() - internal fun provideCallAdapterFactoryProvider(): CallAdapterFactoryProvider = - CallAdapterFactoryProvider() - internal fun provideServiceUrl(): String = BuildConfig.SERVER_URL diff --git a/network/src/main/java/pl/valueadd/restcountries/network/exception/Exception.kt b/network/src/main/java/pl/valueadd/restcountries/network/exception/Exception.kt index 8e1f019..b4830c0 100644 --- a/network/src/main/java/pl/valueadd/restcountries/network/exception/Exception.kt +++ b/network/src/main/java/pl/valueadd/restcountries/network/exception/Exception.kt @@ -1,47 +1,28 @@ package pl.valueadd.restcountries.network.exception +import org.apache.commons.lang3.StringUtils.EMPTY import pl.valueadd.restcountries.utility.exception.HttpCallException import pl.valueadd.restcountries.utility.exception.UnavailableNetworkException import pl.valueadd.restcountries.utility.exception.UnreachableServerException -import io.reactivex.Completable -import io.reactivex.Single -import org.apache.commons.lang3.StringUtils.EMPTY import retrofit2.HttpException import java.net.SocketTimeoutException import java.net.UnknownHostException /** * Map common network errors. - * @return [Completable] */ -fun Completable.mapNetworkErrors(): Completable = - this.onErrorResumeNext { error -> +@Suppress("ThrowsCount") +suspend fun mapNetworkErrors(method: (suspend () -> T)): T { + try { + return method() + } catch (error: Throwable) { when (error) { - is SocketTimeoutException -> Completable.error(UnavailableNetworkException(error)) - is UnknownHostException -> Completable.error(UnreachableServerException(error)) - is HttpException -> Completable.error( - HttpCallException(error, - error.code(), - error.response()?.errorBody()?.string() ?: EMPTY) - ) - else -> Completable.error(error) + is SocketTimeoutException -> throw UnavailableNetworkException(error) + is UnknownHostException -> throw UnreachableServerException(error) + is HttpException -> throw HttpCallException(error, + error.code(), + error.response()?.errorBody()?.string() ?: EMPTY) + else -> throw error } } - -/** - * Map common network errors. - * @return [Single]<*> - */ -fun Single.mapNetworkErrors(): Single = - this.onErrorResumeNext { error -> - when (error) { - is SocketTimeoutException -> Single.error(UnavailableNetworkException(error)) - is UnknownHostException -> Single.error(UnreachableServerException(error)) - is HttpException -> Single.error( - HttpCallException(error, - error.code(), - error.response()?.errorBody()?.string() ?: EMPTY) - ) - else -> Single.error(error) - } - } \ No newline at end of file +} diff --git a/network/src/main/java/pl/valueadd/restcountries/network/http/HttpProviders.kt b/network/src/main/java/pl/valueadd/restcountries/network/http/HttpProviders.kt index 11ec868..a8a8f32 100644 --- a/network/src/main/java/pl/valueadd/restcountries/network/http/HttpProviders.kt +++ b/network/src/main/java/pl/valueadd/restcountries/network/http/HttpProviders.kt @@ -6,7 +6,6 @@ import com.google.gson.GsonBuilder import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import pl.valueadd.restcountries.network.adapter.LatLngTypeAdapter -import pl.valueadd.restcountries.network.definition.CallAdapterFactory import pl.valueadd.restcountries.network.definition.ConverterFactory import pl.valueadd.restcountries.network.definition.HttpLoggingLevel import pl.valueadd.restcountries.network.definition.OkHttpBuilder @@ -15,10 +14,8 @@ import pl.valueadd.restcountries.network.definition.ServerUrl import pl.valueadd.restcountries.network.dto.country.LatLngDto import pl.valueadd.restcountries.network.interceptor.HeaderAuthorizationInterceptor import pl.valueadd.restcountries.utility.BuildConfig -import retrofit2.CallAdapter import retrofit2.Converter import retrofit2.Retrofit -import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory import retrofit2.converter.gson.GsonConverterFactory import toothpick.ProvidesSingletonInScope import java.util.concurrent.TimeUnit @@ -42,14 +39,12 @@ class RetrofitProvider @Inject constructor( @Singleton @ProvidesSingletonInScope class RetrofitBuilderProvider @Inject constructor( - @CallAdapterFactory private val callAdapterFactory: CallAdapter.Factory, @ConverterFactory private val converterFactory: Converter.Factory, @pl.valueadd.restcountries.network.definition.OkHttpClientInstance private val client: OkHttpClient ) : Provider { override fun get(): Retrofit.Builder = Retrofit.Builder() - .addCallAdapterFactory(callAdapterFactory) .addConverterFactory(converterFactory) .client(client) } @@ -79,14 +74,6 @@ class OkHttpClientProvider @Inject constructor( builder.build() } -@Singleton -@ProvidesSingletonInScope -class CallAdapterFactoryProvider : Provider { - - override fun get(): CallAdapter.Factory = - RxJava2CallAdapterFactory.create() -} - @Singleton @ProvidesSingletonInScope class ConverterFactoryProvider : Provider { diff --git a/network/src/main/java/pl/valueadd/restcountries/network/manager/CountryNetworkManager.kt b/network/src/main/java/pl/valueadd/restcountries/network/manager/CountryNetworkManager.kt index 4562aa4..626cc24 100644 --- a/network/src/main/java/pl/valueadd/restcountries/network/manager/CountryNetworkManager.kt +++ b/network/src/main/java/pl/valueadd/restcountries/network/manager/CountryNetworkManager.kt @@ -1,20 +1,14 @@ package pl.valueadd.restcountries.network.manager -import io.reactivex.Single -import io.reactivex.annotations.SchedulerSupport import pl.valueadd.restcountries.network.dto.country.CountryDto -import pl.valueadd.restcountries.network.exception.mapNetworkErrors import pl.valueadd.restcountries.network.service.CountryApi -import pl.valueadd.restcountries.utility.reactivex.subscribeOnIo +import pl.valueadd.restcountries.network.exception.mapNetworkErrors import javax.inject.Inject import javax.inject.Singleton @Singleton -@SchedulerSupport(value = SchedulerSupport.IO) class CountryNetworkManager @Inject constructor(private val api: CountryApi) { - fun getAllCountries(): Single> = - api.getAllCountries() - .mapNetworkErrors() - .subscribeOnIo() + suspend fun getAllCountries(): List = + mapNetworkErrors(api::getAllCountries) } \ No newline at end of file diff --git a/network/src/main/java/pl/valueadd/restcountries/network/service/CountryApi.kt b/network/src/main/java/pl/valueadd/restcountries/network/service/CountryApi.kt index 176eba1..2805334 100644 --- a/network/src/main/java/pl/valueadd/restcountries/network/service/CountryApi.kt +++ b/network/src/main/java/pl/valueadd/restcountries/network/service/CountryApi.kt @@ -1,11 +1,10 @@ package pl.valueadd.restcountries.network.service -import io.reactivex.Single import pl.valueadd.restcountries.network.dto.country.CountryDto import retrofit2.http.GET interface CountryApi { @GET("all") - fun getAllCountries(): Single> + suspend fun getAllCountries(): List } \ No newline at end of file diff --git a/persistence/build.gradle b/persistence/build.gradle index 7409158..3293a41 100644 --- a/persistence/build.gradle +++ b/persistence/build.gradle @@ -71,9 +71,7 @@ dependencies { // Persistence implementation deps.persistence.room - - // Reactivex - implementation deps.rx.room + implementation deps.persistence.room_ktx // Unit tests testImplementation deps.test.mockito diff --git a/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/AltSpellingDao.kt b/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/AltSpellingDao.kt index 554060e..cfd3679 100644 --- a/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/AltSpellingDao.kt +++ b/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/AltSpellingDao.kt @@ -2,7 +2,7 @@ package pl.valueadd.restcountries.persistence.dao import androidx.room.Dao import androidx.room.Query -import io.reactivex.Flowable +import kotlinx.coroutines.flow.Flow import pl.valueadd.restcountries.persistence.entity.AltSpellingEntity @Dao @@ -14,5 +14,5 @@ abstract class AltSpellingDao : BaseDao() { WHERE alt_spellings.country_id = :countryId COLLATE NOCASE """) - abstract fun observeAltSpellings(countryId: String): Flowable> + abstract fun observeAltSpellings(countryId: String): Flow> } \ No newline at end of file diff --git a/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/BaseDao.kt b/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/BaseDao.kt index 07bacdc..757a40c 100644 --- a/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/BaseDao.kt +++ b/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/BaseDao.kt @@ -4,8 +4,6 @@ import androidx.room.Delete import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Update -import io.reactivex.Completable -import io.reactivex.Single abstract class BaseDao { @@ -14,47 +12,47 @@ abstract class BaseDao { * @return id of inserted entity. */ @Insert(onConflict = OnConflictStrategy.REPLACE) - abstract fun insert(entity: E): Single + abstract suspend fun insert(entity: E): Long /** * Insert entities to database. * @return [Completable] */ @Insert(onConflict = OnConflictStrategy.REPLACE) - abstract fun insert(entities: Iterable): Completable + abstract suspend fun insert(entities: Iterable) /** * Insert entities to database. * @return [Single] which contains [List] of [ids][Long]. */ @Insert(onConflict = OnConflictStrategy.REPLACE) - abstract fun insertEntities(entities: Collection): Single> + abstract suspend fun insertEntities(entities: Collection): List /** * Update the entity. * @return count of updated rows. */ @Update(onConflict = OnConflictStrategy.REPLACE) - abstract fun update(entity: E): Single + abstract suspend fun update(entity: E): Int /** * Update the entities. * @return count of updated rows. */ @Update(onConflict = OnConflictStrategy.REPLACE) - abstract fun update(entities: Iterable): Completable + abstract suspend fun update(entities: Iterable) /** * Delete the entity. * @return count of deleted rows. */ @Delete - abstract fun remove(entity: E): Single + abstract suspend fun remove(entity: E): Int /** * Delete the entities. * @return count of deleted rows. */ @Delete - abstract fun remove(entities: Iterable): Completable + abstract suspend fun remove(entities: Iterable) } \ No newline at end of file diff --git a/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/CallingCodeDao.kt b/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/CallingCodeDao.kt index e39dd18..3bbe2f8 100644 --- a/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/CallingCodeDao.kt +++ b/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/CallingCodeDao.kt @@ -2,7 +2,7 @@ package pl.valueadd.restcountries.persistence.dao import androidx.room.Dao import androidx.room.Query -import io.reactivex.Flowable +import kotlinx.coroutines.flow.Flow import pl.valueadd.restcountries.persistence.entity.CallingCodeEntity @Dao @@ -14,5 +14,5 @@ abstract class CallingCodeDao : BaseDao() { WHERE calling_codes.country_id = :countryId COLLATE NOCASE """) - abstract fun observeCallingCodes(countryId: String): Flowable> + abstract fun observeCallingCodes(countryId: String): Flow> } \ No newline at end of file diff --git a/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/CountryDao.kt b/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/CountryDao.kt index ee363bb..4ada3f5 100644 --- a/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/CountryDao.kt +++ b/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/CountryDao.kt @@ -2,7 +2,7 @@ package pl.valueadd.restcountries.persistence.dao import androidx.room.Dao import androidx.room.Query -import io.reactivex.Flowable +import kotlinx.coroutines.flow.Flow import pl.valueadd.restcountries.persistence.entity.CountryEntity import pl.valueadd.restcountries.persistence.model.CountryFlat @@ -13,7 +13,7 @@ abstract class CountryDao : BaseDao() { SELECT * FROM countries """) - abstract fun observeAllCountries(): Flowable> + abstract fun observeAllCountries(): Flow> @Query(value = """ SELECT * @@ -22,7 +22,7 @@ abstract class CountryDao : BaseDao() { COLLATE NOCASE LIMIT 1 """) - abstract fun observeCountry(countryId: String): Flowable + abstract fun observeCountry(countryId: String): Flow @Query(value = """ SELECT * @@ -30,7 +30,7 @@ abstract class CountryDao : BaseDao() { WHERE countries.alpha3_code IN (:countryIds) ORDER BY name COLLATE NOCASE ASC """) - abstract fun observeCountries(countryIds: List): Flowable> + abstract fun observeCountries(countryIds: List): Flow> @Query(value = """ SELECT name, alpha3_code, alpha2_code, flag_url @@ -38,7 +38,7 @@ abstract class CountryDao : BaseDao() { WHERE countries.alpha3_code IN (:countryIds) ORDER BY name COLLATE NOCASE ASC """) - abstract fun observeCountriesFlat(countryIds: List): Flowable> + abstract fun observeCountriesFlat(countryIds: List): Flow> @Query(value = """ SELECT name, alpha3_code, alpha2_code, flag_url @@ -47,7 +47,7 @@ abstract class CountryDao : BaseDao() { ON countries.alpha3_code = country_border_join.border_id WHERE country_border_join.country_id = :countryId """) - abstract fun observeCountriesFlat(countryId: String): Flowable> + abstract fun observeCountriesFlat(countryId: String): Flow> @Query(""" SELECT * @@ -61,5 +61,5 @@ abstract class CountryDao : BaseDao() { CASE WHEN :ascendingOrder = 1 THEN name END ASC, CASE WHEN :ascendingOrder = 0 THEN name END DESC """) - abstract fun observeCountries(query: String, ascendingOrder: Boolean): Flowable> + abstract fun observeCountries(query: String, ascendingOrder: Boolean): Flow> } \ No newline at end of file diff --git a/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/CurrencyDao.kt b/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/CurrencyDao.kt index 8d2f454..3ca0245 100644 --- a/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/CurrencyDao.kt +++ b/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/CurrencyDao.kt @@ -2,7 +2,7 @@ package pl.valueadd.restcountries.persistence.dao import androidx.room.Dao import androidx.room.Query -import io.reactivex.Flowable +import kotlinx.coroutines.flow.Flow import pl.valueadd.restcountries.persistence.entity.CurrencyEntity @Dao @@ -15,5 +15,5 @@ abstract class CurrencyDao : BaseDao() { ON currencies.code = country_currency_join.currency_id WHERE country_currency_join.country_id = :countryId """) - abstract fun observeCurriences(countryId: String): Flowable> + abstract fun observeCurriences(countryId: String): Flow> } \ No newline at end of file diff --git a/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/LanguageDao.kt b/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/LanguageDao.kt index e22501d..6143176 100644 --- a/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/LanguageDao.kt +++ b/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/LanguageDao.kt @@ -2,7 +2,7 @@ package pl.valueadd.restcountries.persistence.dao import androidx.room.Dao import androidx.room.Query -import io.reactivex.Flowable +import kotlinx.coroutines.flow.Flow import pl.valueadd.restcountries.persistence.entity.LanguageEntity @Dao @@ -15,5 +15,5 @@ abstract class LanguageDao : BaseDao() { ON languages.iso6392 = country_language_join.language_id WHERE country_language_join.country_id = :countryId """) - abstract fun observeLanguages(countryId: String): Flowable> + abstract fun observeLanguages(countryId: String): Flow> } \ No newline at end of file diff --git a/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/RegionalBlocDao.kt b/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/RegionalBlocDao.kt index 3e61817..b3b7fbe 100644 --- a/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/RegionalBlocDao.kt +++ b/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/RegionalBlocDao.kt @@ -2,7 +2,7 @@ package pl.valueadd.restcountries.persistence.dao import androidx.room.Dao import androidx.room.Query -import io.reactivex.Flowable +import kotlinx.coroutines.flow.Flow import pl.valueadd.restcountries.persistence.entity.RegionalBlocEntity @Dao @@ -15,5 +15,5 @@ abstract class RegionalBlocDao : BaseDao() { ON regional_blocks.name = country_regional_bloc_join.regional_bloc_id WHERE country_regional_bloc_join.country_id = :countryId """) - abstract fun observeRegionalBlocs(countryId: String): Flowable> + abstract fun observeRegionalBlocs(countryId: String): Flow> } \ No newline at end of file diff --git a/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/TimeZoneDao.kt b/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/TimeZoneDao.kt index 4b93b22..56dd403 100644 --- a/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/TimeZoneDao.kt +++ b/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/TimeZoneDao.kt @@ -2,7 +2,7 @@ package pl.valueadd.restcountries.persistence.dao import androidx.room.Dao import androidx.room.Query -import io.reactivex.Flowable +import kotlinx.coroutines.flow.Flow import pl.valueadd.restcountries.persistence.entity.TimeZoneEntity @Dao @@ -15,5 +15,5 @@ abstract class TimeZoneDao : BaseDao() { ON timezones.id = country_time_zone_join.time_zone_id WHERE country_time_zone_join.country_id = :countryId """) - abstract fun observeTimeZones(countryId: String): Flowable> + abstract fun observeTimeZones(countryId: String): Flow> } \ No newline at end of file diff --git a/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/TopLevelDomainDao.kt b/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/TopLevelDomainDao.kt index a471cc7..e4b98e2 100644 --- a/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/TopLevelDomainDao.kt +++ b/persistence/src/main/java/pl/valueadd/restcountries/persistence/dao/TopLevelDomainDao.kt @@ -2,7 +2,7 @@ package pl.valueadd.restcountries.persistence.dao import androidx.room.Dao import androidx.room.Query -import io.reactivex.Flowable +import kotlinx.coroutines.flow.Flow import pl.valueadd.restcountries.persistence.entity.TopLevelDomainEntity @Dao @@ -13,5 +13,5 @@ abstract class TopLevelDomainDao : BaseDao() { FROM top_level_domains WHERE top_level_domains.country_id = :countryId """) - abstract fun observeTopLevelDomains(countryId: String): Flowable> + abstract fun observeTopLevelDomains(countryId: String): Flow> } \ No newline at end of file diff --git a/persistence/src/main/java/pl/valueadd/restcountries/persistence/manager/AltSpellingPersistenceManager.kt b/persistence/src/main/java/pl/valueadd/restcountries/persistence/manager/AltSpellingPersistenceManager.kt index 624eba8..c3f336b 100644 --- a/persistence/src/main/java/pl/valueadd/restcountries/persistence/manager/AltSpellingPersistenceManager.kt +++ b/persistence/src/main/java/pl/valueadd/restcountries/persistence/manager/AltSpellingPersistenceManager.kt @@ -1,29 +1,22 @@ package pl.valueadd.restcountries.persistence.manager -import io.reactivex.Completable -import io.reactivex.Flowable -import io.reactivex.Single -import io.reactivex.annotations.SchedulerSupport +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged import pl.valueadd.restcountries.persistence.dao.AltSpellingDao import pl.valueadd.restcountries.persistence.entity.AltSpellingEntity -import pl.valueadd.restcountries.utility.reactivex.subscribeOnIo import javax.inject.Inject import javax.inject.Singleton @Singleton -@SchedulerSupport(value = SchedulerSupport.IO) class AltSpellingPersistenceManager @Inject constructor(private val dao: AltSpellingDao) { - fun saveAltSpellings(list: List): Completable = + suspend fun saveAltSpellings(list: List) = dao.insert(list) - .subscribeOnIo() - fun saveAltSpellingsIds(list: List): Single> = + suspend fun saveAltSpellingsIds(list: List): List = dao.insertEntities(list) - .subscribeOnIo() - fun observeAltSpellings(countryId: String): Flowable> = + fun observeAltSpellings(countryId: String): Flow> = dao.observeAltSpellings(countryId) .distinctUntilChanged() - .subscribeOnIo() } \ No newline at end of file diff --git a/persistence/src/main/java/pl/valueadd/restcountries/persistence/manager/CallingCodePersistenceManager.kt b/persistence/src/main/java/pl/valueadd/restcountries/persistence/manager/CallingCodePersistenceManager.kt index 5ba031a..2073dce 100644 --- a/persistence/src/main/java/pl/valueadd/restcountries/persistence/manager/CallingCodePersistenceManager.kt +++ b/persistence/src/main/java/pl/valueadd/restcountries/persistence/manager/CallingCodePersistenceManager.kt @@ -1,29 +1,22 @@ package pl.valueadd.restcountries.persistence.manager -import io.reactivex.Completable -import io.reactivex.Flowable -import io.reactivex.Single -import io.reactivex.annotations.SchedulerSupport +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged import pl.valueadd.restcountries.persistence.dao.CallingCodeDao import pl.valueadd.restcountries.persistence.entity.CallingCodeEntity -import pl.valueadd.restcountries.utility.reactivex.subscribeOnIo import javax.inject.Inject import javax.inject.Singleton @Singleton -@SchedulerSupport(value = SchedulerSupport.IO) class CallingCodePersistenceManager @Inject constructor(private val dao: CallingCodeDao) { - fun saveCallingCodesIds(list: List): Single> = + suspend fun saveCallingCodesIds(list: List): List = dao.insertEntities(list) - .subscribeOnIo() - fun saveCallingCodes(list: List): Completable = + suspend fun saveCallingCodes(list: List) = dao.insert(list) - .subscribeOnIo() - fun observeCallingCodes(countryId: String): Flowable> = + fun observeCallingCodes(countryId: String): Flow> = dao.observeCallingCodes(countryId) .distinctUntilChanged() - .subscribeOnIo() } \ No newline at end of file diff --git a/persistence/src/main/java/pl/valueadd/restcountries/persistence/manager/CountryPersistenceManager.kt b/persistence/src/main/java/pl/valueadd/restcountries/persistence/manager/CountryPersistenceManager.kt index 11da892..99762a5 100644 --- a/persistence/src/main/java/pl/valueadd/restcountries/persistence/manager/CountryPersistenceManager.kt +++ b/persistence/src/main/java/pl/valueadd/restcountries/persistence/manager/CountryPersistenceManager.kt @@ -1,8 +1,7 @@ package pl.valueadd.restcountries.persistence.manager -import io.reactivex.Completable -import io.reactivex.Flowable -import io.reactivex.annotations.SchedulerSupport +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged import org.apache.commons.lang3.StringUtils.stripAccents import pl.valueadd.restcountries.persistence.dao.CountryBorderDao import pl.valueadd.restcountries.persistence.dao.CountryCurrencyDao @@ -17,12 +16,10 @@ import pl.valueadd.restcountries.persistence.entity.join.CountryLanguageJoin import pl.valueadd.restcountries.persistence.entity.join.CountryRegionalBlocJoin import pl.valueadd.restcountries.persistence.entity.join.CountryTimeZoneJoin import pl.valueadd.restcountries.persistence.model.CountryFlat -import pl.valueadd.restcountries.utility.reactivex.subscribeOnIo import javax.inject.Inject import javax.inject.Singleton @Singleton -@SchedulerSupport(value = SchedulerSupport.IO) class CountryPersistenceManager @Inject constructor( private val dao: CountryDao, private val currencyDao: CountryCurrencyDao, @@ -32,87 +29,63 @@ class CountryPersistenceManager @Inject constructor( private val borderDao: CountryBorderDao ) { - fun observeAllCountries(): Flowable> = + fun observeAllCountries(): Flow> = dao.observeAllCountries() .distinctUntilChanged() - .subscribeOnIo() - fun observeCountries(countryIds: List): Flowable> = + fun observeCountries(countryIds: List): Flow> = dao.observeCountries(countryIds) .distinctUntilChanged() - .subscribeOnIo() - fun observeCountries(query: String, ascendingOrder: Boolean = true): Flowable> = + fun observeCountries(query: String, ascendingOrder: Boolean = true): Flow> = dao.observeCountries(stripAccents(query), ascendingOrder) .distinctUntilChanged() - .subscribeOnIo() - fun observeCountriesFlat(countryIds: List): Flowable> = + fun observeCountriesFlat(countryIds: List): Flow> = dao.observeCountriesFlat(countryIds) .distinctUntilChanged() - .subscribeOnIo() - fun observeBorders(countryId: String): Flowable> = + fun observeBorders(countryId: String): Flow> = dao.observeCountriesFlat(countryId) .distinctUntilChanged() - .subscribeOnIo() - fun observeCountry(countryId: String): Flowable = + fun observeCountry(countryId: String): Flow = dao.observeCountry(countryId) .distinctUntilChanged() - .subscribeOnIo() - fun saveCountries(list: List): Completable = + suspend fun saveCountries(list: List) = dao.insert(list) - .subscribeOnIo() - fun saveCountry(entity: CountryEntity): Completable = + suspend fun saveCountry(entity: CountryEntity) = dao.insert(entity) - .subscribeOnIo() - .ignoreElement() - fun saveCountryCurrencyJoins(list: List): Completable = + suspend fun saveCountryCurrencyJoins(list: List) = currencyDao.insert(list) - .subscribeOnIo() - fun saveCountryLanguageJoins(list: List): Completable = + suspend fun saveCountryLanguageJoins(list: List) = languageDao.insert(list) - .subscribeOnIo() - fun saveCountryRegionalBlocJoins(list: List): Completable = + suspend fun saveCountryRegionalBlocJoins(list: List) = regionalBlocDao.insert(list) - .subscribeOnIo() - fun saveCountryTimeZoneJoins(list: List): Completable = + suspend fun saveCountryTimeZoneJoins(list: List) = timeZoneDao.insert(list) - .subscribeOnIo() - fun saveCountryBorderJoins(list: List): Completable = + suspend fun saveCountryBorderJoins(list: List) = borderDao.insert(list) - .subscribeOnIo() - fun saveCountryCurrencyJoin(entity: CountryCurrencyJoin): Completable = + suspend fun saveCountryCurrencyJoin(entity: CountryCurrencyJoin) = currencyDao.insert(entity) - .subscribeOnIo() - .ignoreElement() - fun saveCountryLanguageJoin(entity: CountryLanguageJoin): Completable = + suspend fun saveCountryLanguageJoin(entity: CountryLanguageJoin) = languageDao.insert(entity) - .subscribeOnIo() - .ignoreElement() - fun saveCountryRegionalBlocJoin(entity: CountryRegionalBlocJoin): Completable = + suspend fun saveCountryRegionalBlocJoin(entity: CountryRegionalBlocJoin) = regionalBlocDao.insert(entity) - .subscribeOnIo() - .ignoreElement() - fun saveCountryTimeZoneJoin(entity: CountryTimeZoneJoin): Completable = + suspend fun saveCountryTimeZoneJoin(entity: CountryTimeZoneJoin) = timeZoneDao.insert(entity) - .subscribeOnIo() - .ignoreElement() - fun saveCountryBorderJoin(entity: CountryBorderJoin): Completable = + suspend fun saveCountryBorderJoin(entity: CountryBorderJoin) = borderDao.insert(entity) - .subscribeOnIo() - .ignoreElement() } \ No newline at end of file diff --git a/persistence/src/main/java/pl/valueadd/restcountries/persistence/manager/CurrencyPersistenceManager.kt b/persistence/src/main/java/pl/valueadd/restcountries/persistence/manager/CurrencyPersistenceManager.kt index b7b0cf2..4294ec0 100644 --- a/persistence/src/main/java/pl/valueadd/restcountries/persistence/manager/CurrencyPersistenceManager.kt +++ b/persistence/src/main/java/pl/valueadd/restcountries/persistence/manager/CurrencyPersistenceManager.kt @@ -1,29 +1,23 @@ package pl.valueadd.restcountries.persistence.manager -import io.reactivex.Completable -import io.reactivex.Flowable -import io.reactivex.Single -import io.reactivex.annotations.SchedulerSupport +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged import pl.valueadd.restcountries.persistence.dao.CurrencyDao import pl.valueadd.restcountries.persistence.entity.CurrencyEntity -import pl.valueadd.restcountries.utility.reactivex.subscribeOnIo import javax.inject.Inject import javax.inject.Singleton @Singleton -@SchedulerSupport(value = SchedulerSupport.IO) class CurrencyPersistenceManager @Inject constructor(private val dao: CurrencyDao) { - fun saveCurrencies(list: List): Completable = + suspend fun saveCurrencies(list: List) { dao.insert(list) - .subscribeOnIo() + } - fun saveCurrenciesIds(list: List): Single> = + suspend fun saveCurrenciesIds(list: List): List = dao.insertEntities(list) - .subscribeOnIo() - fun observeCurrencies(countryId: String): Flowable> = + fun observeCurrencies(countryId: String): Flow> = dao.observeCurriences(countryId) .distinctUntilChanged() - .subscribeOnIo() } \ No newline at end of file diff --git a/persistence/src/main/java/pl/valueadd/restcountries/persistence/manager/LanguagePersistenceManager.kt b/persistence/src/main/java/pl/valueadd/restcountries/persistence/manager/LanguagePersistenceManager.kt index d08edd6..1917309 100644 --- a/persistence/src/main/java/pl/valueadd/restcountries/persistence/manager/LanguagePersistenceManager.kt +++ b/persistence/src/main/java/pl/valueadd/restcountries/persistence/manager/LanguagePersistenceManager.kt @@ -1,29 +1,22 @@ package pl.valueadd.restcountries.persistence.manager -import io.reactivex.Completable -import io.reactivex.Flowable -import io.reactivex.Single -import io.reactivex.annotations.SchedulerSupport +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged import pl.valueadd.restcountries.persistence.dao.LanguageDao import pl.valueadd.restcountries.persistence.entity.LanguageEntity -import pl.valueadd.restcountries.utility.reactivex.subscribeOnIo import javax.inject.Inject import javax.inject.Singleton @Singleton -@SchedulerSupport(value = SchedulerSupport.IO) class LanguagePersistenceManager @Inject constructor(private val dao: LanguageDao) { - fun saveLanguages(list: List): Completable = + suspend fun saveLanguages(list: List) = dao.insert(list) - .subscribeOnIo() - fun saveLanguagesIds(list: List): Single> = + suspend fun saveLanguagesIds(list: List): List = dao.insertEntities(list) - .subscribeOnIo() - fun observeLanguages(countryId: String): Flowable> = + fun observeLanguages(countryId: String): Flow> = dao.observeLanguages(countryId) .distinctUntilChanged() - .subscribeOnIo() } \ No newline at end of file diff --git a/persistence/src/main/java/pl/valueadd/restcountries/persistence/manager/RegionalBlocPersistenceManager.kt b/persistence/src/main/java/pl/valueadd/restcountries/persistence/manager/RegionalBlocPersistenceManager.kt index 4ff3177..3abe7a9 100644 --- a/persistence/src/main/java/pl/valueadd/restcountries/persistence/manager/RegionalBlocPersistenceManager.kt +++ b/persistence/src/main/java/pl/valueadd/restcountries/persistence/manager/RegionalBlocPersistenceManager.kt @@ -1,24 +1,19 @@ package pl.valueadd.restcountries.persistence.manager -import io.reactivex.Completable -import io.reactivex.Flowable -import io.reactivex.annotations.SchedulerSupport +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged import pl.valueadd.restcountries.persistence.dao.RegionalBlocDao import pl.valueadd.restcountries.persistence.entity.RegionalBlocEntity -import pl.valueadd.restcountries.utility.reactivex.subscribeOnIo import javax.inject.Inject import javax.inject.Singleton @Singleton -@SchedulerSupport(value = SchedulerSupport.IO) class RegionalBlocPersistenceManager @Inject constructor(private val dao: RegionalBlocDao) { - fun saveRegionalBlocs(list: List): Completable = + suspend fun saveRegionalBlocs(list: List) = dao.insert(list) - .subscribeOnIo() - fun observeRegionalBlocs(countryId: String): Flowable> = + fun observeRegionalBlocs(countryId: String): Flow> = dao.observeRegionalBlocs(countryId) .distinctUntilChanged() - .subscribeOnIo() } \ No newline at end of file diff --git a/persistence/src/main/java/pl/valueadd/restcountries/persistence/manager/TimeZonePersistenceManager.kt b/persistence/src/main/java/pl/valueadd/restcountries/persistence/manager/TimeZonePersistenceManager.kt index 0f69035..4556b63 100644 --- a/persistence/src/main/java/pl/valueadd/restcountries/persistence/manager/TimeZonePersistenceManager.kt +++ b/persistence/src/main/java/pl/valueadd/restcountries/persistence/manager/TimeZonePersistenceManager.kt @@ -1,29 +1,22 @@ package pl.valueadd.restcountries.persistence.manager -import io.reactivex.Completable -import io.reactivex.Flowable -import io.reactivex.Single -import io.reactivex.annotations.SchedulerSupport +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged import pl.valueadd.restcountries.persistence.dao.TimeZoneDao import pl.valueadd.restcountries.persistence.entity.TimeZoneEntity -import pl.valueadd.restcountries.utility.reactivex.subscribeOnIo import javax.inject.Inject import javax.inject.Singleton @Singleton -@SchedulerSupport(value = SchedulerSupport.IO) class TimeZonePersistenceManager @Inject constructor(private val dao: TimeZoneDao) { - fun saveTimezonesIds(list: List): Single> = + suspend fun saveTimezonesIds(list: List): List = dao.insertEntities(list) - .subscribeOnIo() - fun saveTimezones(list: List): Completable = + suspend fun saveTimezones(list: List) = dao.insert(list) - .subscribeOnIo() - fun observeTimeZones(countryId: String): Flowable> = + fun observeTimeZones(countryId: String): Flow> = dao.observeTimeZones(countryId) .distinctUntilChanged() - .subscribeOnIo() } \ No newline at end of file diff --git a/persistence/src/main/java/pl/valueadd/restcountries/persistence/manager/TopLevelDomainPersistenceManager.kt b/persistence/src/main/java/pl/valueadd/restcountries/persistence/manager/TopLevelDomainPersistenceManager.kt index d471d8c..23be166 100644 --- a/persistence/src/main/java/pl/valueadd/restcountries/persistence/manager/TopLevelDomainPersistenceManager.kt +++ b/persistence/src/main/java/pl/valueadd/restcountries/persistence/manager/TopLevelDomainPersistenceManager.kt @@ -1,29 +1,22 @@ package pl.valueadd.restcountries.persistence.manager -import io.reactivex.Completable -import io.reactivex.Flowable -import io.reactivex.Single -import io.reactivex.annotations.SchedulerSupport +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged import pl.valueadd.restcountries.persistence.dao.TopLevelDomainDao import pl.valueadd.restcountries.persistence.entity.TopLevelDomainEntity -import pl.valueadd.restcountries.utility.reactivex.subscribeOnIo import javax.inject.Inject import javax.inject.Singleton @Singleton -@SchedulerSupport(value = SchedulerSupport.IO) class TopLevelDomainPersistenceManager @Inject constructor(private val dao: TopLevelDomainDao) { - fun saveTopLevelDomains(list: List): Completable = + suspend fun saveTopLevelDomains(list: List) = dao.insert(list) - .subscribeOnIo() - fun saveTopLevelDomainsIds(list: List): Single> = + suspend fun saveTopLevelDomainsIds(list: List): List = dao.insertEntities(list) - .subscribeOnIo() - fun observeTopLevelDomains(countryId: String): Flowable> = + fun observeTopLevelDomains(countryId: String): Flow> = dao.observeTopLevelDomains(countryId) .distinctUntilChanged() - .subscribeOnIo() } \ No newline at end of file diff --git a/persistence/src/main/java/pl/valueadd/restcountries/persistence/preferences/BaseCache.kt b/persistence/src/main/java/pl/valueadd/restcountries/persistence/preferences/BaseCache.kt index 1b1d7ed..022c23a 100644 --- a/persistence/src/main/java/pl/valueadd/restcountries/persistence/preferences/BaseCache.kt +++ b/persistence/src/main/java/pl/valueadd/restcountries/persistence/preferences/BaseCache.kt @@ -1,10 +1,9 @@ package pl.valueadd.restcountries.persistence.preferences import android.content.SharedPreferences -import pl.valueadd.restcountries.utility.reactivex.immediate -import io.reactivex.BackpressureStrategy -import io.reactivex.Completable -import io.reactivex.Flowable +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.channelFlow +import kotlinx.coroutines.runBlocking import javax.inject.Inject class BaseCache @Inject constructor(private val sharedPreferences: SharedPreferences) { @@ -58,31 +57,10 @@ class BaseCache @Inject constructor(private val sharedPreferences: SharedPrefere internal fun clear() = executeTransaction { it.clear() } - internal fun clearRx(): Completable = - immediate { clear() } - - internal fun savePreferenceRx(key: String, value: String?): Completable = - immediate { savePreference(key, value) } - - internal fun savePreferenceRx(key: String, valuesList: Set): Completable = - immediate { savePreference(key, valuesList) } - - internal fun savePreferenceRx(key: String, value: Int): Completable = - immediate { savePreference(key, value) } - - internal fun savePreferenceRx(key: String, value: Float): Completable = - immediate { savePreference(key, value) } - - internal fun savePreferenceRx(key: String, value: Long): Completable = - immediate { savePreference(key, value) } - - internal fun savePreferenceRx(key: String, value: Boolean): Completable = - immediate { savePreference(key, value) } - internal fun observeStringSet( key: String, defaultValue: Set = emptySet() - ): Flowable> = + ): Flow> = observePreference(key) { it.getStringSet(key, defaultValue) ?: defaultValue } @@ -90,7 +68,7 @@ class BaseCache @Inject constructor(private val sharedPreferences: SharedPrefere internal fun observeBoolean( key: String, defaultValue: Boolean = DEFAULT_BOOLEAN_VALUE - ): Flowable = + ): Flow = observePreference(key) { it.getBoolean(key, defaultValue) } @@ -98,12 +76,12 @@ class BaseCache @Inject constructor(private val sharedPreferences: SharedPrefere internal fun observeString( key: String, defaultValue: String = DEFAULT_STRING_VALUE - ): Flowable = + ): Flow = observePreference(key) { it.getString(key, defaultValue) ?: defaultValue } - internal fun observeInt(key: String, defaultValue: Int = DEFAULT_INT_VALUE): Flowable = + internal fun observeInt(key: String, defaultValue: Int = DEFAULT_INT_VALUE): Flow = observePreference(key) { it.getInt(key, defaultValue) } @@ -111,7 +89,7 @@ class BaseCache @Inject constructor(private val sharedPreferences: SharedPrefere internal fun observeLong( key: String, defaultValue: Long = DEFAULT_LONG_VALUE - ): Flowable = + ): Flow = observePreference(key) { it.getLong(key, defaultValue) } @@ -119,7 +97,7 @@ class BaseCache @Inject constructor(private val sharedPreferences: SharedPrefere internal fun observeFloat( key: String, defaultValue: Float = DEFAULT_FLOAT_VALUE - ): Flowable = + ): Flow = observePreference(key) { it.getFloat(key, defaultValue) } @@ -127,27 +105,25 @@ class BaseCache @Inject constructor(private val sharedPreferences: SharedPrefere private fun observePreference( preferenceKey: String, function: (SharedPreferences) -> T - ): Flowable { - return Flowable.create({ emitter -> - - val sharedPreferencesListener = + ): Flow = channelFlow { + val sharedPreferencesListener = SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key -> if (key == preferenceKey) { - emitter.onNext(function(sharedPreferences)) + runBlocking { + channel.send(function(sharedPreferences)) + } } } - emitter.setCancellable { + channel.invokeOnClose { sharedPreferences.unregisterOnSharedPreferenceChangeListener( sharedPreferencesListener ) } - - emitter.onNext(function(sharedPreferences)) + channel.send(function(sharedPreferences)) sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferencesListener) - }, BackpressureStrategy.LATEST) } private inline fun executeTransaction(function: (SharedPreferences.Editor) -> Unit) { diff --git a/utility/build.gradle b/utility/build.gradle index ecc137b..2adee10 100644 --- a/utility/build.gradle +++ b/utility/build.gradle @@ -91,19 +91,14 @@ dependencies { api deps.di.toothpick api deps.di.toothpick_smoothie - // Reactivex - api deps.rx.java - api deps.rx.android - api deps.rx.kotlin - // Utility api deps.utility.apache_lang implementation deps.utility.glide implementation deps.utility.android_svg api deps.utility.timber - //Reactivex - implementation deps.rx.binding + // Coroutines + implementation deps.coroutines.core // View implementation deps.androidx.material diff --git a/utility/src/main/java/pl/valueadd/restcountries/utility/common/Transform.kt b/utility/src/main/java/pl/valueadd/restcountries/utility/common/Transform.kt new file mode 100644 index 0000000..0af9bfd --- /dev/null +++ b/utility/src/main/java/pl/valueadd/restcountries/utility/common/Transform.kt @@ -0,0 +1,11 @@ +package pl.valueadd.restcountries.utility.common + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.transform + +/** + * Returns a flow containing the results of applying the given [transform] function to each value of the original flow. + */ +inline fun Flow.map(crossinline transform: (value: T) -> R): Flow = transform { value -> + return@transform emit(transform(value)) +} \ No newline at end of file diff --git a/utility/src/main/java/pl/valueadd/restcountries/utility/coroutines/Coroutines.kt b/utility/src/main/java/pl/valueadd/restcountries/utility/coroutines/Coroutines.kt new file mode 100644 index 0000000..5a97cb1 --- /dev/null +++ b/utility/src/main/java/pl/valueadd/restcountries/utility/coroutines/Coroutines.kt @@ -0,0 +1,61 @@ +package pl.valueadd.restcountries.utility.coroutines + +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.FlowCollector +import kotlinx.coroutines.launch +import kotlin.experimental.ExperimentalTypeInference + +/* + * Works like combineTransform from Zip.kt but collects more parameters. + */ +@Suppress("LongParameterList") +inline fun combineTransform( + flow: Flow, + flow2: Flow, + flow3: Flow, + flow4: Flow, + flow5: Flow, + flow6: Flow, + flow7: Flow, + flow8: Flow, + flow9: Flow, + @UseExperimental(ExperimentalTypeInference::class) + @BuilderInference crossinline transform: suspend FlowCollector.(T1, T2, T3, T4, T5, T6, T7, T8, T9) -> Unit +): Flow = kotlinx.coroutines.flow.combineTransform(flow, flow2, flow3, flow4, flow5, flow6, flow7, flow8, flow9) { args: Array<*> -> + @Suppress("MagicNumber") + transform( + args[0] as T1, + args[1] as T2, + args[2] as T3, + args[3] as T4, + args[4] as T5, + args[5] as T6, + args[6] as T7, + args[7] as T8, + args[8] as T9 + ) +} + +inline fun CoroutineScope.throttleFirst( + skipMs: Long = 400, + crossinline destinationFunction: () -> Unit +): () -> Unit { + var throttleJob: Job? = null + return { + if (throttleJob?.isCompleted != false) { + throttleJob = launch { + destinationFunction() + delay(skipMs) + } + } + } +} + +fun CoroutineScope.cancelSafe(cause: CancellationException? = null) { + val job = coroutineContext[Job] ?: return + job.cancel(cause) +} \ No newline at end of file diff --git a/utility/src/main/java/pl/valueadd/restcountries/utility/reactivex/Reactivex.kt b/utility/src/main/java/pl/valueadd/restcountries/utility/reactivex/Reactivex.kt deleted file mode 100644 index be2a657..0000000 --- a/utility/src/main/java/pl/valueadd/restcountries/utility/reactivex/Reactivex.kt +++ /dev/null @@ -1,85 +0,0 @@ -package pl.valueadd.restcountries.utility.reactivex - -import android.view.View -import com.jakewharton.rxbinding3.view.clicks -import io.reactivex.Completable -import io.reactivex.Flowable -import io.reactivex.Maybe -import io.reactivex.Observable -import io.reactivex.Single -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.disposables.Disposable -import io.reactivex.rxkotlin.addTo -import java.util.concurrent.TimeUnit - -/** - * Throttle clicks on view with given duration. - */ -fun View.throttleClicks(interval: Long = 500L): Observable = - this.clicks() - .throttleFirst(interval, TimeUnit.MILLISECONDS) - -/** - * Calls a given lambda on thread from which caller method has been invoked. - */ -fun immediate(action: () -> Unit): Completable = - Completable - .fromAction { action() } - -/** - * Calls a given lambda on thread from which caller method has been invoked. - */ -fun immediateSingle(action: () -> T): Single = - Single.fromCallable { action() } - -/** - * Immediately subscribes stream and adds to disposables. - * - * Errors are silenced by default. - */ -fun Observable.onSuccess( - composite: CompositeDisposable, - action: (T) -> Unit, - onError: (Throwable) -> Unit = { /* Muted */ } -): Disposable = - this.subscribe(action, onError) - .addTo(composite) - -/** - * Immediately subscribes stream and adds to disposables. - * - * Errors are silenced by default. - */ -fun Flowable.onSuccess( - composite: CompositeDisposable, - action: (T) -> Unit, - onError: (Throwable) -> Unit = { /* Muted */ } -): Disposable = - this.subscribe(action, onError) - .addTo(composite) - -/** - * Immediately subscribes stream and adds to disposables. - * - * Errors are silenced by default. - */ -fun Completable.onSuccess( - composite: CompositeDisposable, - action: () -> Unit, - onError: (Throwable) -> Unit = { /* Muted */ } -): Disposable = - this.subscribe(action, onError) - .addTo(composite) - -/** - * Immediately subscribes stream and adds to disposables. - * - * Errors are silenced by default. - */ -fun Maybe.onSuccess( - composite: CompositeDisposable, - action: (T) -> Unit, - onError: (Throwable) -> Unit = { /* Muted */ } -): Disposable = - this.subscribe(action, onError) - .addTo(composite) \ No newline at end of file diff --git a/utility/src/main/java/pl/valueadd/restcountries/utility/reactivex/Schedulers.kt b/utility/src/main/java/pl/valueadd/restcountries/utility/reactivex/Schedulers.kt deleted file mode 100644 index 722f95f..0000000 --- a/utility/src/main/java/pl/valueadd/restcountries/utility/reactivex/Schedulers.kt +++ /dev/null @@ -1,114 +0,0 @@ -package pl.valueadd.restcountries.utility.reactivex - -import io.reactivex.Completable -import io.reactivex.Flowable -import io.reactivex.Maybe -import io.reactivex.Observable -import io.reactivex.Single -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.schedulers.Schedulers - -/** - * Contains all common subscription calls. - * Do not extend with additional calls. - */ - -/** - * Shorter call: - * - * ```.subscribeOn(Schedulers.io())``` - */ -fun Flowable.subscribeOnIo(): Flowable = - this.subscribeOn(Schedulers.io()) - -/** - * Shorter call: - * - * ```.subscribeOn(Schedulers.io())``` - */ -fun Observable.subscribeOnIo(): Observable = - this.subscribeOn(Schedulers.io()) - -/** - * Shorter call: - * - * ```.subscribeOn(Schedulers.io())``` - */ -fun Single.subscribeOnIo(): Single = - this.subscribeOn(Schedulers.io()) - -/** - * Shorter call: - * - * ```.subscribeOn(Schedulers.io())``` - */ -fun Maybe.subscribeOnIo(): Maybe = - this.subscribeOn(Schedulers.io()) - -/** - * Shorter call: - * - * ```.subscribeOn(Schedulers.io())``` - */ -fun Completable.subscribeOnIo(): Completable = - this.subscribeOn(Schedulers.io()) - -/** - * Shorter call: - * - * ``` - * .subscribeOn(Schedulers.io()) - * .observeOn(AndroidSchedulers.mainThread()) - * ``` - */ -fun Flowable.observeOnMain(): Flowable = - this.subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - -/** - * Shorter call: - * - * ``` - * .subscribeOn(Schedulers.io()) - * .observeOn(AndroidSchedulers.mainThread()) - * ``` - */ -fun Observable.observeOnMain(): Observable = - this.subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - -/** - * Shorter call: - * - * ``` - * .subscribeOn(Schedulers.io()) - * .observeOn(AndroidSchedulers.mainThread()) - * ``` - */ -fun Single.observeOnMain(): Single = - this.subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - -/** - * Shorter call: - * - * ``` - * .subscribeOn(Schedulers.io()) - * .observeOn(AndroidSchedulers.mainThread()) - * ``` - */ -fun Maybe.observeOnMain(): Maybe = - this.subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - -/** - * Shorter call: - * - * ``` - * .subscribeOn(Schedulers.io()) - * .observeOn(AndroidSchedulers.mainThread()) - * ``` - */ -fun Completable.observeOnMain(): Completable = - this.subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) \ No newline at end of file diff --git a/utility/src/main/java/pl/valueadd/restcountries/utility/view/View.kt b/utility/src/main/java/pl/valueadd/restcountries/utility/view/View.kt index 08028d5..a29cb47 100644 --- a/utility/src/main/java/pl/valueadd/restcountries/utility/view/View.kt +++ b/utility/src/main/java/pl/valueadd/restcountries/utility/view/View.kt @@ -10,6 +10,8 @@ import androidx.annotation.ColorInt import androidx.annotation.IdRes import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.Toolbar +import kotlinx.coroutines.CoroutineScope +import pl.valueadd.restcountries.utility.coroutines.throttleFirst fun View.setVisible(isVisible: Boolean, falseRes: Int = View.GONE) { @@ -38,4 +40,15 @@ fun Menu.show(@IdRes idRes: Int) { fun SearchView.applyAligmentToTheRight() { layoutParams = Toolbar.LayoutParams(Gravity.END) +} + +fun View.setOnClickListener(listener: () -> Unit) { + setOnClickListener { listener() } +} + +inline fun View.throttleClicks(scope: CoroutineScope, crossinline onClick: () -> Unit) { + val onClickListener = scope.throttleFirst { + onClick() + } + setOnClickListener(onClickListener) } \ No newline at end of file