Guest User

Untitled

a guest
May 24th, 2018
106
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.36 KB | None | 0 0
  1. package com.alphaott.webtv.client.android.data
  2.  
  3. import com.alphaott.webtv.client.android.injection.SingletonHolderSingleArg
  4. import com.alphaott.webtv.client.api.entities.contentgroup.category.Category
  5. import com.alphaott.webtv.client.api.entities.contentgroup.genre.Genre
  6. import com.alphaott.webtv.client.api.entities.contentitem.MediaStream
  7. import com.alphaott.webtv.client.api.entities.contentitem.channel.tv.TvChannel
  8. import com.alphaott.webtv.client.api.entities.dictionary.country.Country
  9. import com.alphaott.webtv.client.api.entities.dictionary.language.Language
  10. import io.reactivex.Observable
  11. import io.reactivex.rxkotlin.Observables
  12. import io.reactivex.rxkotlin.Singles
  13. import io.reactivex.schedulers.Schedulers
  14. import io.reactivex.subjects.BehaviorSubject
  15.  
  16. val showedLatestChannels: Long = 10
  17. val showedPopularChannels: Long = 10
  18.  
  19. //TODO можно напрямую передавать content path или contentPath observable
  20. class TvRepository(
  21. private val configRepository: ConfigRepository
  22. ) {
  23. data class ChannelSources(
  24. val streams: Set<MediaStream>?,
  25. val error: Boolean
  26. )
  27.  
  28. data class Dictionaries(
  29. var categories: List<Category>,
  30. val genres: List<Genre>,
  31. val languages: List<Language>,
  32. val countries: List<Country>
  33. )
  34.  
  35. // TODO оставил для не отрефаченых методов
  36. private val contentPath: String = configRepository.config.content.url
  37.  
  38. private val configObservable = configRepository.configObservable()
  39.  
  40. //channels
  41. private val channelsBehaviour: BehaviorSubject<List<TvChannel>> = BehaviorSubject.create<List<TvChannel>>()
  42. private val channelsBehaviourError: BehaviorSubject<Throwable> = BehaviorSubject.create<Throwable>()
  43. private val channelsSourcesBehaviour: BehaviorSubject<MutableMap<String, ChannelSources>> = BehaviorSubject.createDefault(mutableMapOf())
  44.  
  45. //dictionaries
  46. private val categoriesBehaviour: BehaviorSubject<List<Category>> = BehaviorSubject.create<List<Category>>()
  47. private val genresBehaviour: BehaviorSubject<List<Genre>> = BehaviorSubject.create<List<Genre>>()
  48. private val languagesBehaviour: BehaviorSubject<List<Language>> = BehaviorSubject.create<List<Language>>()
  49. private val countriesBehaviour: BehaviorSubject<List<Country>> = BehaviorSubject.create<List<Country>>()
  50. private val loadDictionariesErrorBehaviour: BehaviorSubject<Boolean> = BehaviorSubject.create<Boolean>()
  51.  
  52. //computed
  53. private val computedChannelsObservable: Observable<List<TvChannel>>
  54. private val popularChannelsObservable: Observable<List<TvChannel>>
  55. private val latestChannelsObservable: Observable<List<TvChannel>>
  56. private val computedCategoriesObservable: Observable<List<Category>>
  57. private val computedGenresObservable: Observable<List<Genre>>
  58. private val computedCountriesObservable: Observable<List<Country>>
  59. private val computedLanguageObservable: Observable<List<Language>>
  60.  
  61. init {
  62. computedChannelsObservable = Observables.combineLatest(
  63. channelsBehaviour,
  64. configObservable
  65. ).flatMap { pair ->
  66. Observable.fromIterable(pair.first)
  67. .map {
  68. //TODO это должно собираться где то на уровне view model
  69. it.logos.forEach { it.path = "${pair.second.content.url}${it.path}" }
  70. it.backgrounds.forEach { it.path = "${pair.second.content.url}${it.path}" }
  71. it
  72. }
  73. .toList().toObservable()
  74. }.cache()
  75.  
  76. computedCategoriesObservable = Observables.combineLatest(
  77. categoriesBehaviour,
  78. configObservable,
  79. { categories, config ->
  80. val contentPath = config.content.url
  81. categories.apply {
  82. forEach {
  83. it.backgrounds.forEach { it.path = "$contentPath${it.path}" }
  84. it.posters.forEach { it.path = "$contentPath${it.path}" }
  85. }
  86. }
  87. }
  88. ).cache()
  89.  
  90. computedGenresObservable = Observables.combineLatest(
  91. genresBehaviour,
  92. configObservable
  93. ).flatMap { (genres, config) ->
  94. Observable.fromIterable(genres)
  95. .map {
  96. val contentPath = config.content.url
  97. it.posters.forEach { it.path = "$contentPath${it.path}" }
  98. it.backgrounds.forEach { it.path = "$contentPath${it.path}" }
  99. it
  100. }
  101. .toList()
  102. .toObservable()
  103. }.cache()
  104.  
  105. computedLanguageObservable = Observables.combineLatest(
  106. languagesBehaviour,
  107. configObservable
  108. ).flatMap { pair ->
  109. Observable.fromIterable(pair.first)
  110. .map {
  111. //TODO это должно собираться где то на уровне view model
  112. it.images.forEach { it.path = "${pair.second.content.url}${it.path}" }
  113. it
  114. }
  115. .toList()
  116. .toObservable()
  117. }.cache()
  118.  
  119. computedCountriesObservable = Observables.combineLatest(
  120. countriesBehaviour,
  121. configObservable
  122. ).flatMap { pair ->
  123. Observable.fromIterable(pair.first)
  124. .map {
  125. //TODO это должно собираться где то на уровне view model
  126. it.images.forEach { it.path = "${pair.second.content.url}${it.path}" }
  127. it
  128. }
  129. .toList()
  130. .toObservable()
  131. }.cache()
  132.  
  133. popularChannelsObservable = computedChannelsObservable.flatMap { channels ->
  134. Observable.fromIterable(channels)
  135. .sorted { a, b -> (b.rating - a.rating).toInt() }
  136. .take(showedPopularChannels)
  137. .toList()
  138. .toObservable()
  139. }.cache()
  140.  
  141. latestChannelsObservable = computedChannelsObservable.flatMap { channels ->
  142. Observable.fromIterable(channels)
  143. .sorted { a, b ->
  144. //TODO блин я не могу без tmp сравнивать иначе concurrent, проверка не верная если чего то нет, то то что есть больше
  145. val aTime = a.created?.time
  146. val bTime = b.created?.time
  147. if (aTime != null && bTime != null) (aTime - bTime).toInt() else 0
  148. }
  149. .take(showedLatestChannels)
  150. .toList()
  151. .toObservable()
  152. }.cache()
  153. //TODO нужно добавить логику что пока все не заэмитят данные хотя бы по 1 му разу не обновлять ui
  154. }
  155.  
  156. //TODO есть очень много комбинаций как тут делать. Можно комбинировать из
  157. //TODO 1) возвращать Completable 2) Возвращать Disposable, 3) Сохранить результат выполнение операции в repository
  158. //TODO один из вариантов - сохранить данные о процессе выполнения в репозитории, вернуть Completable что бы был
  159. //TODO явный callback и позволять прекращать загрузку через stopLoadChannels ( сохранив disposable )
  160. fun loadChannels() {
  161. //TODO customer repository should be observable
  162.  
  163. //я в сомнениях что лучше single в api или observable
  164. ApiClient.publicApi.getTvChannels(CustomerRepository.customer.serviceSpec)
  165. .subscribeOn(Schedulers.io())
  166. .observeOn(Schedulers.computation())
  167. .subscribe({ channels -> channelsBehaviour.onNext(channels) }, { error -> channelsBehaviourError.onNext(error) })
  168. }
  169.  
  170. fun loadDictionaries() {
  171. //Блин не знаю как их загрузить, затем померджить
  172. loadDictionariesErrorBehaviour.onNext(false)
  173.  
  174. ApiClient.publicApi.getTvCategories(CustomerRepository.customer.serviceSpec)
  175. .subscribeOn(Schedulers.io())
  176. .observeOn(Schedulers.computation())
  177. .subscribe({ categories -> categoriesBehaviour.onNext(categories) }, { error -> loadDictionariesErrorBehaviour.onNext(true) })
  178.  
  179. //TODO а они действительно идут параллельно? не нужно юзать flat map?
  180. //TODO мне кажется что зип кидает error того кто первый кинет ошибку, а не позволяет обработать ошибки
  181. //TODO или получить массив ответов
  182. Singles.zip(
  183. ApiClient.publicApi.getTvCategories(CustomerRepository.customer.serviceSpec).subscribeOn(Schedulers.io()),
  184. ApiClient.publicApi.getTvGenres(CustomerRepository.customer.serviceSpec).subscribeOn(Schedulers.io()),
  185. ApiClient.publicApi.getTvLanguages(CustomerRepository.customer.serviceSpec).subscribeOn(Schedulers.io()),
  186. ApiClient.publicApi.getTvCountries(CustomerRepository.customer.serviceSpec).subscribeOn(Schedulers.io()),
  187. { categories, genres, languages, countries -> Dictionaries(categories, genres, languages, countries) }
  188. )
  189. .subscribeOn(Schedulers.io())
  190. .doOnError({ _ -> loadDictionariesErrorBehaviour.onNext(true) })
  191. .doOnSuccess { dictionaries ->
  192. categoriesBehaviour.onNext(dictionaries.categories)
  193. genresBehaviour.onNext(dictionaries.genres)
  194. languagesBehaviour.onNext(dictionaries.languages)
  195. countriesBehaviour.onNext(dictionaries.countries)
  196. }
  197. .subscribe()
  198. }
  199.  
  200. fun loadChannelSources(channelId: String) {
  201. ApiClient.privateApi.getTvSourcesById(channelId)
  202. .subscribeOn(Schedulers.io())
  203. .observeOn(Schedulers.computation())
  204. .subscribe(
  205. { channels ->
  206. channelsSourcesBehaviour.value[channelId] = ChannelSources(channels.first().sources, false)
  207. channelsSourcesBehaviour.onNext(channelsSourcesBehaviour.value)
  208. },
  209. { _ ->
  210. channelsSourcesBehaviour.value[channelId] = ChannelSources(null, true)
  211. channelsSourcesBehaviour.onNext(channelsSourcesBehaviour.value)
  212. }
  213. )
  214. }
  215.  
  216. fun channelsSourcesObservable(): Observable<Map<String, ChannelSources>> {
  217. return channelsSourcesBehaviour.map { it -> it.toMap()}
  218. }
  219.  
  220.  
  221. //TODO Мне кажется что тут можно поиграть и использоваться трансформаторы - что бы репозиторий эмитил только необработанные данные
  222. //TODO а наши view, могли его переиспользовать
  223. //TODO я правильно использую sorted?
  224.  
  225. //TODO почему когда я делал pair приходилось делать not null проверки?
  226. fun channelsObservable(): Observable<List<TvChannel>> {
  227. return computedChannelsObservable
  228. }
  229.  
  230. fun latestChannelsObservable(): Observable<List<TvChannel>> {
  231. return latestChannelsObservable
  232. }
  233.  
  234. fun popularChannelsObservable(): Observable<List<TvChannel>> {
  235. return popularChannelsObservable
  236. }
  237.  
  238. fun categoriesObservable(): Observable<List<Category>> {
  239. return computedCategoriesObservable
  240. }
  241.  
  242. fun genresObservable(): Observable<List<Genre>> {
  243. return computedGenresObservable
  244. }
  245.  
  246. fun languagesObservable(): Observable<List<Language>> {
  247. return computedLanguageObservable
  248. }
  249.  
  250. fun countriesObservable(): Observable<List<Country>> {
  251. return computedCountriesObservable
  252. }
  253.  
  254. companion object : SingletonHolderSingleArg<TvRepository, ConfigRepository>(::TvRepository)
  255. }
Add Comment
Please, Sign In to add comment