Updated LottieSample libraries and Lottiefiles API (#1522)

Also did a bit of cleanup and converted LottiefilesFragment to epoxy-paging.
diff --git a/LottieSample/build.gradle b/LottieSample/build.gradle
index f797e29..7447eb2 100644
--- a/LottieSample/build.gradle
+++ b/LottieSample/build.gradle
@@ -12,7 +12,7 @@
     applicationId "com.airbnb.lottie"
     minSdkVersion 16
     targetSdkVersion 29
-    versionCode 67
+    versionCode 69
     versionName VERSION_NAME
     multiDexEnabled true
     testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -62,25 +62,29 @@
   implementation project(':lottie')
   implementation 'androidx.multidex:multidex:2.0.1'
 
-  implementation "androidx.fragment:fragment:1.2.0-rc04"
-  implementation("androidx.appcompat:appcompat:1.1.0")
-  implementation "androidx.recyclerview:recyclerview:1.0.0"
+  implementation "androidx.fragment:fragment:1.2.1"
+  implementation "androidx.appcompat:appcompat:1.1.0"
+  implementation "androidx.recyclerview:recyclerview:1.1.0"
+  implementation "androidx.paging:paging-runtime-ktx:2.1.1"
+  implementation "androidx.paging:paging-rxjava2-ktx:2.1.1"
   implementation "androidx.cardview:cardview:1.0.0"
-  implementation 'androidx.core:core-ktx:1.2.0-rc01'
-  implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta1'
-  implementation "androidx.browser:browser:1.0.0"
-  implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0-beta01'
-  kapt "androidx.lifecycle:lifecycle-common-java8:2.1.0-beta01"
-  implementation "com.google.android.material:material:1.0.0"
+  implementation 'androidx.core:core-ktx:1.2.0'
+  implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta4'
+  implementation "androidx.browser:browser:1.2.0"
+  implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
+  kapt "androidx.lifecycle:lifecycle-common-java8:2.2.0"
+  implementation "com.google.android.material:material:1.1.0"
 
-  implementation 'com.airbnb.android:epoxy:2.17.0'
-  kapt 'com.airbnb.android:epoxy-processor:2.17.0'
-  implementation 'com.airbnb.android:mvrx:0.5.0'
+  implementation 'com.airbnb.android:epoxy:3.9.0'
+  implementation 'com.airbnb.android:epoxy-paging:3.9.0'
+  kapt 'com.airbnb.android:epoxy-processor:3.9.0'
+  implementation 'com.airbnb.android:mvrx:1.3.0'
+
   implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
   implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
   implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
-  implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.0'
-  implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1'
+  implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3'
+  implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3'
   implementation 'com.matthew-tamlin:sliding-intro-screen:3.2.0'
   implementation 'com.dlazaro66.qrcodereaderview:qrcodereaderview:2.0.2'
   implementation 'com.github.PhilJay:MPAndroidChart:v3.0.2'
diff --git a/LottieSample/src/main/assets/DynamicGradient.json b/LottieSample/src/main/assets/DynamicGradient.json
deleted file mode 100644
index 133c20c..0000000
--- a/LottieSample/src/main/assets/DynamicGradient.json
+++ /dev/null
@@ -1 +0,0 @@
-{"v":"5.4.4","fr":30.0000305175781,"ip":0,"op":300.000305175781,"w":800,"h":400,"nm":"DyanamicGradient","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Radial","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[836,202,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[250,250],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"gs","o":{"a":0,"k":100,"ix":9},"w":{"a":0,"k":45,"ix":10},"g":{"p":2,"k":{"a":0,"k":[0,1,1,1,1,0,0,0],"ix":8}},"s":{"a":0,"k":[0,0],"ix":4},"e":{"a":0,"k":[300,0],"ix":5},"t":2,"h":{"a":0,"k":0,"ix":6},"a":{"a":0,"k":0,"ix":7},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":13},"bm":0,"nm":"Gradient Stroke","mn":"ADBE Vector Graphic - G-Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":2,"k":{"a":0,"k":[0,1,1,1,1,0,0,0],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[101,0],"ix":6},"t":2,"h":{"a":0,"k":0,"ix":7},"a":{"a":0,"k":0,"ix":8},"nm":"Gradient Fill","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-218,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":300.000305175781,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Linear","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[400,202,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[250,250],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"gs","o":{"a":0,"k":100,"ix":9},"w":{"a":0,"k":45,"ix":10},"g":{"p":2,"k":{"a":0,"k":[0,1,1,1,1,0,0,0],"ix":8}},"s":{"a":0,"k":[0,0],"ix":4},"e":{"a":0,"k":[100,0],"ix":5},"t":1,"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":13},"bm":0,"nm":"Gradient Stroke","mn":"ADBE Vector Graphic - G-Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":2,"k":{"a":0,"k":[0,1,1,1,1,0,0,0],"ix":9}},"s":{"a":0,"k":[-75,0],"ix":5},"e":{"a":0,"k":[75,0],"ix":6},"t":1,"nm":"Gradient Fill","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-218,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":300.000305175781,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/LottieSample/src/main/assets/LottieLogo1.json b/LottieSample/src/main/assets/Lottie Logo 1.json
similarity index 100%
rename from LottieSample/src/main/assets/LottieLogo1.json
rename to LottieSample/src/main/assets/Lottie Logo 1.json
diff --git a/LottieSample/src/main/assets/LottieLogo2.json b/LottieSample/src/main/assets/Lottie Logo 2.json
similarity index 100%
rename from LottieSample/src/main/assets/LottieLogo2.json
rename to LottieSample/src/main/assets/Lottie Logo 2.json
diff --git a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/AnimationItemView.kt b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/AnimationItemView.kt
deleted file mode 100644
index 507a98e..0000000
--- a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/AnimationItemView.kt
+++ /dev/null
@@ -1,40 +0,0 @@
-package com.airbnb.lottie.samples
-
-import android.content.Context
-import android.graphics.drawable.ColorDrawable
-import android.util.AttributeSet
-import android.view.View
-import android.widget.FrameLayout
-import com.airbnb.epoxy.ModelProp
-import com.airbnb.epoxy.ModelView
-import com.airbnb.lottie.samples.model.AnimationData
-import com.airbnb.lottie.samples.model.CompositionArgs
-import kotlinx.android.synthetic.main.item_view_animation.view.*
-
-@ModelView(autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT)
-class AnimationItemView @JvmOverloads constructor(
-        context: Context,
-        attrs: AttributeSet? = null,
-        defStyleAttr: Int = 0
-) : FrameLayout(context, attrs, defStyleAttr) {
-
-    private var args: CompositionArgs? = null
-
-    init {
-        inflate(R.layout.item_view_animation)
-    }
-
-    @ModelProp
-    fun setAnimationData(animationData: AnimationData) {
-        args = CompositionArgs(animationData = animationData)
-        animationView.background = ColorDrawable(animationData.bgColorInt())
-        titleView.text = animationData.title
-        authorView.text = animationData.userInfo?.name ?: context.getString(R.string.anonymous)
-        animationView.setImageUrl(animationData.preview)
-    }
-
-    @ModelProp(options = [ModelProp.Option.DoNotHash])
-    fun setClickListener(listener: View.OnClickListener) {
-        clickOverlay.setOnClickListener(listener)
-    }
-}
\ No newline at end of file
diff --git a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/ListActivity.kt b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/ListActivity.kt
index f0bf1b8..b3fefe8 100644
--- a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/ListActivity.kt
+++ b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/ListActivity.kt
@@ -3,6 +3,7 @@
 import android.os.Bundle
 import androidx.appcompat.app.AppCompatActivity
 import com.airbnb.epoxy.EpoxyController
+import com.airbnb.epoxy.EpoxyRecyclerView
 import com.airbnb.lottie.samples.views.WishListIconView
 import com.airbnb.lottie.samples.views.listingCard
 import com.airbnb.lottie.samples.views.marquee
@@ -18,7 +19,11 @@
         supportActionBar?.setDisplayShowTitleEnabled(false)
         toolbar.setNavigationOnClickListener { finish() }
 
-        recyclerView.buildModelsWith { it.buildModels() }
+        recyclerView.buildModelsWith(object : EpoxyRecyclerView.ModelBuilderCallback {
+            override fun buildModels(controller: EpoxyController) {
+                controller.buildModels()
+            }
+        })
     }
 
     private fun EpoxyController.buildModels() {
diff --git a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/LottieApplication.kt b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/LottieApplication.kt
index 79379bf..451d150 100644
--- a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/LottieApplication.kt
+++ b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/LottieApplication.kt
@@ -25,16 +25,18 @@
     val retrofit by lazy {
         Retrofit.Builder()
                 .client(okHttpClient)
-                .baseUrl("https://lottiefiles.frb.io/api/v1/")
+                .baseUrl("https://api.lottiefiles.com/")
                 .addConverterFactory(GsonConverterFactory.create(gson))
                 .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                 .build()
     }
 
-    val lottiefilesService by lazy { retrofit.create(LottiefilesService::class.java) }
+    val lottiefilesService by lazy { retrofit.create(LottiefilesApi::class.java) }
 
     override fun onCreate() {
         super.onCreate()
         L.DBG = true
+        @Suppress("RestrictedApi")
+        L.setTraceEnabled(true)
     }
 }
\ No newline at end of file
diff --git a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/LottiefilesApi.kt b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/LottiefilesApi.kt
new file mode 100644
index 0000000..be114f6
--- /dev/null
+++ b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/LottiefilesApi.kt
@@ -0,0 +1,22 @@
+package com.airbnb.lottie.samples
+
+import com.airbnb.lottie.samples.model.AnimationResponse
+import com.airbnb.lottie.samples.model.AnimationResponseV2
+import io.reactivex.Single
+import retrofit2.http.GET
+import retrofit2.http.Path
+import retrofit2.http.Query
+
+interface LottiefilesApi {
+    @GET("v1/recent")
+    fun getRecent(@Query("page") page: Int): Single<AnimationResponse>
+
+    @GET("v1/popular")
+    fun getPopular(@Query("page") page: Int): Single<AnimationResponse>
+
+    @GET("v2/featured")
+    fun getCollection(): Single<AnimationResponseV2>
+
+    @GET("v1/search/{query}")
+    fun search(@Path("query") query: String, @Query("page") page: Int): Single<AnimationResponse>
+}
\ No newline at end of file
diff --git a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/LottiefilesFragment.kt b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/LottiefilesFragment.kt
index e66e777..763d74f 100644
--- a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/LottiefilesFragment.kt
+++ b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/LottiefilesFragment.kt
@@ -1,103 +1,181 @@
 package com.airbnb.lottie.samples
 
-import androidx.fragment.app.FragmentActivity
-import com.airbnb.epoxy.EpoxyController
+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.AnimationResponse
 import com.airbnb.lottie.samples.model.CompositionArgs
-import com.airbnb.lottie.samples.views.loadingView
+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 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 items: List<AnimationData> = emptyList(),
-        val request: Async<AnimationResponse> = Uninitialized,
         val query: String = ""
 ) : MvRxState
 
-class LottiefilesViewModel(
-        initialState: LottiefilesState,
-        private val service: LottiefilesService) : MvRxViewModel<LottiefilesState>(initialState
-) {
+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) { fetchMoreItems() }
+        selectSubscribe(LottiefilesState::mode, LottiefilesState::query) { mode, query ->
+            this.mode = mode
+            this.query = query
+            pagedList.value?.dataSource?.invalidate()
+        }
     }
 
-    fun fetchMoreItems() = withState { state ->
-        if (state.request is Loading) return@withState
-        val page = (state.request()?.currentPage ?: -1) + 1
-        if (state.request()?.lastPage == page && page > 0) return@withState
+    fun setMode(mode: LottiefilesMode) = setState { copy(mode = mode) }
 
-        when (state.mode) {
-            LottiefilesMode.Recent -> service.getRecent(page)
-            LottiefilesMode.Popular -> service.getPopular(page)
-            LottiefilesMode.Search -> service.search(state.query)
-        }.execute { copy(request = it, items = items + (it()?.data ?: emptyList())) }
-    }
+    fun setQuery(query: String) = setState { copy(query = query) }
 
-    fun setMode(mode: LottiefilesMode, query: String = "") = setState {
-        if (this.mode == mode && mode != LottiefilesMode.Search) return@setState this
-        if (this.mode == mode && mode == LottiefilesMode.Search && this.query == query) return@setState this
-
-        copy(mode = mode, request = Uninitialized, items = emptyList(), query = query)
-    }
-
-    companion object : MvRxViewModelFactory<LottiefilesState> {
-        @JvmStatic
-        override fun create(activity: FragmentActivity, state: LottiefilesState): LottiefilesViewModel {
-            val service = (activity.applicationContext as LottieApplication).lottiefilesService
+    companion object : MvRxViewModelFactory<LottiefilesViewModel, LottiefilesState> {
+        override fun create(viewModelContext: ViewModelContext, state: LottiefilesState): LottiefilesViewModel? {
+            val service = viewModelContext.app<LottieApplication>().lottiefilesService
             return LottiefilesViewModel(state, service)
         }
-
     }
 }
 
-class LottiefilesFragment : BaseEpoxyFragment() {
+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()
 
-    override fun EpoxyController.buildModels() = withState(viewModel) { state ->
-        marquee {
-            id("lottiefiles")
-            title(R.string.lottiefiles)
-            subtitle(R.string.lottiefiles_airbnb)
-        }
+    private val controller by lazy {
+        object : PagedListEpoxyController<AnimationData>() {
 
-        lottiefilesTabBar {
-            id("mode")
-            mode(state.mode)
-            recentClickListener { _ -> viewModel.setMode(LottiefilesMode.Recent) }
-            popularClickListener { _ -> viewModel.setMode(LottiefilesMode.Popular) }
-            searchClickListener { _ -> viewModel.setMode(LottiefilesMode.Search) }
-        }
+            var mode by Delegates.observable(LottiefilesMode.Recent) { _, _, _ -> requestModelBuild() }
 
-        if (state.mode == LottiefilesMode.Search) {
-            searchInputItemView {
-                id("search")
-                searchClickListener { viewModel.setMode(LottiefilesMode.Search, it) }
-            }
-        }
+            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)
+                            }
 
-        state.items.forEach {
-            val args = CompositionArgs(animationData = it)
-            animationItemView {
-                id(it.id)
-                animationData(it)
-                clickListener { _ ->
-                    startActivity(PlayerActivity.intent(requireContext(), args))
                 }
-                onBind { _, _, _ -> viewModel.fetchMoreItems() }
             }
-        }
 
-        if (state.request is Loading) {
-            loadingView {
-                id("loading")
-                onBind { _, _, _ -> viewModel.fetchMoreItems() }
+            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
+    }
 }
\ No newline at end of file
diff --git a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/LottiefilesService.kt b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/LottiefilesService.kt
deleted file mode 100644
index 27a9ecb..0000000
--- a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/LottiefilesService.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.airbnb.lottie.samples
-
-import com.airbnb.lottie.samples.model.AnimationResponse
-import io.reactivex.Observable
-import retrofit2.http.GET
-import retrofit2.http.Path
-import retrofit2.http.Query
-
-interface LottiefilesService {
-    @GET("recent")
-    fun getRecent(@Query("page") page: Int): Observable<AnimationResponse>
-
-    @GET("popular")
-    fun getPopular(@Query("page") page: Int): Observable<AnimationResponse>
-
-    @GET("collections/{collection}")
-    fun getCollection(@Path("collection") collection: String): Observable<AnimationResponse>
-
-    @GET("search/{query}")
-    fun search(@Path("query") query: String): Observable<AnimationResponse>
-}
\ No newline at end of file
diff --git a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/PlayerFragment.kt b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/PlayerFragment.kt
index dcd63ed..632bbac 100644
--- a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/PlayerFragment.kt
+++ b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/PlayerFragment.kt
@@ -12,11 +12,14 @@
 import androidx.appcompat.app.AppCompatActivity
 import androidx.core.content.ContextCompat
 import androidx.core.view.children
+import androidx.core.view.get
 import androidx.core.view.isVisible
 import androidx.fragment.app.Fragment
 import androidx.lifecycle.Lifecycle
 import androidx.transition.AutoTransition
 import androidx.transition.TransitionManager
+import com.airbnb.epoxy.EpoxyController
+import com.airbnb.epoxy.EpoxyRecyclerView
 import com.airbnb.lottie.*
 import com.airbnb.lottie.model.KeyPath
 import com.airbnb.lottie.samples.model.CompositionArgs
@@ -105,7 +108,7 @@
         (requireActivity() as AppCompatActivity).supportActionBar?.setDisplayShowTitleEnabled(false)
         setHasOptionsMenu(true)
 
-        lottieVersionView.text = getString(R.string.lottie_version, com.airbnb.lottie.BuildConfig.VERSION_NAME)
+        lottieVersionView.text = getString(R.string.lottie_version, BuildConfig.VERSION_NAME)
 
         animationView.setFontAssetDelegate(object : FontAssetDelegate() {
             override fun fetchFont(fontFamily: String?): Typeface {
@@ -115,7 +118,13 @@
 
         val args = arguments?.getParcelable<CompositionArgs>(EXTRA_ANIMATION_ARGS)
                 ?: throw IllegalArgumentException("No composition args specified")
-        args.animationData?.bgColorInt()?.let {
+        args.animationData?.bgColorInt?.let {
+            backgroundButton1.setBackgroundColor(it)
+            animationContainer.setBackgroundColor(it)
+            invertColor(it)
+        }
+
+        args.animationDataV2?.bgColorInt?.let {
             backgroundButton1.setBackgroundColor(it)
             animationContainer.setBackgroundColor(it)
             invertColor(it)
@@ -438,15 +447,16 @@
         // Scale up to fill the screen
         scaleSeekBar.progress = 100
 
-        keyPathsRecyclerView.buildModelsWith { controller ->
-            animationView.resolveKeyPath(KeyPath("**")).forEachIndexed { index, keyPath ->
-                BottomSheetItemViewModel_()
-                        .id(index)
-                        .text(keyPath.keysToString())
-                        .addTo(controller)
-
+        keyPathsRecyclerView.buildModelsWith(object : EpoxyRecyclerView.ModelBuilderCallback {
+            override fun buildModels(controller: EpoxyController) {
+                animationView.resolveKeyPath(KeyPath("**")).forEachIndexed { index, keyPath ->
+                    BottomSheetItemViewModel_()
+                            .id(index)
+                            .text(keyPath.keysToString())
+                            .addTo(controller)
+                }
             }
-        }
+        })
 
         updateWarnings()
     }
diff --git a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/PlayerViewModel.kt b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/PlayerViewModel.kt
index 7b6b6bc..dc7ad22 100644
--- a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/PlayerViewModel.kt
+++ b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/PlayerViewModel.kt
@@ -3,15 +3,12 @@
 import android.animation.ValueAnimator
 import android.app.Application
 import android.net.Uri
-import androidx.fragment.app.FragmentActivity
 import com.airbnb.lottie.LottieComposition
 import com.airbnb.lottie.LottieCompositionFactory
 import com.airbnb.lottie.LottieTask
-import com.airbnb.lottie.model.LottieCompositionCache
 import com.airbnb.lottie.samples.model.CompositionArgs
 import com.airbnb.mvrx.*
 import java.io.FileInputStream
-import java.io.FileNotFoundException
 
 data class PlayerState(
         val composition: Async<LottieComposition> = Uninitialized,
@@ -37,7 +34,7 @@
 ) : MvRxViewModel<PlayerState>(initialState) {
 
     fun fetchAnimation(args: CompositionArgs) {
-        val url = args.url ?: args.animationData?.lottieLink
+        val url = args.url ?: args.animationDataV2?.file ?: args.animationData?.lottieLink
 
         when {
             url != null -> LottieCompositionFactory.fromUrl(application, url, null)
@@ -112,8 +109,9 @@
         )
     }
 
-    companion object : MvRxViewModelFactory<PlayerState> {
-        @JvmStatic
-        override fun create(activity: FragmentActivity, state: PlayerState) = PlayerViewModel(state, activity.application)
+    companion object : MvRxViewModelFactory<PlayerViewModel, PlayerState> {
+        override fun create(viewModelContext: ViewModelContext, state: PlayerState): PlayerViewModel? {
+            return PlayerViewModel(state, viewModelContext.app())
+        }
     }
 }
\ No newline at end of file
diff --git a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/ShowcaseFragment.kt b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/ShowcaseFragment.kt
index 81661b5..1fa9406 100644
--- a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/ShowcaseFragment.kt
+++ b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/ShowcaseFragment.kt
@@ -1,30 +1,36 @@
 package com.airbnb.lottie.samples
 
 import android.content.Intent
-import androidx.fragment.app.FragmentActivity
 import com.airbnb.epoxy.EpoxyController
-import com.airbnb.lottie.samples.model.AnimationResponse
+import com.airbnb.lottie.samples.model.AnimationResponseV2
 import com.airbnb.lottie.samples.model.CompositionArgs
 import com.airbnb.lottie.samples.model.ShowcaseItem
+import com.airbnb.lottie.samples.views.animationItemView
 import com.airbnb.lottie.samples.views.loadingView
 import com.airbnb.lottie.samples.views.marquee
-import com.airbnb.lottie.samples.views.showcaseAnimationItemView
 import com.airbnb.lottie.samples.views.showcaseCarousel
-import com.airbnb.mvrx.*
+import com.airbnb.mvrx.Async
+import com.airbnb.mvrx.MvRxState
+import com.airbnb.mvrx.MvRxViewModelFactory
+import com.airbnb.mvrx.Uninitialized
+import com.airbnb.mvrx.ViewModelContext
+import com.airbnb.mvrx.fragmentViewModel
+import com.airbnb.mvrx.withState
+import io.reactivex.schedulers.Schedulers
 
-data class ShowcaseState(val response: Async<AnimationResponse> = Uninitialized) : MvRxState
+data class ShowcaseState(val response: Async<AnimationResponseV2> = Uninitialized) : MvRxState
 
-class ShowcaseViewModel(initialState: ShowcaseState, service: LottiefilesService) : MvRxViewModel<ShowcaseState>(initialState) {
+class ShowcaseViewModel(initialState: ShowcaseState, api: LottiefilesApi) : MvRxViewModel<ShowcaseState>(initialState) {
     init {
-        service.getCollection("lottie-showcase")
+        api.getCollection()
+                .subscribeOn(Schedulers.io())
                 .retry(3)
                 .execute { copy(response = it) }
     }
 
-    companion object : MvRxViewModelFactory<ShowcaseState> {
-        @JvmStatic
-        override fun create(activity: FragmentActivity, state: ShowcaseState): ShowcaseViewModel {
-            val service = (activity.applicationContext as LottieApplication).lottiefilesService
+    companion object : MvRxViewModelFactory<ShowcaseViewModel, ShowcaseState> {
+        override fun create(viewModelContext: ViewModelContext, state: ShowcaseState): ShowcaseViewModel? {
+            val service = viewModelContext.app<LottieApplication>().lottiefilesService
             return ShowcaseViewModel(state, service)
         }
     }
@@ -72,11 +78,12 @@
             }
         } else {
             collectionItems.forEach {
-                showcaseAnimationItemView {
+                animationItemView {
                     id(it.id)
                     title(it.title)
-                    previewUrl(it.preview)
-                    onClickListener { _ -> startActivity(PlayerActivity.intent(requireContext(), CompositionArgs(animationData = it))) }
+                    previewUrl("https://assets9.lottiefiles.com/${it.preview}")
+                    previewBackgroundColor(it.bgColorInt)
+                    onClickListener { _ -> startActivity(PlayerActivity.intent(requireContext(), CompositionArgs(animationDataV2 = it))) }
                 }
             }
         }
diff --git a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/TypeExtensions.kt b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/TypeExtensions.kt
index dcec895..2a72e7f 100644
--- a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/TypeExtensions.kt
+++ b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/TypeExtensions.kt
@@ -4,10 +4,12 @@
 import android.content.Context
 import android.content.Intent
 import android.content.pm.PackageManager
+import android.graphics.Color
 import android.net.Uri
 import android.os.Build
 import android.os.VibrationEffect
 import android.os.Vibrator
+import android.util.Log
 import androidx.annotation.DrawableRes
 import androidx.annotation.LayoutRes
 import androidx.annotation.StringRes
@@ -18,8 +20,13 @@
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
+import android.view.inputmethod.InputMethodManager
 import android.widget.ImageView
 import android.widget.TextView
+import androidx.annotation.ColorInt
+import androidx.core.content.getSystemService
+import androidx.core.graphics.toColorInt
+import com.airbnb.lottie.L
 import com.bumptech.glide.Glide
 
 fun Fragment.startActivity(cls: Class<*>) {
@@ -72,4 +79,36 @@
         @Suppress("DEPRECATION")
         vibrate(millis)
     }
+}
+
+@ColorInt
+fun String?.toColorIntSafe(): Int {
+    var bgColor = this ?: "#ffffff"
+    bgColor = if (bgColor.startsWith("#")) bgColor else "#$bgColor"
+
+    return try {
+        when (bgColor.length) {
+            0 -> "#ffffff"
+            4 -> "#%c%c%c%c%c%c".format(
+                    bgColor[1], bgColor[1],
+                    bgColor[2], bgColor[2],
+                    bgColor[3], bgColor[3]
+            )
+            5 -> "#%c%c%c%c%c%c%c%c".format(
+                    bgColor[1], bgColor[1],
+                    bgColor[2], bgColor[2],
+                    bgColor[3], bgColor[3],
+                    bgColor[4], bgColor[4]
+            )
+            else -> bgColor
+        }.toColorInt()
+    } catch (e: IllegalArgumentException) {
+        Log.w(L.TAG, "Unable to parse $bgColor.")
+        Color.WHITE
+    }
+}
+
+fun Context.hideKeyboard() {
+    val inputMethodManager = getSystemService<InputMethodManager>()!!
+    inputMethodManager.hideSoftInputFromWindow((this as Activity).currentFocus?.windowToken, 0)
 }
\ No newline at end of file
diff --git a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/model/AnimationData.kt b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/model/AnimationData.kt
index dfca563..c36abbb 100644
--- a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/model/AnimationData.kt
+++ b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/model/AnimationData.kt
@@ -1,53 +1,18 @@
 package com.airbnb.lottie.samples.model
 
-import android.graphics.Color
 import android.os.Parcelable
-import androidx.annotation.ColorInt
-import android.util.Log
-import androidx.core.graphics.toColorInt
-import com.airbnb.lottie.L
+import com.airbnb.lottie.samples.toColorIntSafe
 import kotlinx.android.parcel.Parcelize
 
-// This is a lint bug
-@SuppressWarnings("ParcelCreator")
 @Parcelize
 data class AnimationData(
         val id: Long,
         val title: String,
         val description: String?,
         private val bgColor: String?,
-        val aepFile: String,
-        val bodymovinVersion: String,
-        val slug: String,
-        val speed: String,
         val preview: String?,
         val lottieLink: String,
         val userInfo: UserInfo?
 ) : Parcelable {
-    @ColorInt
-    fun bgColorInt(): Int {
-        var bgColor = this.bgColor ?: "#ffffff"
-        bgColor = if (bgColor.startsWith("#")) bgColor else "#$bgColor"
-
-        return try {
-            when (bgColor.length) {
-                0 -> "#ffffff"
-                4 -> "#%c%c%c%c%c%c".format(
-                        bgColor[1], bgColor[1],
-                        bgColor[2], bgColor[2],
-                        bgColor[3], bgColor[3]
-                )
-                5 -> "#%c%c%c%c%c%c%c%c".format(
-                        bgColor[1], bgColor[1],
-                        bgColor[2], bgColor[2],
-                        bgColor[3], bgColor[3],
-                        bgColor[4], bgColor[4]
-                )
-                else -> bgColor
-            }.toColorInt()
-        } catch (e: IllegalArgumentException) {
-            Log.w(L.TAG, "Unable to parse $bgColor.")
-            Color.WHITE
-        }
-    }
+    val bgColorInt get() = bgColor.toColorIntSafe()
 }
\ No newline at end of file
diff --git a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/model/AnimationDataV2.kt b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/model/AnimationDataV2.kt
new file mode 100644
index 0000000..7c64b4a
--- /dev/null
+++ b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/model/AnimationDataV2.kt
@@ -0,0 +1,40 @@
+package com.airbnb.lottie.samples.model
+
+import android.os.Parcelable
+import com.airbnb.lottie.samples.toColorIntSafe
+import kotlinx.android.parcel.Parcelize
+
+@Parcelize
+data class AnimationDataV2(
+        val aepFlag: Int,
+        val baseprice: String,
+        val bgColor: String,
+        val bodymovinVersion: String?,
+        val createdAt: String,
+        val dataFile: String,
+        val description: String,
+        val downloads: Int,
+        val file: String,
+        val fileHash: String,
+        val id: Int,
+        val inProcess: Int,
+        val isSticker: Int,
+        val preview: String,
+        val previewFrame: Int,
+        val previewUrl: String,
+        val previewVideo: String,
+        val previewVideoUrl: String,
+        val price: Int,
+        val publishedAt: String,
+        val slug: String,
+        val speed: Int,
+        val status: Int,
+        val title: String,
+        val updated_at: String,
+        val userId: Int,
+        val videoFramerate: Int,
+        val views: Int
+) : Parcelable {
+    val bgColorInt get() = bgColor.toColorIntSafe()
+
+}
\ No newline at end of file
diff --git a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/model/AnimationResponseV2.kt b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/model/AnimationResponseV2.kt
new file mode 100644
index 0000000..9a4a159
--- /dev/null
+++ b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/model/AnimationResponseV2.kt
@@ -0,0 +1,16 @@
+package com.airbnb.lottie.samples.model
+
+data class AnimationResponseV2(
+        val current_page: Int,
+        val data: List<AnimationDataV2>,
+        val first_page_url: String,
+        val from: Int,
+        val last_page: Int,
+        val last_page_url: String,
+        val next_page_url: Any,
+        val path: String,
+        val per_page: Int,
+        val prev_page_url: Any,
+        val to: Int,
+        val total: Int
+)
\ No newline at end of file
diff --git a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/model/CompositionArgs.kt b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/model/CompositionArgs.kt
index 0e0fdf8..8192d95 100644
--- a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/model/CompositionArgs.kt
+++ b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/model/CompositionArgs.kt
@@ -12,5 +12,6 @@
         val url: String? = null,
         val fileUri: Uri? = null,
         val asset: String? = null,
-        val animationData: AnimationData? = null
+        val animationData: AnimationData? = null,
+        val animationDataV2: AnimationDataV2? = null
 ) : Parcelable
\ No newline at end of file
diff --git a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/views/ShowcaseAnimationItemView.kt b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/views/AnimationItemView.kt
similarity index 70%
rename from LottieSample/src/main/kotlin/com/airbnb/lottie/samples/views/ShowcaseAnimationItemView.kt
rename to LottieSample/src/main/kotlin/com/airbnb/lottie/samples/views/AnimationItemView.kt
index 168c9e7..e94c588 100644
--- a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/views/ShowcaseAnimationItemView.kt
+++ b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/views/AnimationItemView.kt
@@ -3,6 +3,7 @@
 import android.content.Context
 import android.util.AttributeSet
 import android.widget.FrameLayout
+import androidx.annotation.ColorInt
 import com.airbnb.epoxy.ModelProp
 import com.airbnb.epoxy.ModelView
 import com.airbnb.epoxy.TextProp
@@ -12,7 +13,7 @@
 import kotlinx.android.synthetic.main.item_view_showcase_animation.view.*
 
 @ModelView(autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT)
-class ShowcaseAnimationItemView @JvmOverloads constructor(
+class AnimationItemView @JvmOverloads constructor(
         context: Context,
         attrs: AttributeSet? = null,
         defStyleAttr: Int = 0
@@ -28,10 +29,20 @@
     }
 
     @TextProp
-    fun setTitle(title: CharSequence) {
+    fun setTitle(title: CharSequence?) {
         titleView.text = title
     }
 
+    @ModelProp
+    fun setPreviewBackgroundColor(@ColorInt bgColor: Int?) {
+        if (bgColor == null) {
+            imageView.setBackgroundResource(R.color.loading_placeholder)
+            imageView.setImageDrawable(null)
+        } else {
+            imageView.setBackgroundColor(bgColor)
+        }
+    }
+
     @ModelProp(options = [ModelProp.Option.DoNotHash])
     override fun setOnClickListener(l: OnClickListener?) {
         container.setOnClickListener(l)
diff --git a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/views/SearchInputItemView.kt b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/views/SearchInputItemView.kt
index 318d4d0..0037c64 100644
--- a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/views/SearchInputItemView.kt
+++ b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/views/SearchInputItemView.kt
@@ -6,6 +6,7 @@
 import android.view.inputmethod.EditorInfo
 import android.view.inputmethod.InputMethodManager
 import android.widget.FrameLayout
+import androidx.core.content.getSystemService
 import com.airbnb.epoxy.ModelProp
 import com.airbnb.epoxy.ModelView
 import com.airbnb.lottie.samples.R
@@ -36,8 +37,8 @@
     @ModelProp(options = [ModelProp.Option.DoNotHash])
     fun setSearchClickListener(listener: (String) -> Unit) {
         searchButton.setOnClickListener {
-            val inputMethodManager = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
-            inputMethodManager.hideSoftInputFromInputMethod(windowToken, 0)
+            val inputMethodManager = context.getSystemService<InputMethodManager>()!!
+            inputMethodManager.hideSoftInputFromWindow(windowToken, 0)
             listener(searchEditText.text.toString())
         }
     }
diff --git a/LottieSample/src/main/res/layout/fragment_base.xml b/LottieSample/src/main/res/layout/fragment_base.xml
index bd6a661..4c25116 100644
--- a/LottieSample/src/main/res/layout/fragment_base.xml
+++ b/LottieSample/src/main/res/layout/fragment_base.xml
@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
-                                                     android:id="@+id/coordinatorLayout"
-                                                     android:layout_width="match_parent"
-                                                     android:layout_height="match_parent">
+    android:id="@+id/coordinatorLayout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
 
     <com.airbnb.epoxy.EpoxyRecyclerView
         android:id="@+id/recyclerView"
diff --git a/LottieSample/src/main/res/layout/item_view_animation.xml b/LottieSample/src/main/res/layout/item_view_animation.xml
deleted file mode 100644
index 9231cb0..0000000
--- a/LottieSample/src/main/res/layout/item_view_animation.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_marginLeft="24dp"
-    android:layout_marginRight="24dp">
-
-    <ImageView
-        android:id="@+id/animationView"
-        android:layout_width="match_parent"
-        android:background="@drawable/list_item_animation_background"
-        app:layout_constraintBottom_toTopOf="@id/titleView"
-        app:layout_constraintDimensionRatio="1"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent" />
-
-    <TextView
-        android:id="@+id/titleView"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_marginEnd="8dp"
-        android:layout_marginRight="8dp"
-        android:layout_marginTop="8dp"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toBottomOf="@+id/animationView"
-        tools:text="Loading Animation 1" />
-
-    <TextView
-        android:id="@+id/authorView"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_marginBottom="24dp"
-        android:layout_marginEnd="8dp"
-        android:layout_marginRight="8dp"
-        android:layout_marginTop="8dp"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="@+id/titleView"
-        app:layout_constraintTop_toBottomOf="@+id/titleView"
-        tools:text="Gabriel Peal" />
-
-    <View
-        android:id="@+id/clickOverlay"
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        android:background="?attr/selectableItemBackground"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="@+id/animationView" />
-
-
-</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/LottieSample/src/main/res/layout/item_view_search_input.xml b/LottieSample/src/main/res/layout/item_view_search_input.xml
index d247673..e731a42 100644
--- a/LottieSample/src/main/res/layout/item_view_search_input.xml
+++ b/LottieSample/src/main/res/layout/item_view_search_input.xml
@@ -7,8 +7,8 @@
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginLeft="24dp"
-        android:layout_marginRight="24dp"
+        android:layout_marginLeft="16dp"
+        android:layout_marginRight="16dp"
         android:orientation="horizontal">
 
         <EditText
diff --git a/LottieSample/src/main/res/layout/list_item_preview.xml b/LottieSample/src/main/res/layout/list_item_preview.xml
index e44c95a..59fb1b6 100644
--- a/LottieSample/src/main/res/layout/list_item_preview.xml
+++ b/LottieSample/src/main/res/layout/list_item_preview.xml
@@ -8,8 +8,8 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:paddingBottom="18dp"
-        android:paddingLeft="24dp"
-        android:paddingRight="24dp"
+        android:paddingLeft="16dp"
+        android:paddingRight="16dp"
         android:paddingTop="18dp"
         android:gravity="center_vertical"
         android:orientation="horizontal"
diff --git a/LottieSample/src/main/res/layout/lottiefiles_tab_bar.xml b/LottieSample/src/main/res/layout/lottiefiles_tab_bar.xml
index 74f74b8..bfa86ac 100644
--- a/LottieSample/src/main/res/layout/lottiefiles_tab_bar.xml
+++ b/LottieSample/src/main/res/layout/lottiefiles_tab_bar.xml
@@ -8,7 +8,7 @@
         android:id="@+id/recentView"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginLeft="24dp"
+        android:layout_marginLeft="16dp"
         android:layout_marginRight="24dp"
         android:background="?attr/selectableItemBackground"
         app:titleText="@string/recent"/>
diff --git a/LottieSample/src/main/res/layout/marquee.xml b/LottieSample/src/main/res/layout/marquee.xml
index e6a2e97..125c0eb 100644
--- a/LottieSample/src/main/res/layout/marquee.xml
+++ b/LottieSample/src/main/res/layout/marquee.xml
@@ -9,8 +9,8 @@
         android:id="@+id/titleView"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginLeft="24dp"
-        android:layout_marginTop="64dp"
+        android:layout_marginLeft="16dp"
+        android:layout_marginTop="24dp"
         android:textSize="32sp"
         android:textStyle="bold"
         android:textColor="@color/text_color_dark"/>
@@ -19,7 +19,7 @@
         android:id="@+id/subtitleView"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginLeft="24dp"
+        android:layout_marginLeft="16dp"
         android:layout_marginTop="8dp"
         android:textSize="16sp"
         android:textStyle="bold"
diff --git a/build.gradle b/build.gradle
index 38fcd03..b9600ba 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,7 +1,7 @@
 import org.ajoberstar.grgit.Grgit
 
 buildscript {
-  ext.kotlinVersion = '1.3.41'
+  ext.kotlinVersion = '1.3.61'
 
   repositories {
     jcenter()