Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package com.alphaott.webtv.client.android.data
- import com.alphaott.webtv.client.android.injection.SingletonHolderSingleArg
- import com.alphaott.webtv.client.api.entities.contentgroup.category.Category
- import com.alphaott.webtv.client.api.entities.contentgroup.genre.Genre
- import com.alphaott.webtv.client.api.entities.contentitem.MediaStream
- import com.alphaott.webtv.client.api.entities.contentitem.channel.tv.TvChannel
- import com.alphaott.webtv.client.api.entities.dictionary.country.Country
- import com.alphaott.webtv.client.api.entities.dictionary.language.Language
- import io.reactivex.Observable
- import io.reactivex.rxkotlin.Observables
- import io.reactivex.rxkotlin.Singles
- import io.reactivex.schedulers.Schedulers
- import io.reactivex.subjects.BehaviorSubject
- val showedLatestChannels: Long = 10
- val showedPopularChannels: Long = 10
- //TODO можно напрямую передавать content path или contentPath observable
- class TvRepository(
- private val configRepository: ConfigRepository
- ) {
- data class ChannelSources(
- val streams: Set<MediaStream>?,
- val error: Boolean
- )
- data class Dictionaries(
- var categories: List<Category>,
- val genres: List<Genre>,
- val languages: List<Language>,
- val countries: List<Country>
- )
- // TODO оставил для не отрефаченых методов
- private val contentPath: String = configRepository.config.content.url
- private val configObservable = configRepository.configObservable()
- //channels
- private val channelsBehaviour: BehaviorSubject<List<TvChannel>> = BehaviorSubject.create<List<TvChannel>>()
- private val channelsBehaviourError: BehaviorSubject<Throwable> = BehaviorSubject.create<Throwable>()
- private val channelsSourcesBehaviour: BehaviorSubject<MutableMap<String, ChannelSources>> = BehaviorSubject.createDefault(mutableMapOf())
- //dictionaries
- private val categoriesBehaviour: BehaviorSubject<List<Category>> = BehaviorSubject.create<List<Category>>()
- private val genresBehaviour: BehaviorSubject<List<Genre>> = BehaviorSubject.create<List<Genre>>()
- private val languagesBehaviour: BehaviorSubject<List<Language>> = BehaviorSubject.create<List<Language>>()
- private val countriesBehaviour: BehaviorSubject<List<Country>> = BehaviorSubject.create<List<Country>>()
- private val loadDictionariesErrorBehaviour: BehaviorSubject<Boolean> = BehaviorSubject.create<Boolean>()
- //computed
- private val computedChannelsObservable: Observable<List<TvChannel>>
- private val popularChannelsObservable: Observable<List<TvChannel>>
- private val latestChannelsObservable: Observable<List<TvChannel>>
- private val computedCategoriesObservable: Observable<List<Category>>
- private val computedGenresObservable: Observable<List<Genre>>
- private val computedCountriesObservable: Observable<List<Country>>
- private val computedLanguageObservable: Observable<List<Language>>
- init {
- computedChannelsObservable = Observables.combineLatest(
- channelsBehaviour,
- configObservable
- ).flatMap { pair ->
- Observable.fromIterable(pair.first)
- .map {
- //TODO это должно собираться где то на уровне view model
- it.logos.forEach { it.path = "${pair.second.content.url}${it.path}" }
- it.backgrounds.forEach { it.path = "${pair.second.content.url}${it.path}" }
- it
- }
- .toList().toObservable()
- }.cache()
- computedCategoriesObservable = Observables.combineLatest(
- categoriesBehaviour,
- configObservable,
- { categories, config ->
- val contentPath = config.content.url
- categories.apply {
- forEach {
- it.backgrounds.forEach { it.path = "$contentPath${it.path}" }
- it.posters.forEach { it.path = "$contentPath${it.path}" }
- }
- }
- }
- ).cache()
- computedGenresObservable = Observables.combineLatest(
- genresBehaviour,
- configObservable
- ).flatMap { (genres, config) ->
- Observable.fromIterable(genres)
- .map {
- val contentPath = config.content.url
- it.posters.forEach { it.path = "$contentPath${it.path}" }
- it.backgrounds.forEach { it.path = "$contentPath${it.path}" }
- it
- }
- .toList()
- .toObservable()
- }.cache()
- computedLanguageObservable = Observables.combineLatest(
- languagesBehaviour,
- configObservable
- ).flatMap { pair ->
- Observable.fromIterable(pair.first)
- .map {
- //TODO это должно собираться где то на уровне view model
- it.images.forEach { it.path = "${pair.second.content.url}${it.path}" }
- it
- }
- .toList()
- .toObservable()
- }.cache()
- computedCountriesObservable = Observables.combineLatest(
- countriesBehaviour,
- configObservable
- ).flatMap { pair ->
- Observable.fromIterable(pair.first)
- .map {
- //TODO это должно собираться где то на уровне view model
- it.images.forEach { it.path = "${pair.second.content.url}${it.path}" }
- it
- }
- .toList()
- .toObservable()
- }.cache()
- popularChannelsObservable = computedChannelsObservable.flatMap { channels ->
- Observable.fromIterable(channels)
- .sorted { a, b -> (b.rating - a.rating).toInt() }
- .take(showedPopularChannels)
- .toList()
- .toObservable()
- }.cache()
- latestChannelsObservable = computedChannelsObservable.flatMap { channels ->
- Observable.fromIterable(channels)
- .sorted { a, b ->
- //TODO блин я не могу без tmp сравнивать иначе concurrent, проверка не верная если чего то нет, то то что есть больше
- val aTime = a.created?.time
- val bTime = b.created?.time
- if (aTime != null && bTime != null) (aTime - bTime).toInt() else 0
- }
- .take(showedLatestChannels)
- .toList()
- .toObservable()
- }.cache()
- //TODO нужно добавить логику что пока все не заэмитят данные хотя бы по 1 му разу не обновлять ui
- }
- //TODO есть очень много комбинаций как тут делать. Можно комбинировать из
- //TODO 1) возвращать Completable 2) Возвращать Disposable, 3) Сохранить результат выполнение операции в repository
- //TODO один из вариантов - сохранить данные о процессе выполнения в репозитории, вернуть Completable что бы был
- //TODO явный callback и позволять прекращать загрузку через stopLoadChannels ( сохранив disposable )
- fun loadChannels() {
- //TODO customer repository should be observable
- //я в сомнениях что лучше single в api или observable
- ApiClient.publicApi.getTvChannels(CustomerRepository.customer.serviceSpec)
- .subscribeOn(Schedulers.io())
- .observeOn(Schedulers.computation())
- .subscribe({ channels -> channelsBehaviour.onNext(channels) }, { error -> channelsBehaviourError.onNext(error) })
- }
- fun loadDictionaries() {
- //Блин не знаю как их загрузить, затем померджить
- loadDictionariesErrorBehaviour.onNext(false)
- ApiClient.publicApi.getTvCategories(CustomerRepository.customer.serviceSpec)
- .subscribeOn(Schedulers.io())
- .observeOn(Schedulers.computation())
- .subscribe({ categories -> categoriesBehaviour.onNext(categories) }, { error -> loadDictionariesErrorBehaviour.onNext(true) })
- //TODO а они действительно идут параллельно? не нужно юзать flat map?
- //TODO мне кажется что зип кидает error того кто первый кинет ошибку, а не позволяет обработать ошибки
- //TODO или получить массив ответов
- Singles.zip(
- ApiClient.publicApi.getTvCategories(CustomerRepository.customer.serviceSpec).subscribeOn(Schedulers.io()),
- ApiClient.publicApi.getTvGenres(CustomerRepository.customer.serviceSpec).subscribeOn(Schedulers.io()),
- ApiClient.publicApi.getTvLanguages(CustomerRepository.customer.serviceSpec).subscribeOn(Schedulers.io()),
- ApiClient.publicApi.getTvCountries(CustomerRepository.customer.serviceSpec).subscribeOn(Schedulers.io()),
- { categories, genres, languages, countries -> Dictionaries(categories, genres, languages, countries) }
- )
- .subscribeOn(Schedulers.io())
- .doOnError({ _ -> loadDictionariesErrorBehaviour.onNext(true) })
- .doOnSuccess { dictionaries ->
- categoriesBehaviour.onNext(dictionaries.categories)
- genresBehaviour.onNext(dictionaries.genres)
- languagesBehaviour.onNext(dictionaries.languages)
- countriesBehaviour.onNext(dictionaries.countries)
- }
- .subscribe()
- }
- fun loadChannelSources(channelId: String) {
- ApiClient.privateApi.getTvSourcesById(channelId)
- .subscribeOn(Schedulers.io())
- .observeOn(Schedulers.computation())
- .subscribe(
- { channels ->
- channelsSourcesBehaviour.value[channelId] = ChannelSources(channels.first().sources, false)
- channelsSourcesBehaviour.onNext(channelsSourcesBehaviour.value)
- },
- { _ ->
- channelsSourcesBehaviour.value[channelId] = ChannelSources(null, true)
- channelsSourcesBehaviour.onNext(channelsSourcesBehaviour.value)
- }
- )
- }
- fun channelsSourcesObservable(): Observable<Map<String, ChannelSources>> {
- return channelsSourcesBehaviour.map { it -> it.toMap()}
- }
- //TODO Мне кажется что тут можно поиграть и использоваться трансформаторы - что бы репозиторий эмитил только необработанные данные
- //TODO а наши view, могли его переиспользовать
- //TODO я правильно использую sorted?
- //TODO почему когда я делал pair приходилось делать not null проверки?
- fun channelsObservable(): Observable<List<TvChannel>> {
- return computedChannelsObservable
- }
- fun latestChannelsObservable(): Observable<List<TvChannel>> {
- return latestChannelsObservable
- }
- fun popularChannelsObservable(): Observable<List<TvChannel>> {
- return popularChannelsObservable
- }
- fun categoriesObservable(): Observable<List<Category>> {
- return computedCategoriesObservable
- }
- fun genresObservable(): Observable<List<Genre>> {
- return computedGenresObservable
- }
- fun languagesObservable(): Observable<List<Language>> {
- return computedLanguageObservable
- }
- fun countriesObservable(): Observable<List<Country>> {
- return computedCountriesObservable
- }
- companion object : SingletonHolderSingleArg<TvRepository, ConfigRepository>(::TvRepository)
- }
Add Comment
Please, Sign In to add comment