blob: 763d74f6e3ae2d5746eba234d6f9b6f2857098b7 [file] [log] [blame]
package com.airbnb.lottie.samples
import android.os.Bundle
import android.view.View
import androidx.lifecycle.Observer
import androidx.paging.DataSource
import androidx.paging.LivePagedListBuilder
import androidx.paging.PageKeyedDataSource
import com.airbnb.epoxy.EpoxyModel
import com.airbnb.epoxy.paging.PagedListEpoxyController
import com.airbnb.lottie.samples.model.AnimationData
import com.airbnb.lottie.samples.model.CompositionArgs
import com.airbnb.lottie.samples.views.AnimationItemViewModel_
import com.airbnb.lottie.samples.views.lottiefilesTabBar
import com.airbnb.lottie.samples.views.marquee
import com.airbnb.lottie.samples.views.searchInputItemView
import com.airbnb.mvrx.BaseMvRxFragment
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import io.reactivex.schedulers.Schedulers
import kotlinx.android.synthetic.main.fragment_epoxy_recycler_view.*
import kotlin.properties.Delegates
data class LottiefilesState(
val mode: LottiefilesMode = LottiefilesMode.Recent,
val query: String = ""
) : MvRxState
class LottiefilesViewModel(initialState: LottiefilesState, private val api: LottiefilesApi) : MvRxViewModel<LottiefilesState>(initialState) {
private var mode = initialState.mode
private var query = initialState.query
val pagedList = LivePagedListBuilder<Int, AnimationData>(object : DataSource.Factory<Int, AnimationData>() {
override fun create(): DataSource<Int, AnimationData> {
return LottiefilesDataSource(api, mode, query)
}
}, 16).build()
init {
selectSubscribe(LottiefilesState::mode, LottiefilesState::query) { mode, query ->
this.mode = mode
this.query = query
pagedList.value?.dataSource?.invalidate()
}
}
fun setMode(mode: LottiefilesMode) = setState { copy(mode = mode) }
fun setQuery(query: String) = setState { copy(query = query) }
companion object : MvRxViewModelFactory<LottiefilesViewModel, LottiefilesState> {
override fun create(viewModelContext: ViewModelContext, state: LottiefilesState): LottiefilesViewModel? {
val service = viewModelContext.app<LottieApplication>().lottiefilesService
return LottiefilesViewModel(state, service)
}
}
}
class LottiefilesDataSource(
private val api: LottiefilesApi,
val mode: LottiefilesMode,
val query: String
) : PageKeyedDataSource<Int, AnimationData>() {
override fun loadInitial(params: LoadInitialParams<Int>, callback: LoadInitialCallback<Int, AnimationData>) {
if (mode == LottiefilesMode.Search && query.isEmpty()) {
callback.onResult(emptyList(), null, null)
return
}
when (mode) {
LottiefilesMode.Popular -> api.getPopular(1)
LottiefilesMode.Recent -> api.getRecent(1)
LottiefilesMode.Search -> api.search(query, 1)
}
.subscribeOn(Schedulers.io())
.subscribe(
{ d -> callback.onResult(d.data, 0, d.total, null, 2.takeIf { d.data.isNotEmpty() }) },
{ callback.onError(it) }
)
}
override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, AnimationData>) {
loadPage(params.key, callback)
}
override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<Int, AnimationData>) {
loadPage(params.key, callback)
}
private fun loadPage(page: Int, callback: LoadCallback<Int, AnimationData>) {
when (mode) {
LottiefilesMode.Popular -> api.getPopular(page)
LottiefilesMode.Recent -> api.getRecent(page)
LottiefilesMode.Search -> api.search(query, page)
}
.subscribeOn(Schedulers.io())
.subscribe(
{ callback.onResult(it.data, page + 1) },
{ callback.onError(it) }
)
}
}
class LottiefilesFragment : BaseMvRxFragment(R.layout.fragment_epoxy_recycler_view) {
private val viewModel: LottiefilesViewModel by fragmentViewModel()
private val controller by lazy {
object : PagedListEpoxyController<AnimationData>() {
var mode by Delegates.observable(LottiefilesMode.Recent) { _, _, _ -> requestModelBuild() }
override fun buildItemModel(currentPosition: Int, item: AnimationData?): EpoxyModel<*> {
return if (item == null) {
AnimationItemViewModel_().id(-currentPosition)
} else {
AnimationItemViewModel_()
.id(item.id)
.previewUrl(item.preview)
.title(item.title)
.previewBackgroundColor(item.bgColorInt)
.onClickListener { _ ->
val intent = PlayerActivity.intent(requireContext(), CompositionArgs(animationData = item))
requireContext().startActivity(intent)
}
}
}
override fun addModels(models: List<EpoxyModel<*>>) {
marquee {
id("lottiefiles")
title(R.string.lottiefiles)
subtitle(R.string.lottiefiles_airbnb)
}
lottiefilesTabBar {
id("mode")
mode(mode)
recentClickListener { _ ->
viewModel.setMode(LottiefilesMode.Recent)
requireContext().hideKeyboard()
}
popularClickListener { _ ->
viewModel.setMode(LottiefilesMode.Popular)
requireContext().hideKeyboard()
}
searchClickListener { _ ->
viewModel.setMode(LottiefilesMode.Search)
requireContext().hideKeyboard()
}
}
if (mode == LottiefilesMode.Search) {
searchInputItemView {
id("search")
searchClickListener(viewModel::setQuery)
}
}
super.addModels(models)
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.pagedList.observe(this, Observer {
controller.submitList(it)
})
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
recyclerView.setController(controller)
}
override fun invalidate(): Unit = withState(viewModel) { state ->
controller.mode = state.mode
}
}