blob: 479e76569e278ac1770eedb044b96164310f3378 [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.*
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
}
try {
val response = when (mode) {
LottiefilesMode.Popular -> api.getPopular(1)
LottiefilesMode.Recent -> api.getRecent(1)
LottiefilesMode.Search -> api.search(query, 1)
}.blockingGet()
callback.onResult(
response.data,
0,
response.total,
null,
2.takeIf {
!response.nextPageUrl.isNullOrEmpty()
}
)
} catch (e: Exception) {
callback.onError(e)
}
}
override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, AnimationData>) {
loadPage(params.key, callback)
}
override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<Int, AnimationData>) {
// ignored, prepend will never happen.
}
private fun loadPage(page: Int, callback: LoadCallback<Int, AnimationData>) {
try {
val response = when (mode) {
LottiefilesMode.Popular -> api.getPopular(page)
LottiefilesMode.Recent -> api.getRecent(page)
LottiefilesMode.Search -> api.search(query, page)
}.blockingGet()
callback.onResult(
response.data,
(page + 1).takeIf {
!response.nextPageUrl.isNullOrEmpty()
}
)
} catch (e: Exception) {
callback.onError(e)
}
}
}
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
}
}