diff --git a/app/build.gradle b/app/build.gradle
index 0b27de6..2f3e31f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,4 +1,4 @@
-apply from: 'versions.gradle'
+apply from: '../versions.gradle'
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
@@ -15,6 +15,9 @@ android {
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+
+ buildConfigField "String", "DB_NAME", '"friendlists.db"'
+ buildConfigField "String", "BASE_ENDPOINT", '"https://jsonplaceholder.typicode.com/"'
}
buildTypes {
@@ -45,4 +48,5 @@ android {
dependencies {
implementation project(":base")
+ implementation project(":userlist")
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index cfc8478..c89e347 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,11 +1,28 @@
+
+
+ android:supportsRtl="false"
+ android:theme="@style/AppTheme">
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/java/com/hako/friendlists/MainApplication.kt b/app/src/main/java/com/hako/friendlists/MainApplication.kt
new file mode 100644
index 0000000..92db1b3
--- /dev/null
+++ b/app/src/main/java/com/hako/friendlists/MainApplication.kt
@@ -0,0 +1,34 @@
+package com.hako.friendlists
+
+import android.app.Application
+import com.hako.userlist.di.userlistModules
+import com.hako.friendlists.di.appModules
+import org.koin.android.ext.koin.androidContext
+import org.koin.core.context.startKoin
+import timber.log.Timber
+
+@Suppress("unused")
+class MainApplication : Application() {
+ override fun onCreate() {
+ super.onCreate()
+ setupLogger()
+ setupDi()
+ }
+
+ private fun setupLogger() {
+ Timber.plant(Timber.DebugTree())
+ }
+
+ private fun setupDi() {
+ startKoin {
+ androidContext(this@MainApplication)
+
+ modules(
+ listOf(
+ appModules,
+ userlistModules
+ )
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/hako/friendlists/di/AppModules.kt b/app/src/main/java/com/hako/friendlists/di/AppModules.kt
new file mode 100644
index 0000000..46b2320
--- /dev/null
+++ b/app/src/main/java/com/hako/friendlists/di/AppModules.kt
@@ -0,0 +1,18 @@
+package com.hako.friendlists.di
+
+import androidx.room.Room
+import com.hako.base.domain.database.DatabaseClient
+import com.hako.base.domain.network.RemoteClient
+import com.hako.friendlists.BuildConfig
+import org.koin.dsl.module
+
+val appModules = module {
+ // Room database
+ single { Room.databaseBuilder(get(), DatabaseClient::class.java, BuildConfig.DB_NAME).build() }
+ factory { get().userDao() }
+ factory { get().albumDao() }
+ factory { get().photoDao() }
+
+ // Retrofit
+ single { RemoteClient(BuildConfig.BASE_ENDPOINT) }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/hako/friendlists/view/MainActivity.kt b/app/src/main/java/com/hako/friendlists/view/MainActivity.kt
new file mode 100644
index 0000000..3652198
--- /dev/null
+++ b/app/src/main/java/com/hako/friendlists/view/MainActivity.kt
@@ -0,0 +1,22 @@
+package com.hako.friendlists.view
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.navigation.findNavController
+import com.hako.friendlists.R
+
+class MainActivity : AppCompatActivity() {
+
+ private val navController by lazy { findNavController(R.id.main_fragment_container) }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main)
+ setupNavigation()
+ }
+
+ private fun setupNavigation() {
+ navController.setGraph(R.navigation.main_navigation)
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..dd69f9f
--- /dev/null
+++ b/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,8 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/navigation/main_navigation.xml b/app/src/main/res/navigation/main_navigation.xml
new file mode 100644
index 0000000..4befeb8
--- /dev/null
+++ b/app/src/main/res/navigation/main_navigation.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 69b2233..3743e89 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -1,6 +1,7 @@
- #008577
- #00574B
- #D81B60
+ #37474F
+ #324047
+ #CBCFD1
+ #D32F2F
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 5885930..f69921a 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -1,11 +1,10 @@
-
diff --git a/app/versions.gradle b/app/versions.gradle
deleted file mode 100644
index 1c984d7..0000000
--- a/app/versions.gradle
+++ /dev/null
@@ -1,33 +0,0 @@
-ext.deps = [:]
-
-def versions = [:]
-versions.kotlin = "1.3.41"
-versions.gradle = "3.5.3"
-
-def deps = [:]
-
-def project = [:]
-project.kotlin = "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlin"
-project.gradle = "com.android.tools.build:gradle:$versions.gradle"
-deps.project = project
-
-def build_versions = [:]
-build_versions.min_sdk = 21
-build_versions.target_sdk = 29
-build_versions.build_tools = "29.0.3"
-ext.build_versions = build_versions
-
-def kotlin = [:]
-kotlin.version = "$versions.kotlin"
-kotlin.std_lib = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$versions.kotlin"
-deps.kotlin = kotlin
-
-ext.deps = deps
-
-def addRepos(RepositoryHandler handler) {
- handler.google()
- handler.jcenter()
- handler.maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
-}
-
-ext.addRepos = this.&addRepos
diff --git a/base/build.gradle b/base/build.gradle
index 75c9e33..88cc65c 100644
--- a/base/build.gradle
+++ b/base/build.gradle
@@ -37,6 +37,8 @@ dependencies {
api deps.androidx.lifecycle_ext
api deps.androidx.lifecycle_viewmodel
api deps.androidx.recycler_view
+ api deps.androidx.navigation_fragment
+ api deps.androidx.navigation_ui
api deps.retrofit.runtime
api deps.retrofit.gson
api deps.retrofit.rx
@@ -48,6 +50,7 @@ dependencies {
api deps.rx.android
api deps.okhttp_logging_interceptor
api deps.timber
+ api deps.lottie
//Testing
api deps.testing.junit
api deps.testing.koin
diff --git a/base/src/main/java/com/hako/base/domain/Either.kt b/base/src/main/java/com/hako/base/domain/Either.kt
new file mode 100644
index 0000000..2c90b33
--- /dev/null
+++ b/base/src/main/java/com/hako/base/domain/Either.kt
@@ -0,0 +1,33 @@
+package com.hako.base.domain
+
+sealed class Either {
+
+ data class Left(val a: L) : Either()
+ data class Right(val b: R) : Either()
+
+ val isRight get() = this is Right
+ val isLeft get() = this is Left
+
+ fun left(a: L) = Left(a)
+ fun right(b: R) = Right(b)
+
+ fun either(fnL: (L) -> Any, fnR: (R) -> Any): Any =
+ when (this) {
+ is Left -> fnL(a)
+ is Right -> fnR(b)
+ }
+}
+
+fun ((A) -> B).c(f: (B) -> C): (A) -> C = {
+ f(this(it))
+}
+
+fun Either.flatMap(fn: (R) -> Either): Either =
+ when (this) {
+ is Either.Left -> Either.Left(
+ a
+ )
+ is Either.Right -> fn(b)
+ }
+
+fun Either.map(fn: (R) -> (T)): Either = this.flatMap(fn.c(::right))
diff --git a/base/src/main/java/com/hako/base/domain/UseCase.kt b/base/src/main/java/com/hako/base/domain/UseCase.kt
new file mode 100644
index 0000000..d9e178c
--- /dev/null
+++ b/base/src/main/java/com/hako/base/domain/UseCase.kt
@@ -0,0 +1,5 @@
+package com.hako.base.domain
+
+interface UseCase {
+ fun execute(onSuccess: (List) -> Unit, onError: (Throwable) -> Unit, onLoading: () -> Unit)
+}
\ No newline at end of file
diff --git a/base/src/main/java/com/hako/base/domain/database/DatabaseClient.kt b/base/src/main/java/com/hako/base/domain/database/DatabaseClient.kt
new file mode 100644
index 0000000..b78a001
--- /dev/null
+++ b/base/src/main/java/com/hako/base/domain/database/DatabaseClient.kt
@@ -0,0 +1,17 @@
+package com.hako.base.domain.database
+
+import androidx.room.Database
+import androidx.room.RoomDatabase
+import com.hako.base.domain.database.dao.AlbumDao
+import com.hako.base.domain.database.dao.PhotoDao
+import com.hako.base.domain.database.dao.UserDao
+import com.hako.base.domain.database.entities.AlbumEntity
+import com.hako.base.domain.database.entities.PhotoEntity
+import com.hako.base.domain.database.entities.UserEntity
+
+@Database(entities = [UserEntity::class, AlbumEntity::class, PhotoEntity::class], version = 1, exportSchema = false)
+abstract class DatabaseClient : RoomDatabase() {
+ abstract fun userDao(): UserDao
+ abstract fun albumDao(): AlbumDao
+ abstract fun photoDao(): PhotoDao
+}
\ No newline at end of file
diff --git a/base/src/main/java/com/hako/base/domain/database/dao/AlbumDao.kt b/base/src/main/java/com/hako/base/domain/database/dao/AlbumDao.kt
new file mode 100644
index 0000000..3a395de
--- /dev/null
+++ b/base/src/main/java/com/hako/base/domain/database/dao/AlbumDao.kt
@@ -0,0 +1,27 @@
+package com.hako.base.domain.database.dao
+
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import com.hako.base.domain.database.entities.AlbumEntity
+
+@Dao
+interface AlbumDao {
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ fun save(entity: AlbumEntity)
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ fun saveAll(entities: List)
+
+ @get:Query("SELECT * FROM ${AlbumEntity.TABLE_NAME}")
+ val all: List
+
+ @Query("SELECT COUNT(*) FROM ${AlbumEntity.TABLE_NAME}")
+ fun count(): Int
+
+ @Query("DELETE FROM ${AlbumEntity.TABLE_NAME}")
+ fun nukeDatabase()
+
+}
diff --git a/base/src/main/java/com/hako/base/domain/database/dao/PhotoDao.kt b/base/src/main/java/com/hako/base/domain/database/dao/PhotoDao.kt
new file mode 100644
index 0000000..0fc562b
--- /dev/null
+++ b/base/src/main/java/com/hako/base/domain/database/dao/PhotoDao.kt
@@ -0,0 +1,27 @@
+package com.hako.base.domain.database.dao
+
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import com.hako.base.domain.database.entities.PhotoEntity
+
+@Dao
+interface PhotoDao {
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ fun save(entity: PhotoEntity)
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ fun saveAll(entities: List)
+
+ @get:Query("SELECT * FROM ${PhotoEntity.TABLE_NAME}")
+ val all: List
+
+ @Query("SELECT COUNT(*) FROM ${PhotoEntity.TABLE_NAME}")
+ fun count(): Int
+
+ @Query("DELETE FROM ${PhotoEntity.TABLE_NAME}")
+ fun nukeDatabase()
+
+}
diff --git a/base/src/main/java/com/hako/base/domain/database/dao/UserDao.kt b/base/src/main/java/com/hako/base/domain/database/dao/UserDao.kt
new file mode 100644
index 0000000..217fe87
--- /dev/null
+++ b/base/src/main/java/com/hako/base/domain/database/dao/UserDao.kt
@@ -0,0 +1,30 @@
+package com.hako.base.domain.database.dao
+
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import com.hako.base.domain.database.entities.UserEntity
+
+@Dao
+interface UserDao {
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ fun save(entity: UserEntity)
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ fun saveAll(entities: List)
+
+ @get:Query("SELECT * FROM ${UserEntity.TABLE_NAME}")
+ val all: List
+
+ @Query("SELECT * FROM ${UserEntity.TABLE_NAME}")
+ fun getAllUsers(): List
+
+ @Query("SELECT COUNT(*) FROM ${UserEntity.TABLE_NAME}")
+ fun count(): Int
+
+ @Query("DELETE FROM ${UserEntity.TABLE_NAME}")
+ fun nukeDatabase()
+
+}
diff --git a/base/src/main/java/com/hako/base/domain/database/entities/Album.kt b/base/src/main/java/com/hako/base/domain/database/entities/Album.kt
new file mode 100644
index 0000000..a6174ad
--- /dev/null
+++ b/base/src/main/java/com/hako/base/domain/database/entities/Album.kt
@@ -0,0 +1,17 @@
+package com.hako.base.domain.database.entities
+
+import androidx.room.Entity
+import androidx.room.Index
+import androidx.room.PrimaryKey
+
+@Entity(tableName = AlbumEntity.TABLE_NAME, indices = [Index(value = ["id"], unique = true)])
+data class AlbumEntity(
+ @PrimaryKey
+ val id: Int,
+ val userId: Int,
+ val title: String
+) {
+ companion object {
+ const val TABLE_NAME = "albums"
+ }
+}
\ No newline at end of file
diff --git a/base/src/main/java/com/hako/base/domain/database/entities/Photos.kt b/base/src/main/java/com/hako/base/domain/database/entities/Photos.kt
new file mode 100644
index 0000000..68432f7
--- /dev/null
+++ b/base/src/main/java/com/hako/base/domain/database/entities/Photos.kt
@@ -0,0 +1,19 @@
+package com.hako.base.domain.database.entities
+
+import androidx.room.Entity
+import androidx.room.Index
+import androidx.room.PrimaryKey
+
+@Entity(tableName = PhotoEntity.TABLE_NAME, indices = [Index(value = ["id"], unique = true)])
+data class PhotoEntity(
+ @PrimaryKey
+ val id: Int,
+ val albumId: Int,
+ val title: String,
+ val photoUrl: String,
+ val thumbnailUrl: String
+) {
+ companion object {
+ const val TABLE_NAME = "photos"
+ }
+}
\ No newline at end of file
diff --git a/base/src/main/java/com/hako/base/domain/database/entities/User.kt b/base/src/main/java/com/hako/base/domain/database/entities/User.kt
new file mode 100644
index 0000000..c359911
--- /dev/null
+++ b/base/src/main/java/com/hako/base/domain/database/entities/User.kt
@@ -0,0 +1,20 @@
+package com.hako.base.domain.database.entities
+
+import androidx.room.Entity
+import androidx.room.Index
+import androidx.room.PrimaryKey
+
+@Entity(tableName = UserEntity.TABLE_NAME, indices = [Index(value = ["id"], unique = true)])
+data class UserEntity(
+ @PrimaryKey
+ val id: Int,
+ val realName: String,
+ val userName: String,
+ val email: String,
+ val phone: String,
+ val website: String
+) {
+ companion object {
+ const val TABLE_NAME = "users"
+ }
+}
\ No newline at end of file
diff --git a/base/src/main/java/com/hako/base/domain/network/RemoteClient.kt b/base/src/main/java/com/hako/base/domain/network/RemoteClient.kt
new file mode 100644
index 0000000..e24863d
--- /dev/null
+++ b/base/src/main/java/com/hako/base/domain/network/RemoteClient.kt
@@ -0,0 +1,35 @@
+package com.hako.base.domain.network
+
+import com.hako.base.BuildConfig
+import okhttp3.OkHttpClient
+import okhttp3.logging.HttpLoggingInterceptor
+import retrofit2.Retrofit
+import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
+import retrofit2.converter.gson.GsonConverterFactory
+import timber.log.Timber
+
+class RemoteClient(endpoint: String) {
+ private val logger = HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger {
+ override fun log(message: String) {
+ Timber.d(message)
+ }
+ }).setLevel(getLoggerLevel())
+
+ private val client = OkHttpClient.Builder()
+ .addInterceptor(logger)
+ .build()
+
+ private val retrofit: Retrofit = Retrofit.Builder()
+ .baseUrl(endpoint)
+ .addConverterFactory(GsonConverterFactory.create())
+ .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
+ .client(client)
+ .build()
+
+ fun getClient(api: Class): T = retrofit.create(api)
+
+ private fun getLoggerLevel() = when (BuildConfig.DEBUG) {
+ true -> HttpLoggingInterceptor.Level.BASIC
+ false -> HttpLoggingInterceptor.Level.NONE
+ }
+}
\ No newline at end of file
diff --git a/base/src/main/java/com/hako/base/domain/network/RequestStatus.kt b/base/src/main/java/com/hako/base/domain/network/RequestStatus.kt
new file mode 100644
index 0000000..d34b084
--- /dev/null
+++ b/base/src/main/java/com/hako/base/domain/network/RequestStatus.kt
@@ -0,0 +1,7 @@
+package com.hako.base.domain.network
+
+sealed class RequestStatus {
+ object Ready : RequestStatus()
+ object Loading : RequestStatus()
+ object Errored : RequestStatus()
+}
diff --git a/base/src/main/java/com/hako/base/extensions/ApplicationExtension.kt b/base/src/main/java/com/hako/base/extensions/ApplicationExtension.kt
new file mode 100644
index 0000000..cd7e477
--- /dev/null
+++ b/base/src/main/java/com/hako/base/extensions/ApplicationExtension.kt
@@ -0,0 +1,8 @@
+package com.hako.base.extensions
+
+import android.content.Context
+import android.widget.Toast
+
+fun Context.toast(message: String) {
+ Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
+}
\ No newline at end of file
diff --git a/base/src/main/java/com/hako/base/extensions/ListExtensions.kt b/base/src/main/java/com/hako/base/extensions/ListExtensions.kt
new file mode 100644
index 0000000..61a3ded
--- /dev/null
+++ b/base/src/main/java/com/hako/base/extensions/ListExtensions.kt
@@ -0,0 +1,23 @@
+package com.hako.base.extensions
+
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.RecyclerView
+
+fun RecyclerView.Adapter<*>.autoNotify(oldList: List, newList: List, compare: (T, T) -> Boolean) {
+ val diff = DiffUtil.calculateDiff(object : DiffUtil.Callback() {
+
+ override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
+ return compare(oldList[oldItemPosition], newList[newItemPosition])
+ }
+
+ override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
+ return oldList[oldItemPosition] == newList[newItemPosition]
+ }
+
+ override fun getOldListSize() = oldList.size
+
+ override fun getNewListSize() = newList.size
+ })
+
+ diff.dispatchUpdatesTo(this)
+}
\ No newline at end of file
diff --git a/base/src/main/java/com/hako/base/extensions/LiveDataExtensions.kt b/base/src/main/java/com/hako/base/extensions/LiveDataExtensions.kt
new file mode 100644
index 0000000..8aa5111
--- /dev/null
+++ b/base/src/main/java/com/hako/base/extensions/LiveDataExtensions.kt
@@ -0,0 +1,13 @@
+package com.hako.base.extensions
+
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.Observer
+
+fun LiveData.observeNonNull(owner: LifecycleOwner, func: (T) -> Unit) {
+ observe(owner, Observer {
+ it?.let {
+ func(it)
+ }
+ })
+}
\ No newline at end of file
diff --git a/base/src/main/java/com/hako/base/extensions/NavigationExtensions.kt b/base/src/main/java/com/hako/base/extensions/NavigationExtensions.kt
new file mode 100644
index 0000000..0045856
--- /dev/null
+++ b/base/src/main/java/com/hako/base/extensions/NavigationExtensions.kt
@@ -0,0 +1,19 @@
+package com.hako.base.extensions
+
+import androidx.annotation.IdRes
+import androidx.appcompat.app.AppCompatActivity
+import androidx.fragment.app.Fragment
+import androidx.navigation.fragment.NavHostFragment
+
+fun AppCompatActivity.findNavHostFragment(@IdRes id: Int) =
+ supportFragmentManager.findFragmentById(id) as NavHostFragment
+
+fun Fragment.findNavHostFragment(@IdRes id: Int) =
+ childFragmentManager.findFragmentById(id) as NavHostFragment
+
+fun Fragment.findNavController(@IdRes id: Int) =
+ androidx.navigation.Navigation.findNavController(view?.findViewById(id) ?: viewNotFound(id, this))
+
+private fun viewNotFound(@IdRes id: Int, fragment: Fragment): Nothing = throw IllegalStateException(
+ "View ID $id at '${fragment::class.java.simpleName}' not found."
+)
\ No newline at end of file
diff --git a/base/src/main/java/com/hako/base/extensions/ViewExtensions.kt b/base/src/main/java/com/hako/base/extensions/ViewExtensions.kt
new file mode 100644
index 0000000..39105ff
--- /dev/null
+++ b/base/src/main/java/com/hako/base/extensions/ViewExtensions.kt
@@ -0,0 +1,39 @@
+package com.hako.base.extensions
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.annotation.LayoutRes
+
+fun View.enable() {
+ isEnabled = true
+}
+
+fun View.disable() {
+ isEnabled = false
+}
+
+fun View.visible() {
+ visibility = View.VISIBLE
+}
+
+fun View.invisible() {
+ visibility = View.INVISIBLE
+}
+
+fun View.transparent() {
+ alpha = 0f
+}
+
+fun View.opaque() {
+ alpha = 1f
+}
+
+fun View.gone() {
+ visibility = View.GONE
+}
+
+fun ViewGroup.inflate(@LayoutRes layout: Int, attachToRoot: Boolean = false): View =
+ LayoutInflater
+ .from(context)
+ .inflate(layout, this, attachToRoot)
diff --git a/base/src/main/java/com/hako/base/widgets/LikeButton.kt b/base/src/main/java/com/hako/base/widgets/LikeButton.kt
new file mode 100644
index 0000000..357dbe1
--- /dev/null
+++ b/base/src/main/java/com/hako/base/widgets/LikeButton.kt
@@ -0,0 +1,46 @@
+package com.hako.base.widgets
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.FrameLayout
+import com.hako.base.R
+import com.hako.base.extensions.inflate
+import kotlinx.android.synthetic.main.like_button.view.*
+
+private const val LIKE_MIN_FRAME = 0
+private const val LIKE_MAX_FRAME = 28
+private const val LIKE_ANIM_SPEED = 1f
+private const val DISLIKE_MIN_FRAME = 28
+private const val DISLIKE_MAX_FRAME = 70
+private const val DISLIKE_ANIM_SPEED = 2f
+
+class LikeButton @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0
+) : FrameLayout(context, attrs, defStyleAttr) {
+
+ init {
+ inflate(R.layout.like_button, true)
+ }
+
+ fun like() {
+ like_button_animation_view.frame = LIKE_MAX_FRAME
+ }
+
+ fun dislike() {
+ like_button_animation_view.frame = LIKE_MIN_FRAME
+ }
+
+ fun playLike() {
+ like_button_animation_view.setMinAndMaxFrame(LIKE_MIN_FRAME, LIKE_MAX_FRAME)
+ like_button_animation_view.speed = LIKE_ANIM_SPEED
+ like_button_animation_view.playAnimation()
+ }
+
+ fun playDislike() {
+ like_button_animation_view.setMinAndMaxFrame(DISLIKE_MIN_FRAME, DISLIKE_MAX_FRAME)
+ like_button_animation_view.speed = DISLIKE_ANIM_SPEED
+ like_button_animation_view.playAnimation()
+ }
+}
\ No newline at end of file
diff --git a/base/src/main/java/com/hako/base/widgets/LoadingOverlay.kt b/base/src/main/java/com/hako/base/widgets/LoadingOverlay.kt
new file mode 100644
index 0000000..8f021f6
--- /dev/null
+++ b/base/src/main/java/com/hako/base/widgets/LoadingOverlay.kt
@@ -0,0 +1,18 @@
+package com.hako.base.widgets
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.FrameLayout
+import com.hako.base.R
+import com.hako.base.extensions.inflate
+
+class LoadingOverlay @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0
+) : FrameLayout(context, attrs, defStyleAttr) {
+
+ init {
+ inflate(R.layout.loading_overlay, true)
+ }
+}
\ No newline at end of file
diff --git a/base/src/main/java/com/hako/base/widgets/NetworkErrorOverlay.kt b/base/src/main/java/com/hako/base/widgets/NetworkErrorOverlay.kt
new file mode 100644
index 0000000..01b6cc2
--- /dev/null
+++ b/base/src/main/java/com/hako/base/widgets/NetworkErrorOverlay.kt
@@ -0,0 +1,18 @@
+package com.hako.base.widgets
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.FrameLayout
+import com.hako.base.R
+import com.hako.base.extensions.inflate
+
+class NetworkErrorOverlay @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0
+) : FrameLayout(context, attrs, defStyleAttr) {
+
+ init {
+ inflate(R.layout.network_error_overlay, true)
+ }
+}
\ No newline at end of file
diff --git a/base/src/main/res/drawable/bg_card.xml b/base/src/main/res/drawable/bg_card.xml
new file mode 100644
index 0000000..a7029e8
--- /dev/null
+++ b/base/src/main/res/drawable/bg_card.xml
@@ -0,0 +1,9 @@
+
+
+ -
+
+
+
+
+
+
\ No newline at end of file
diff --git a/base/src/main/res/layout/like_button.xml b/base/src/main/res/layout/like_button.xml
new file mode 100644
index 0000000..29c09f0
--- /dev/null
+++ b/base/src/main/res/layout/like_button.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/base/src/main/res/layout/loading_overlay.xml b/base/src/main/res/layout/loading_overlay.xml
new file mode 100644
index 0000000..b9d5a46
--- /dev/null
+++ b/base/src/main/res/layout/loading_overlay.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/base/src/main/res/layout/network_error_overlay.xml b/base/src/main/res/layout/network_error_overlay.xml
new file mode 100644
index 0000000..72ee319
--- /dev/null
+++ b/base/src/main/res/layout/network_error_overlay.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/base/src/main/res/raw/like_animation.json b/base/src/main/res/raw/like_animation.json
new file mode 100644
index 0000000..28a586a
--- /dev/null
+++ b/base/src/main/res/raw/like_animation.json
@@ -0,0 +1 @@
+{"v":"5.5.1","fr":30,"ip":0,"op":70,"w":160,"h":160,"nm":"Love and Unlove","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"crack-L/love Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[79,127,0],"ix":2},"a":{"a":0,"k":[99,147,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[9.19,3.12],[-4.54,-21.16],[-6.73,-7.61],[-14.68,-9.59],[-0.53,-0.35],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[-4.52,-8.61],[-21.01,-7.14],[2.14,9.93],[11.62,13.14],[0.32,0.21],[0,0],[0,0],[0,0]],"v":[[15.875,30.375],[40.875,21.375],[8.875,8.375],[34.875,-4.625],[11.875,-13.625],[20.875,-24.135],[-0.305,-43.235],[-36.335,-10.415],[-21.345,15.735],[18.745,49.565],[19.875,50.375],[19.875,43.375],[24.875,37.375]],"c":true}]},{"t":16,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[9.19,3.12],[-4.54,-21.16],[-6.73,-7.61],[-14.68,-9.59],[-0.53,-0.35],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[-4.52,-8.61],[-21.01,-7.14],[2.14,9.93],[11.62,13.14],[0.32,0.21],[0,0],[0,0],[0,0]],"v":[[15.875,30.375],[24.494,20.732],[8.875,8.375],[22.795,-3.103],[11.875,-13.625],[20.875,-24.135],[-0.305,-43.235],[-36.335,-10.415],[-21.345,15.735],[18.745,49.565],[19.875,50.375],[19.875,43.375],[24.875,37.375]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.83,0.18,0.18,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[98.568,146.527],"ix":2},"a":{"a":0,"k":[19.443,49.902],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[-5]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":1,"s":[-10.469]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":2,"s":[-5.938]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3,"s":[-9.942]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":4,"s":[-4.716]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[-8.401]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":6,"s":[-4.873]},{"t":19,"s":[-15]}],"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":0,"s":[100]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":15,"s":[100]},{"t":26,"s":[1]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"crack-R/love Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[79,127,0],"ix":2},"a":{"a":0,"k":[99,147,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[{"i":[[4.55,-21.16],[6.729,-7.61],[14.68,-9.59],[0,0],[0.32,0.21],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-1.04,1.44],[-9.229,3.13]],"o":[[-2.13,9.93],[-11.611,13.14],[-0.51,0.33],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[4.51,-8.64],[21.01,-7.14]],"v":[[32.365,-10.715],[17.375,15.435],[-22.715,49.265],[-24.875,50.675],[-25.435,50.315],[-25.915,43.075],[-20.915,37.075],[-29.915,30.075],[-4.915,21.075],[-36.915,8.075],[-10.915,-4.925],[-33.915,-13.925],[-24.875,-24.365],[-3.656,-43.535]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":5,"s":[{"i":[[4.55,-21.16],[6.729,-7.61],[14.68,-9.59],[0,0],[0.32,0.21],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-1.592,2.962],[-9.229,3.13]],"o":[[-2.13,9.93],[-11.611,13.14],[-0.51,0.33],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[3.978,-7.912],[21.01,-7.14]],"v":[[32.365,-10.715],[17.375,15.435],[-22.715,49.265],[-24.875,50.675],[-25.435,50.315],[-25.915,43.075],[-20.915,37.075],[-27.419,29.73],[-6.748,20.272],[-33.29,9.53],[-11.387,-3.666],[-28.853,-14.095],[-24.875,-24.365],[-3.656,-43.535]],"c":true}]},{"t":16,"s":[{"i":[[4.55,-21.16],[6.729,-7.61],[14.68,-9.59],[0,0],[0.32,0.21],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0.152,-0.028],[-2.807,6.31],[-9.229,3.13]],"o":[[-2.13,9.93],[-11.611,13.14],[-0.51,0.33],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0.026,-3.877],[2.807,-6.31],[21.01,-7.14]],"v":[[32.365,-10.715],[17.375,15.435],[-22.715,49.265],[-24.875,50.675],[-25.435,50.315],[-25.915,43.075],[-20.915,37.075],[-21.929,28.97],[-10.78,18.505],[-25.315,12.731],[-12.424,-0.897],[-28.309,-12.321],[-24.875,-24.365],[-3.656,-43.535]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.83,0.18,0.18,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[98.993,147.034],"to":[0,0],"ti":[0,0]},{"t":14,"s":[98.993,147.034]}],"ix":2},"a":{"a":0,"k":[-25.922,50.109],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":1,"s":[10.938]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":2,"s":[0.208]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3,"s":[4.265]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":4,"s":[0.091]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[9.333]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":6,"s":[4.848]},{"t":16,"s":[15]}],"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":0,"s":[100]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":15,"s":[100]},{"t":26,"s":[1]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"love/love disable","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":7,"s":[34]},{"t":14,"s":[99]}],"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[-5]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3,"s":[5]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":6,"s":[-5]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":9,"s":[-3]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":11,"s":[3]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":13,"s":[-2]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":15,"s":[2]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":17,"s":[-1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":19,"s":[1]},{"t":21,"s":[0]}],"ix":10},"p":{"a":0,"k":[80,127.607,0],"ix":2},"a":{"a":0,"k":[99.038,147.6,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6,"x":"var $bm_rt;\nvar maxDev, spd, decay, t, x, y;\nmaxDev = 10;\nspd = 30;\ndecay = 3;\nt = $bm_sub(time, inPoint);\nx = $bm_sum($bm_transform.scale[0], $bm_div($bm_mul(maxDev, Math.sin($bm_mul(spd, t))), Math.exp($bm_mul(decay, t))));\ny = $bm_div($bm_mul($bm_transform.scale[0], $bm_transform.scale[1]), x);\n$bm_rt = [\n x,\n y\n];"}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.509,0.333],[11.614,13.14],[2.134,9.936],[-21.012,-7.139],[-4.515,-8.635],[-9.227,3.135],[4.543,-21.158],[6.73,-7.616],[14.683,-9.592]],"o":[[0,0],[-14.682,-9.592],[-6.73,-7.616],[-4.542,-21.158],[9.227,3.135],[4.515,-8.635],[21.011,-7.139],[-2.133,9.936],[-11.614,13.14],[-0.508,0.333]],"v":[[0,50.676],[-2.165,49.266],[-42.255,15.439],[-57.246,-10.717],[-21.221,-43.537],[0,-24.365],[21.221,-43.537],[57.245,-10.717],[42.255,15.439],[2.164,49.266]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.793550848961,0.793550848961,0.793550848961,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[99.038,108.457],"ix":2},"a":{"a":0,"k":[0,11.274],"ix":1},"s":{"a":0,"k":[100,100],"ix":3,"x":"var $bm_rt;\nvar maxDev, spd, decay, t, x, y;\nmaxDev = 10;\nspd = 30;\ndecay = 6;\nt = $bm_sub(time, inPoint);\nx = $bm_sum($bm_transform.scale[0], $bm_div($bm_mul(maxDev, Math.sin($bm_mul(spd, t))), Math.exp($bm_mul(decay, t))));\ny = $bm_div($bm_mul($bm_transform.scale[0], $bm_transform.scale[1]), x);\n$bm_rt = [\n x,\n y\n];"},"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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"bm":0}]},{"id":"comp_1","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"love/love enable","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[80,98.107,0],"ix":2},"a":{"a":0,"k":[99.038,117.735,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6,"x":"var $bm_rt;\nvar maxDev, spd, decay, t, x, y;\nmaxDev = 10;\nspd = 40;\ndecay = 4.5;\nt = $bm_sub(time, inPoint);\nx = $bm_sum($bm_transform.scale[0], $bm_div($bm_mul(maxDev, Math.sin($bm_mul(spd, t))), Math.exp($bm_mul(decay, t))));\ny = $bm_div($bm_mul($bm_transform.scale[0], $bm_transform.scale[1]), x);\n$bm_rt = [\n x,\n y\n];"}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.509,0.333],[11.614,13.14],[2.134,9.936],[-21.012,-7.139],[-4.515,-8.635],[-9.227,3.135],[4.543,-21.158],[6.73,-7.616],[14.683,-9.592]],"o":[[0,0],[-14.682,-9.592],[-6.73,-7.616],[-4.542,-21.158],[9.227,3.135],[4.515,-8.635],[21.011,-7.139],[-2.133,9.936],[-11.614,13.14],[-0.508,0.333]],"v":[[0,50.676],[-2.165,49.266],[-42.255,15.439],[-57.246,-10.717],[-21.221,-43.537],[0,-24.365],[21.221,-43.537],[57.245,-10.717],[42.255,15.439],[2.164,49.266]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0.793550848961,0.793550848961,0.793550848961,1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":1,"s":[0.792156875134,0.792156875134,0.792156875134,1]},{"t":2,"s":[0.798314929008,0.04177454859,0.258352786303,1]}],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[99.038,91.234],"ix":2},"a":{"a":0,"k":[0,-5.949],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":0,"s":[100,100]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":1,"s":[107,107]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":2,"s":[91,91]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":4,"s":[107,107]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":6,"s":[91,91]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":9,"s":[107,107]},{"t":11,"s":[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":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"dislike","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[80,80,0],"ix":2},"a":{"a":0,"k":[80,80,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":160,"h":160,"ip":30,"op":90,"st":30,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"like","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[80,80,0],"ix":2},"a":{"a":0,"k":[80,80,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":160,"h":160,"ip":0,"op":30,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/base/src/main/res/raw/loading_animation.json b/base/src/main/res/raw/loading_animation.json
new file mode 100644
index 0000000..ad2269b
--- /dev/null
+++ b/base/src/main/res/raw/loading_animation.json
@@ -0,0 +1 @@
+{"v":"5.1.6","fr":30,"ip":0,"op":94,"w":300,"h":300,"nm":"Comp 2","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[81,59.26,0],"ix":2},"a":{"a":0,"k":[-30,-6.544,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.651,0.667,0.667],"y":[0.998,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"n":["0p651_0p998_0p333_0","0p667_1_0p333_0","0p667_1_0p333_0"],"t":9,"s":[0,75.476,100],"e":[110,75.476,100]},{"i":{"x":[0.524,0.833,0.833],"y":[0.97,1,1]},"o":{"x":[0.379,0.167,0.167],"y":[0.013,0,0]},"n":["0p524_0p97_0p379_0p013","0p833_1_0p167_0","0p833_1_0p167_0"],"t":21,"s":[110,75.476,100],"e":[100,75.476,100]},{"t":29}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[85.26,14.271],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":2,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.792156862745098,0.792156862745098,0.792156862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[12.63,-8.364],"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 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":118,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[81,41.26,0],"ix":2},"a":{"a":0,"k":[-30,-6.544,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.651,0.667,0.667],"y":[0.997,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"n":["0p651_0p997_0p333_0","0p667_1_0p333_0","0p667_1_0p333_0"],"t":3,"s":[0,75.476,100],"e":[90,75.476,100]},{"i":{"x":[0.524,0.833,0.833],"y":[0.94,1,1]},"o":{"x":[0.379,0.167,0.167],"y":[0.027,0,0]},"n":["0p524_0p94_0p379_0p027","0p833_1_0p167_0","0p833_1_0p167_0"],"t":15,"s":[90,75.476,100],"e":[85,75.476,100]},{"t":23}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[85.26,14.271],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":2,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.8549019607843137,0.8549019607843137,0.8549019607843137,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[12.63,-8.364],"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 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":166,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[48.961,49.211,0],"ix":2},"a":{"a":0,"k":[-66.789,-32.789,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.044,0.044,0.667],"y":[0.991,0.991,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"n":["0p044_0p991_0p333_0","0p044_0p991_0p333_0","0p667_1_0p333_0"],"t":0,"s":[0,0,100],"e":[93,93,100]},{"t":12}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[38.422,38.422],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":4,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.7294117647058823,0.7294117647058823,0.7294117647058823,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-66.789,-32.789],"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 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":166,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Comp 1","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,175,0],"ix":2},"a":{"a":0,"k":[100,50,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":200,"h":100,"ip":62,"op":152,"st":62,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"Comp 1","refId":"comp_0","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":62,"s":[100],"e":[60]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":76,"s":[60],"e":[60]},{"t":94}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.182,"y":1},"o":{"x":0.333,"y":0},"n":"0p182_1_0p333_0","t":62,"s":[150,175,0],"e":[150,123.5,0],"to":[0,-8.58333301544189,0],"ti":[0,8.58333301544189,0]},{"i":{"x":0.182,"y":0.182},"o":{"x":0.167,"y":0.167},"n":"0p182_0p182_0p167_0p167","t":76,"s":[150,123.5,0],"e":[150,123.5,0],"to":[0,0,0],"ti":[0,0,0]},{"t":94}],"ix":2},"a":{"a":0,"k":[100,50,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.069,0.069,0.667],"y":[0.995,0.995,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"n":["0p069_0p995_0p333_0","0p069_0p995_0p333_0","0p667_1_0p333_0"],"t":62,"s":[100,100,100],"e":[80,80,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"n":["0p833_1_0p333_0","0p833_1_0p333_0","0p833_1_0p333_0"],"t":76,"s":[80,80,100],"e":[80,80,100]},{"t":94}],"ix":6}},"ao":0,"w":200,"h":100,"ip":30,"op":120,"st":30,"bm":0},{"ddd":0,"ind":3,"ty":0,"nm":"Comp 1","refId":"comp_0","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":30,"s":[100],"e":[60]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":44,"s":[60],"e":[60]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":62,"s":[60],"e":[0]},{"t":76}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.182,"y":1},"o":{"x":0.333,"y":0},"n":"0p182_1_0p333_0","t":30,"s":[150,175,0],"e":[150,123.5,0],"to":[0,-8.58333301544189,0],"ti":[0,8.58333301544189,0]},{"i":{"x":0.182,"y":0.182},"o":{"x":0.167,"y":0.167},"n":"0p182_0p182_0p167_0p167","t":44,"s":[150,123.5,0],"e":[150,123.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.182,"y":1},"o":{"x":0.167,"y":0},"n":"0p182_1_0p167_0","t":62,"s":[150,123.5,0],"e":[150,86.5,0],"to":[0,-6.16666650772095,0],"ti":[0,6.16666650772095,0]},{"t":76}],"ix":2},"a":{"a":0,"k":[100,50,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.069,0.069,0.667],"y":[0.995,0.995,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"n":["0p069_0p995_0p333_0","0p069_0p995_0p333_0","0p667_1_0p333_0"],"t":30,"s":[100,100,100],"e":[80,80,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"n":["0p833_1_0p333_0","0p833_1_0p333_0","0p833_1_0p333_0"],"t":44,"s":[80,80,100],"e":[80,80,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"n":["0p833_1_0p167_0","0p833_1_0p167_0","0p833_1_0p167_0"],"t":62,"s":[80,80,100],"e":[50,50,100]},{"t":76}],"ix":6}},"ao":0,"w":200,"h":100,"ip":-2,"op":88,"st":-2,"bm":0},{"ddd":0,"ind":4,"ty":0,"nm":"Comp 1","refId":"comp_0","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":-1,"s":[100],"e":[60]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":13,"s":[60],"e":[60]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":31,"s":[60],"e":[0]},{"t":45}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.182,"y":1},"o":{"x":0.333,"y":0},"n":"0p182_1_0p333_0","t":-1,"s":[150,175,0],"e":[150,123.5,0],"to":[0,-8.58333301544189,0],"ti":[0,8.58333301544189,0]},{"i":{"x":0.182,"y":0.182},"o":{"x":0.167,"y":0.167},"n":"0p182_0p182_0p167_0p167","t":13,"s":[150,123.5,0],"e":[150,123.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.182,"y":1},"o":{"x":0.167,"y":0},"n":"0p182_1_0p167_0","t":31,"s":[150,123.5,0],"e":[150,86.5,0],"to":[0,-6.16666650772095,0],"ti":[0,6.16666650772095,0]},{"t":45}],"ix":2},"a":{"a":0,"k":[100,50,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.069,0.069,0.667],"y":[0.995,0.995,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"n":["0p069_0p995_0p333_0","0p069_0p995_0p333_0","0p667_1_0p333_0"],"t":-1,"s":[100,100,100],"e":[80,80,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"n":["0p833_1_0p333_0","0p833_1_0p333_0","0p833_1_0p333_0"],"t":13,"s":[80,80,100],"e":[80,80,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"n":["0p833_1_0p167_0","0p833_1_0p167_0","0p833_1_0p167_0"],"t":31,"s":[80,80,100],"e":[50,50,100]},{"t":45}],"ix":6}},"ao":0,"w":200,"h":100,"ip":-33,"op":57,"st":-33,"bm":0},{"ddd":0,"ind":5,"ty":0,"nm":"Comp 1","refId":"comp_0","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":-35,"s":[100],"e":[60]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":-21,"s":[60],"e":[60]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":-1,"s":[60],"e":[0]},{"t":13}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.182,"y":1},"o":{"x":0.333,"y":0},"n":"0p182_1_0p333_0","t":-35,"s":[150,175,0],"e":[150,123.5,0],"to":[0,-8.58333301544189,0],"ti":[0,8.58333301544189,0]},{"i":{"x":0.182,"y":0.182},"o":{"x":0.167,"y":0.167},"n":"0p182_0p182_0p167_0p167","t":-21,"s":[150,123.5,0],"e":[150,123.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.182,"y":1},"o":{"x":0.167,"y":0},"n":"0p182_1_0p167_0","t":-1,"s":[150,123.5,0],"e":[150,86.5,0],"to":[0,-6.16666650772095,0],"ti":[0,6.16666650772095,0]},{"t":13}],"ix":2},"a":{"a":0,"k":[100,50,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.069,0.069,0.667],"y":[0.995,0.995,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"n":["0p069_0p995_0p333_0","0p069_0p995_0p333_0","0p667_1_0p333_0"],"t":-35,"s":[100,100,100],"e":[80,80,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"n":["0p833_1_0p333_0","0p833_1_0p333_0","0p833_1_0p333_0"],"t":-21,"s":[80,80,100],"e":[80,80,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"n":["0p833_1_0p167_0","0p833_1_0p167_0","0p833_1_0p167_0"],"t":-1,"s":[80,80,100],"e":[50,50,100]},{"t":13}],"ix":6}},"ao":0,"w":200,"h":100,"ip":-76,"op":14,"st":-76,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/base/src/main/res/raw/network_animation.json b/base/src/main/res/raw/network_animation.json
new file mode 100644
index 0000000..2f268ac
--- /dev/null
+++ b/base/src/main/res/raw/network_animation.json
@@ -0,0 +1 @@
+{"ip":0,"fr":60,"v":"5.1.20","assets":[{"id":"precomp_1","layers":[{"ty":4,"nm":"Fill-67-Copy","ip":0,"st":0,"ind":15,"hix":4,"ks":{"o":{"a":0,"k":100},"or":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[11.560674667358398,2.8008460998535156,0]},"p":{"s":true,"x":{"a":0,"k":136.78967466735838},"y":{"a":0,"k":59.93284609985351}},"rx":{"a":0,"k":0},"ry":{"a":0,"k":0},"rz":{"a":0,"k":0},"s":{"a":0,"k":[100,100,100]}},"shapes":[{"ty":"gr","nm":"Fill-67-Copy shape group","it":[{"ty":"sh","ks":{"a":0,"k":{"c":true,"v":[[46.7294343,62.2339406],[69.8507811,62.2339406],[69.8507811,56.6322499],[46.7294343,56.6322499]],"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]]}}},{"ty":"st","o":{"a":0,"k":0},"w":{"a":0,"k":0},"c":{"a":0,"k":[0,0,0,0]},"lc":3,"lj":1,"ml":1},{"ty":"fl","o":{"a":0,"k":100},"r":2,"c":{"a":0,"k":[0.21568627450980393,0.2784313725490196,0.30980392156862746,1]}},{"ty":"tr","o":{"a":0,"k":100},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[-171.958,-107.196]},"r":{"a":0,"k":0}}]}],"op":152},{"ty":4,"nm":"Fill-66-Copy","ip":0,"st":0,"ind":14,"hix":3,"ks":{"o":{"a":0,"k":100},"or":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[11.560674667358398,11.607135772705078,0]},"p":{"s":true,"x":{"a":0,"k":136.78967466735838},"y":{"a":0,"k":68.73913577270508}},"rx":{"a":0,"k":0},"ry":{"a":0,"k":0},"rz":{"a":0,"k":0},"s":{"a":0,"k":[100,100,100]}},"shapes":[{"ty":"gr","nm":"Fill-66-Copy shape group","it":[{"ty":"sh","ks":{"a":0,"k":{"c":true,"v":[[46.7294343,56.6322499],[69.8507811,56.6322499],[69.8507811,68.2393858],[58.2901077,79.8465217],[46.7294343,68.2393858]],"i":[[0,0],[0,0],[0,0],[6.384348499999994,0],[0,6.410007300000004]],"o":[[0,0],[0,0],[0,6.410007300000004],[-6.3851528,0],[0,0]]}}},{"ty":"st","o":{"a":0,"k":0},"w":{"a":0,"k":0},"c":{"a":0,"k":[0,0,0,0]},"lc":3,"lj":1,"ml":1},{"ty":"fl","o":{"a":0,"k":100},"r":2,"c":{"a":0,"k":[0.19607843137254902,0.25098039215686274,0.2784313725490196,1]}},{"ty":"tr","o":{"a":0,"k":100},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[-171.958,-107.196]},"r":{"a":0,"k":0}}]}],"op":152},{"ty":4,"nm":"Combined-Shape","ip":0,"st":0,"ind":12,"hix":2,"ks":{"o":{"a":0,"k":100},"or":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[8.197591781616211,3.335012435913086,0]},"p":{"s":true,"x":{"a":0,"k":136.8685917816162},"y":{"a":0,"k":53.899012435913086}},"rx":{"a":0,"k":0},"ry":{"a":0,"k":0},"rz":{"a":0,"k":0},"s":{"a":0,"k":[100,100,100]}},"shapes":[{"ty":"gr","nm":"Combined-Shape shape group","it":[{"ty":"sh","ks":{"a":0,"k":{"c":true,"v":[[53.977576,56.7339961],[50.1709343,56.7339961],[50.1709343,51.9745374],[52.0746573,50.0639709],[53.977576,51.9745374]],"i":[[0,0],[0,0],[0,0],[-1.051189700000002,0],[0,-1.0546069000000031]],"o":[[0,0],[0,0],[0,-1.0546069000000031],[1.0503854000000032,0],[0,0]]}}},{"ty":"sh","ks":{"a":0,"k":{"c":true,"v":[[66.5661151,56.7339961],[62.7602777,56.7339961],[62.7602777,51.9745374],[64.6631964,50.0639709],[66.5661151,51.9745374]],"i":[[0,0],[0,0],[0,0],[-1.051189700000002,0],[0,-1.0546069000000031]],"o":[[0,0],[0,0],[0,-1.0546069000000031],[1.050385300000002,0],[0,0]]}}},{"ty":"st","o":{"a":0,"k":0},"w":{"a":0,"k":0},"c":{"a":0,"k":[0,0,0,0]},"lc":3,"lj":1,"ml":1},{"ty":"fl","o":{"a":0,"k":100},"r":2,"c":{"a":0,"k":[0.796078431372549,0.8117647058823529,0.8196078431372549,1]}},{"ty":"tr","o":{"a":0,"k":100},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[-175.4,-100.628]},"r":{"a":0,"k":0}}]}],"op":152}]},{"id":"precomp_2","layers":[{"ty":4,"nm":"Fill-72-Copy","ip":0,"st":0,"ind":18,"hix":7,"ks":{"o":{"a":0,"k":100},"or":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[11.560673713684082,2.801248550415039,0]},"p":{"s":true,"x":{"a":0,"k":136.79},"y":{"a":0,"k":38.024}},"rx":{"a":0,"k":0},"ry":{"a":0,"k":0},"rz":{"a":0,"k":0},"s":{"a":0,"k":[100,100,100]}},"shapes":[{"ty":"gr","nm":"Fill-72-Copy shape group","it":[{"ty":"sh","ks":{"a":0,"k":{"c":true,"v":[[5.86197757e-14,40.3253897],[23.1213468,40.3253897],[23.1213468,34.7228916],[5.86197757e-14,34.7228916]],"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]]}}},{"ty":"st","o":{"a":0,"k":0},"w":{"a":0,"k":0},"c":{"a":0,"k":[0,0,0,0]},"lc":3,"lj":1,"ml":1},{"ty":"fl","o":{"a":0,"k":100},"r":2,"c":{"a":0,"k":[0.21568627450980393,0.2784313725490196,0.30980392156862746,1]}},{"ty":"tr","o":{"a":0,"k":100},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[-125.229,-52.181]},"r":{"a":0,"k":0}}]}],"op":152},{"ty":4,"nm":"Fill-71-Copy","ip":0,"st":0,"ind":17,"hix":6,"ks":{"o":{"a":0,"k":100},"or":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[11.560673713684082,11.607135772705078,0]},"p":{"s":true,"x":{"a":0,"k":136.79},"y":{"a":0,"k":29.065}},"rx":{"a":0,"k":0},"ry":{"a":0,"k":0},"rz":{"a":0,"k":0},"s":{"a":0,"k":[100,100,100]}},"shapes":[{"ty":"gr","nm":"Fill-71-Copy shape group","it":[{"ty":"sh","ks":{"a":0,"k":{"c":true,"v":[[23.1213468,40.171963],[5.86197757e-14,40.171963],[5.86197757e-14,28.5648271],[11.5606734,16.9576912],[23.1213468,28.5648271]],"i":[[0,0],[0,0],[0,0],[-6.38515277,0],[0,-6.410814799999997]],"o":[[0,0],[0,0],[0,-6.410814799999997],[6.3843485,0],[0,0]]}}},{"ty":"st","o":{"a":0,"k":0},"w":{"a":0,"k":0},"c":{"a":0,"k":[0,0,0,0]},"lc":3,"lj":1,"ml":1},{"ty":"fl","o":{"a":0,"k":100},"r":2,"c":{"a":0,"k":[0.19607843137254902,0.25098039215686274,0.2784313725490196,1]}},{"ty":"tr","o":{"a":0,"k":100},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[-125.229,-34.416]},"r":{"a":0,"k":0}}]}],"op":152}]}],"layers":[{"ty":0,"nm":"instance:precomp_2","refId":"precomp_2","ind":15,"ip":0,"st":0,"hix":5,"ks":{"o":{"a":0,"k":100},"or":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[11.560673713684082,11.683692161560058,0]},"p":{"s":true,"x":{"a":0,"k":70.198},"y":{"a":1,"k":[{"t":0,"s":[38.142],"e":[38.142],"i":{"x":[0.515],"y":[0.955]},"o":{"x":[0.455],"y":[0.03]}},{"t":35,"s":[38.142],"e":[29.142],"i":{"x":[0.367],"y":[0.556]},"o":{"x":[0.4],"y":[2.5]}},{"t":50,"s":[29.142],"e":[28.987199999999998],"i":{"x":[0.565],"y":[1]},"o":{"x":[0.39],"y":[0.575]}},{"t":52,"s":[28.987199999999998],"e":[29.142],"i":{"x":[0.745],"y":[0.715]},"o":{"x":[0.47],"y":[0]}},{"t":56,"s":[29.142],"e":[29.1807],"i":{"x":[0.565],"y":[1]},"o":{"x":[0.39],"y":[0.575]}},{"t":58,"s":[29.1807],"e":[29.142],"i":{"x":[0.745],"y":[0.715]},"o":{"x":[0.47],"y":[0]}},{"t":65,"s":[29.142],"e":[29.142],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":127,"s":[29.142],"e":[37.142],"i":{"x":[0.515],"y":[0.955]},"o":{"x":[0.455],"y":[0.03]}},{"t":135,"s":[37.142],"e":[38.142],"i":{"x":[0.515],"y":[0.955]},"o":{"x":[0.455],"y":[0.03]}},{"t":152}]}},"rx":{"a":0,"k":0},"ry":{"a":0,"k":0},"rz":{"a":0,"k":0},"s":{"a":0,"k":[100,100,100]}},"w":23,"h":23,"op":152},{"ty":0,"nm":"instance:precomp_1","refId":"precomp_1","ind":14,"ip":0,"st":0,"hix":1,"ks":{"o":{"a":0,"k":100},"or":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[11.560674667358398,14.89113577270508,0]},"p":{"s":true,"x":{"a":0,"k":70.19767466735838},"y":{"a":1,"k":[{"t":0,"s":[57.455],"e":[57.455],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":35,"s":[57.455],"e":[65.31140939331054],"i":{"x":[0.367],"y":[0.556]},"o":{"x":[0.4],"y":[2.5]}},{"t":50,"s":[65.31140939331054],"e":[65.44653963487548],"i":{"x":[0.565],"y":[1]},"o":{"x":[0.39],"y":[0.575]}},{"t":52,"s":[65.44653963487548],"e":[65.31140939331054],"i":{"x":[0.745],"y":[0.715]},"o":{"x":[0.47],"y":[0]}},{"t":56,"s":[65.31140939331054],"e":[65.27762683291931],"i":{"x":[0.565],"y":[1]},"o":{"x":[0.39],"y":[0.575]}},{"t":58,"s":[65.27762683291931],"e":[65.31140939331054],"i":{"x":[0.745],"y":[0.715]},"o":{"x":[0.47],"y":[0]}},{"t":65,"s":[65.31140939331054],"e":[65.31140939331054],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":127,"s":[65.31140939331054],"e":[57.455],"i":{"x":[0.515],"y":[0.955]},"o":{"x":[0.455],"y":[0.03]}},{"t":135}]}},"rx":{"a":0,"k":0},"ry":{"a":0,"k":0},"rz":{"a":0,"k":0},"s":{"a":0,"k":[100,100,100]}},"w":23,"h":30,"op":152},{"ty":4,"nm":"Fill-70-Copy","ip":0,"st":0,"ind":13,"hix":8,"ks":{"o":{"a":0,"k":100},"or":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[1.608553409576416,9.685667991638184,0]},"p":{"s":true,"x":{"a":0,"k":69.897},"y":{"a":0,"k":18.053}},"rx":{"a":0,"k":0},"ry":{"a":0,"k":0},"rz":{"a":0,"k":0},"s":{"a":0,"k":[100,201.9]}},"shapes":[{"ty":"gr","nm":"Fill-70-Copy shape group","it":[{"ty":"sh","ks":{"a":0,"k":{"c":true,"v":[[9.65132051,19.3713359],[12.8684274,19.3713359],[12.8684274,-1.95399252e-13],[9.65132051,-1.95399252e-13]],"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]]}}},{"ty":"st","o":{"a":0,"k":0},"w":{"a":0,"k":0},"c":{"a":0,"k":[0,0,0,0]},"lc":3,"lj":1,"ml":1},{"ty":"fl","o":{"a":0,"k":100},"r":2,"c":{"a":0,"k":[0.23921568627450981,0.30980392156862746,0.44313725490196076,1]}},{"ty":"tr","o":{"a":0,"k":100},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[-9.651,0]},"r":{"a":0,"k":0}}]}],"op":152},{"ty":4,"nm":"Fill-65-Copy","ip":0,"st":0,"ind":12,"hix":9,"ks":{"o":{"a":0,"k":100},"or":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[1.6085529327392578,10.430999755859375,0]},"p":{"s":true,"x":{"a":0,"k":69.89655293273925},"y":{"a":0,"k":82.703}},"rx":{"a":0,"k":0},"ry":{"a":0,"k":0},"rz":{"a":0,"k":0},"s":{"a":0,"k":[100,175.29999999999998]}},"shapes":[{"ty":"gr","nm":"Fill-65-Copy shape group","it":[{"ty":"sh","ks":{"a":0,"k":{"c":true,"v":[[56.3807548,98.4894844],[59.5978616,98.4894844],[59.5978616,77.6274867],[56.3807548,77.6274867]],"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]]}}},{"ty":"st","o":{"a":0,"k":0},"w":{"a":0,"k":0},"c":{"a":0,"k":[0,0,0,0]},"lc":3,"lj":1,"ml":1},{"ty":"fl","o":{"a":0,"k":100},"r":2,"c":{"a":0,"k":[0.23921568627450981,0.30980392156862746,0.44313725490196076,1]}},{"ty":"tr","o":{"a":0,"k":100},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[-56.381,-77.627]},"r":{"a":0,"k":0}}]}],"op":152},{"ty":4,"nm":"Fill-80-Copy","ip":0,"st":0,"ind":11,"hix":10,"ks":{"o":{"a":0,"k":100},"or":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[6.337699890136719,6.3631744384765625,0]},"p":{"s":true,"x":{"a":0,"k":99.034},"y":{"a":0,"k":86.913}},"rx":{"a":0,"k":0},"ry":{"a":0,"k":0},"rz":{"a":1,"k":[{"t":0,"s":[0],"e":[183.34649444186343],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":152}]},"s":{"a":0,"k":[100,100,100]}},"shapes":[{"ty":"gr","nm":"Fill-80-Copy shape group","it":[{"ty":"sh","ks":{"a":0,"k":{"c":true,"v":[[90.3083635,86.412682],[93.4635411,83.2448238],[90.2818224,80.0495102],[87.1258406,83.218176],[83.970663,80.0495102],[80.7881401,83.2448238],[83.9441219,86.412682],[80.7881401,89.5813477],[83.970663,92.7758537],[87.1258406,89.6079955],[90.2818224,92.7758537],[93.4635411,89.5813477],[90.3083635,86.412682]],"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]]}}},{"ty":"st","o":{"a":0,"k":0},"w":{"a":0,"k":0},"c":{"a":0,"k":[0,0,0,0]},"lc":3,"lj":1,"ml":1},{"ty":"fl","o":{"a":0,"k":100},"r":2,"c":{"a":0,"k":[0.7843137254901961,0.796078431372549,0.8156862745098039,1]}},{"ty":"tr","o":{"a":0,"k":100},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[-80.788,-80.05]},"r":{"a":0,"k":0}}]}],"op":152},{"ty":4,"nm":"Fill-79-Copy","ip":0,"st":0,"ind":10,"hix":11,"ks":{"o":{"a":0,"k":100},"or":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[6.337700843811035,6.362766265869141,0]},"p":{"s":true,"x":{"a":0,"k":24.245999999999988},"y":{"a":0,"k":88.863}},"rx":{"a":0,"k":0},"ry":{"a":0,"k":0},"rz":{"a":1,"k":[{"t":0,"s":[0],"e":[94.53803619658584],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":152}]},"s":{"a":0,"k":[100,100,100]}},"shapes":[{"ty":"gr","nm":"Fill-79-Copy shape group","it":[{"ty":"sh","ks":{"a":0,"k":{"c":true,"v":[[15.5202234,88.3631718],[18.6754009,85.194506],[15.4936823,82],[12.3377005,85.1678582],[9.18252294,82],[6,85.194506],[9.15598181,88.3631718],[6,91.5302225],[9.18252294,94.7247285],[12.3377005,91.5576778],[15.4936823,94.725536],[18.6754009,91.5302225],[15.5202234,88.3631718]],"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]]}}},{"ty":"st","o":{"a":0,"k":0},"w":{"a":0,"k":0},"c":{"a":0,"k":[0,0,0,0]},"lc":3,"lj":1,"ml":1},{"ty":"fl","o":{"a":0,"k":100},"r":2,"c":{"a":0,"k":[0.7843137254901961,0.796078431372549,0.8156862745098039,1]}},{"ty":"tr","o":{"a":0,"k":100},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[-6,-82]},"r":{"a":0,"k":0}}]}],"op":152},{"ty":4,"nm":"Fill-78-Copy","ip":0,"st":0,"ind":9,"hix":12,"ks":{"o":{"a":0,"k":100},"or":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[6.337700843811035,6.363171577453613,0]},"p":{"s":true,"x":{"a":0,"k":19.041000000000004},"y":{"a":0,"k":21.235}},"rx":{"a":0,"k":0},"ry":{"a":0,"k":0},"rz":{"a":1,"k":[{"t":0,"s":[183.34649444186343],"e":[272.154952687141],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":152}]},"s":{"a":0,"k":[100,100,100]}},"shapes":[{"ty":"gr","nm":"Fill-78-Copy shape group","it":[{"ty":"sh","ks":{"a":0,"k":{"c":true,"v":[[10.3154041,20.7352648],[13.4705817,17.566599],[10.288863,14.372093],[7.13288119,17.5399512],[3.97770366,14.372093],[0.795180723,17.566599],[3.95116253,20.7352648],[0.795180723,23.903123],[3.97770366,27.0984365],[7.13288119,23.9297708],[10.288863,27.0984365],[13.4705817,23.903123],[10.3154041,20.7352648]],"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]]}}},{"ty":"st","o":{"a":0,"k":0},"w":{"a":0,"k":0},"c":{"a":0,"k":[0,0,0,0]},"lc":3,"lj":1,"ml":1},{"ty":"fl","o":{"a":0,"k":100},"r":2,"c":{"a":0,"k":[0.7843137254901961,0.796078431372549,0.8156862745098039,1]}},{"ty":"tr","o":{"a":0,"k":100},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[-0.795,-14.372]},"r":{"a":0,"k":0}}]}],"op":152},{"ty":4,"nm":"Fill-77-Copy","ip":0,"st":0,"ind":8,"hix":13,"ks":{"o":{"a":1,"k":[{"t":0,"s":[0],"e":[0],"i":{"x":[0.515],"y":[0.955]},"o":{"x":[0.455],"y":[0.03]}},{"t":27,"s":[0],"e":[100],"i":{"x":[0.515],"y":[0.955]},"o":{"x":[0.455],"y":[0.03]}},{"t":30,"s":[100],"e":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":129,"s":[100],"e":[0],"i":{"x":[0.515],"y":[0.955]},"o":{"x":[0.455],"y":[0.03]}},{"t":134}]},"or":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[8.125606536865234,6.429790496826172,0]},"p":{"s":true,"x":{"a":0,"k":106.17299999999999},"y":{"a":0,"k":56.487}},"rx":{"a":0,"k":0},"ry":{"a":0,"k":0},"rz":{"a":0,"k":0},"s":{"a":0,"k":[100,100,100]}},"shapes":[{"ty":"gr","nm":"Fill-77-Copy shape group","it":[{"ty":"sh","ks":{"a":0,"k":{"c":true,"v":[[100.02483,53.7239059],[95.8063991,56.1682359],[102.390208,59.7253135],[97.7439017,62.4167414],[87.0976909,56.6559714],[91.7391718,53.9669661],[86.1389931,49.5571589],[100.02483,53.7239059]],"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]]}}},{"ty":"st","o":{"a":0,"k":0},"w":{"a":0,"k":0},"c":{"a":0,"k":[0,0,0,0]},"lc":3,"lj":1,"ml":1},{"ty":"fl","o":{"a":0,"k":100},"r":2,"c":{"a":0,"k":[0.9529411764705882,0.6666666666666666,0,1]}},{"ty":"tr","o":{"a":0,"k":100},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[-86.139,-49.557]},"r":{"a":0,"k":0}}]}],"op":152},{"ty":4,"nm":"Fill-76-Copy","ip":0,"st":0,"ind":7,"hix":14,"ks":{"o":{"a":1,"k":[{"t":0,"s":[0],"e":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":10,"s":[0],"e":[100],"i":{"x":[0.515],"y":[0.955]},"o":{"x":[0.455],"y":[0.03]}},{"t":19,"s":[100],"e":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":129,"s":[100],"e":[0],"i":{"x":[0.515],"y":[0.955]},"o":{"x":[0.455],"y":[0.03]}},{"t":134}]},"or":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[7,4.5,0]},"p":{"s":true,"x":{"a":0,"k":37.908},"y":{"a":0,"k":45}},"rx":{"a":0,"k":0},"ry":{"a":0,"k":0},"rz":{"a":0,"k":0},"s":{"a":0,"k":[100,100,100]}},"shapes":[{"ty":"gr","nm":"Fill-76-Copy shape group","it":[{"ty":"sh","ks":{"a":0,"k":{"c":true,"v":[[24.5935437,44.9333587],[19,42.9542509],[22.2243422,40],[31.2707297,43.2081586],[28.0501854,46.1589783],[33,49],[21.6654309,47.6151735]],"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]]}}},{"ty":"st","o":{"a":0,"k":0},"w":{"a":0,"k":0},"c":{"a":0,"k":[0,0,0,0]},"lc":3,"lj":1,"ml":1},{"ty":"fl","o":{"a":0,"k":100},"r":2,"c":{"a":0,"k":[0.9529411764705882,0.6666666666666666,0,1]}},{"ty":"tr","o":{"a":0,"k":100},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[-19,-40]},"r":{"a":0,"k":0}}]}],"op":152},{"ty":4,"nm":"Fill-75-Copy","ip":0,"st":0,"ind":6,"hix":15,"ks":{"o":{"a":1,"k":[{"t":0,"s":[0],"e":[0],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":3,"s":[0],"e":[1.5599999999999998],"i":{"x":[0.68],"y":[0.53]},"o":{"x":[0.55],"y":[0.085]}},{"t":4,"s":[1.5599999999999998],"e":[0],"i":{"x":[0.45],"y":[0.94]},"o":{"x":[0.25],"y":[0.46]}},{"t":5,"s":[0],"e":[6.25],"i":{"x":[0.68],"y":[0.53]},"o":{"x":[0.55],"y":[0.085]}},{"t":7,"s":[6.25],"e":[0],"i":{"x":[0.45],"y":[0.94]},"o":{"x":[0.25],"y":[0.46]}},{"t":10,"s":[0],"e":[25],"i":{"x":[0.68],"y":[0.53]},"o":{"x":[0.55],"y":[0.085]}},{"t":15,"s":[25],"e":[0],"i":{"x":[0.45],"y":[0.94]},"o":{"x":[0.25],"y":[0.46]}},{"t":20,"s":[0],"e":[100],"i":{"x":[0.68],"y":[0.53]},"o":{"x":[0.55],"y":[0.085]}},{"t":30,"s":[100],"e":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":129,"s":[100],"e":[0],"i":{"x":[0.515],"y":[0.955]},"o":{"x":[0.455],"y":[0.03]}},{"t":133}]},"or":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[7.5,10,0]},"p":{"s":true,"x":{"a":0,"k":44.117000000000004},"y":{"a":0,"k":13.921}},"rx":{"a":0,"k":0},"ry":{"a":0,"k":0},"rz":{"a":0,"k":0},"s":{"a":0,"k":[100,100,100]}},"shapes":[{"ty":"gr","nm":"Fill-75-Copy shape group","it":[{"ty":"sh","ks":{"a":0,"k":{"c":true,"v":[[24.7093689,11.181303],[30.4364682,11.0344267],[25.6228981,3.58373029],[31.929724,3.42099412],[39.7093689,15.4793309],[33.408674,15.6413775],[36.7289883,23.4209941],[24.7093689,11.181303]],"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]]}}},{"ty":"st","o":{"a":0,"k":0},"w":{"a":0,"k":0},"c":{"a":0,"k":[0,0,0,0]},"lc":3,"lj":1,"ml":1},{"ty":"fl","o":{"a":0,"k":100},"r":2,"c":{"a":0,"k":[0.9529411764705882,0.6666666666666666,0,1]}},{"ty":"tr","o":{"a":0,"k":100},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[-24.709,-3.421]},"r":{"a":0,"k":0}}]}],"op":152},{"ty":4,"nm":"Fill-74-Copy","ip":0,"st":0,"ind":5,"hix":16,"ks":{"o":{"a":1,"k":[{"t":0,"s":[0],"e":[0],"i":{"x":[0.515],"y":[0.955]},"o":{"x":[0.455],"y":[0.03]}},{"t":22,"s":[0],"e":[1.5599999999999998],"i":{"x":[0.68],"y":[0.53]},"o":{"x":[0.55],"y":[0.085]}},{"t":23,"s":[1.5599999999999998],"e":[0],"i":{"x":[0.45],"y":[0.94]},"o":{"x":[0.25],"y":[0.46]}},{"t":24,"s":[0],"e":[6.25],"i":{"x":[0.68],"y":[0.53]},"o":{"x":[0.55],"y":[0.085]}},{"t":26,"s":[6.25],"e":[0],"i":{"x":[0.45],"y":[0.94]},"o":{"x":[0.25],"y":[0.46]}},{"t":28,"s":[0],"e":[25],"i":{"x":[0.68],"y":[0.53]},"o":{"x":[0.55],"y":[0.085]}},{"t":32,"s":[25],"e":[0],"i":{"x":[0.45],"y":[0.94]},"o":{"x":[0.25],"y":[0.46]}},{"t":36,"s":[0],"e":[100],"i":{"x":[0.68],"y":[0.53]},"o":{"x":[0.55],"y":[0.085]}},{"t":44,"s":[100],"e":[100],"i":{"x":[0.515],"y":[0.955]},"o":{"x":[0.455],"y":[0.03]}},{"t":129,"s":[100],"e":[0],"i":{"x":[0.515],"y":[0.955]},"o":{"x":[0.455],"y":[0.03]}},{"t":134}]},"or":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[11.382123947143555,9.07196044921875,0]},"p":{"s":true,"x":{"a":0,"k":26.29},"y":{"a":0,"k":65.572}},"rx":{"a":0,"k":0},"ry":{"a":0,"k":0},"rz":{"a":0,"k":0},"s":{"a":0,"k":[100,100,100]}},"shapes":[{"ty":"gr","nm":"Fill-74-Copy shape group","it":[{"ty":"sh","ks":{"a":0,"k":{"c":true,"v":[[12.7389867,74.1439221],[11.995835,67.3939535],[3.81714514,73.8168809],[3,66.3837596],[16.2359818,56],[17.0531269,63.4266612],[25.764248,58.7342258],[12.7389867,74.1439221]],"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]]}}},{"ty":"st","o":{"a":0,"k":0},"w":{"a":0,"k":0},"c":{"a":0,"k":[0,0,0,0]},"lc":3,"lj":1,"ml":1},{"ty":"fl","o":{"a":0,"k":100},"r":2,"c":{"a":0,"k":[0.9529411764705882,0.6666666666666666,0,1]}},{"ty":"tr","o":{"a":0,"k":100},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[-3,-56]},"r":{"a":0,"k":0}}]}],"op":152},{"ty":4,"nm":"Fill-73-Copy","ip":0,"st":0,"ind":4,"hix":17,"ks":{"o":{"a":1,"k":[{"t":0,"s":[0],"e":[0],"i":{"x":[0.515],"y":[0.955]},"o":{"x":[0.455],"y":[0.03]}},{"t":15,"s":[0],"e":[1.5599999999999998],"i":{"x":[0.68],"y":[0.53]},"o":{"x":[0.55],"y":[0.085]}},{"t":16,"s":[1.5599999999999998],"e":[0],"i":{"x":[0.45],"y":[0.94]},"o":{"x":[0.25],"y":[0.46]}},{"t":17,"s":[0],"e":[6.25],"i":{"x":[0.68],"y":[0.53]},"o":{"x":[0.55],"y":[0.085]}},{"t":19,"s":[6.25],"e":[0],"i":{"x":[0.45],"y":[0.94]},"o":{"x":[0.25],"y":[0.46]}},{"t":21,"s":[0],"e":[25],"i":{"x":[0.68],"y":[0.53]},"o":{"x":[0.55],"y":[0.085]}},{"t":25,"s":[25],"e":[0],"i":{"x":[0.45],"y":[0.94]},"o":{"x":[0.25],"y":[0.46]}},{"t":29,"s":[0],"e":[100],"i":{"x":[0.68],"y":[0.53]},"o":{"x":[0.55],"y":[0.085]}},{"t":38,"s":[100],"e":[100],"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"t":129,"s":[100],"e":[0],"i":{"x":[0.515],"y":[0.955]},"o":{"x":[0.455],"y":[0.03]}},{"t":134}]},"or":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[10.698890686035156,9.536684036254883,0]},"p":{"s":true,"x":{"a":0,"k":100.70299999999999},"y":{"a":0,"k":25.877}},"rx":{"a":0,"k":0},"ry":{"a":0,"k":0},"rz":{"a":0,"k":0},"s":{"a":0,"k":[100,100,100]}},"shapes":[{"ty":"gr","nm":"Fill-73-Copy shape group","it":[{"ty":"sh","ks":{"a":0,"k":{"c":true,"v":[[96.8712615,27.6258868],[90.7643884,24.7035113],[99.4940078,19.0582152],[92.7702545,15.8402914],[78.654394,24.980487],[85.3717131,28.1959882],[78.096226,34.9136565],[96.8712615,27.6258868]],"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]]}}},{"ty":"st","o":{"a":0,"k":0},"w":{"a":0,"k":0},"c":{"a":0,"k":[0,0,0,0]},"lc":3,"lj":1,"ml":1},{"ty":"fl","o":{"a":0,"k":100},"r":2,"c":{"a":0,"k":[0.9529411764705882,0.6666666666666666,0,1]}},{"ty":"tr","o":{"a":0,"k":100},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[-78.096,-15.84]},"r":{"a":0,"k":0}}]}],"op":152},{"ty":4,"nm":"Fill-63-Copy","ip":0,"st":0,"ind":3,"hix":18,"ks":{"o":{"a":0,"k":100},"or":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[2.473957061767578,2.483494281768799,0]},"p":{"s":true,"x":{"a":0,"k":92.599},"y":{"a":0,"k":6.983}},"rx":{"a":0,"k":0},"ry":{"a":0,"k":0},"rz":{"a":0,"k":0},"s":{"a":1,"k":[{"t":0,"s":[100,100],"e":[120,120],"i":{"x":[0.515,0.515],"y":[0.955,0.955]},"o":{"x":[0.455,0.455],"y":[0.03,0.03]}},{"t":97,"s":[120,120],"e":[100,100],"i":{"x":[0.515,0.515],"y":[0.955,0.955]},"o":{"x":[0.455,0.455],"y":[0.03,0.03]}},{"t":152}]}},"shapes":[{"ty":"gr","nm":"Fill-63-Copy shape group","it":[{"ty":"sh","ks":{"a":0,"k":{"c":true,"v":[[83.1647778,6.4830905],[80.6900184,8.96698851],[78.2168675,6.4830905],[80.6900184,4]],"i":[[0,-1.37115046],[1.3672703999999953,0],[0,1.3719579699999995],[-1.3656619000000063,0]],"o":[[0,1.3719579699999995],[-1.3656619000000063,0],[0,-1.37115046],[1.3672703999999953,0]]}}},{"ty":"st","o":{"a":0,"k":0},"w":{"a":0,"k":0},"c":{"a":0,"k":[0,0,0,0]},"lc":3,"lj":1,"ml":1},{"ty":"fl","o":{"a":0,"k":100},"r":2,"c":{"a":0,"k":[0.24313725490196078,0.5137254901960784,0.7529411764705882,1]}},{"ty":"tr","o":{"a":0,"k":100},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[-78.217,-4]},"r":{"a":0,"k":0}}]}],"op":152},{"ty":4,"nm":"Fill-62-Copy","ip":0,"st":0,"ind":2,"hix":19,"ks":{"o":{"a":0,"k":100},"or":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[2.473957061767578,2.4834938049316406,0]},"p":{"s":true,"x":{"a":0,"k":132.069},"y":{"a":0,"k":74.472}},"rx":{"a":0,"k":0},"ry":{"a":0,"k":0},"rz":{"a":0,"k":0},"s":{"a":1,"k":[{"t":0,"s":[100,100],"e":[70,70],"i":{"x":[0.515,0.515],"y":[0.955,0.955]},"o":{"x":[0.455,0.455],"y":[0.03,0.03]}},{"t":51,"s":[70,70],"e":[100,100],"i":{"x":[0.515,0.515],"y":[0.955,0.955]},"o":{"x":[0.455,0.455],"y":[0.03,0.03]}},{"t":152}]}},"shapes":[{"ty":"gr","nm":"Fill-62-Copy shape group","it":[{"ty":"sh","ks":{"a":0,"k":{"c":true,"v":[[122.634657,73.9714626],[120.160702,76.4553606],[117.686747,73.9714626],[120.160702,71.4883721]],"i":[[0,-1.3711504999999988],[1.3664660000000026,0],[0,1.3719580000000065],[-1.3664660000000026,0]],"o":[[0,1.3719580000000065],[-1.3664660000000026,0],[0,-1.3711504999999988],[1.3664660000000026,0]]}}},{"ty":"st","o":{"a":0,"k":0},"w":{"a":0,"k":0},"c":{"a":0,"k":[0,0,0,0]},"lc":3,"lj":1,"ml":1},{"ty":"fl","o":{"a":0,"k":100},"r":2,"c":{"a":0,"k":[0.24313725490196078,0.5137254901960784,0.7529411764705882,1]}},{"ty":"tr","o":{"a":0,"k":100},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[-117.687,-71.488]},"r":{"a":0,"k":0}}]}],"op":152},{"ty":4,"nm":"Fill-61-Copy","ip":0,"st":0,"ind":1,"hix":20,"ks":{"o":{"a":0,"k":100},"or":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[2.473552703857422,2.4834938049316406,0]},"p":{"s":true,"x":{"a":0,"k":49.382000000000005},"y":{"a":0,"k":85.983}},"rx":{"a":0,"k":0},"ry":{"a":0,"k":0},"rz":{"a":0,"k":0},"s":{"a":1,"k":[{"t":0,"s":[100,100],"e":[150,150],"i":{"x":[0.515,0.515],"y":[0.955,0.955]},"o":{"x":[0.455,0.455],"y":[0.03,0.03]}},{"t":73,"s":[150,150],"e":[100,100],"i":{"x":[0.515,0.515],"y":[0.955,0.955]},"o":{"x":[0.455,0.455],"y":[0.03,0.03]}},{"t":152}]}},"shapes":[{"ty":"gr","nm":"Fill-61-Copy shape group","it":[{"ty":"sh","ks":{"a":0,"k":{"c":true,"v":[[39.947106,85.4830905],[37.4731509,87.9669885],[35,85.4830905],[37.4731509,83]],"i":[[0,-1.3711504999999988],[1.3664660999999967,0],[0,1.3719579999999922],[-1.3656618999999992,0]],"o":[[0,1.3719579999999922],[-1.3656618999999992,0],[0,-1.3711504999999988],[1.3664660999999967,0]]}}},{"ty":"st","o":{"a":0,"k":0},"w":{"a":0,"k":0},"c":{"a":0,"k":[0,0,0,0]},"lc":3,"lj":1,"ml":1},{"ty":"fl","o":{"a":0,"k":100},"r":2,"c":{"a":0,"k":[0.24313725490196078,0.5137254901960784,0.7529411764705882,1]}},{"ty":"tr","o":{"a":0,"k":100},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[-35,-83]},"r":{"a":0,"k":0}}]}],"op":152}],"op":152,"w":140,"h":100}
\ No newline at end of file
diff --git a/base/src/main/res/values/colors.xml b/base/src/main/res/values/colors.xml
new file mode 100644
index 0000000..2a9146f
--- /dev/null
+++ b/base/src/main/res/values/colors.xml
@@ -0,0 +1,8 @@
+
+
+ #EEEEEE
+
+ #00000000
+
+ #FFFFFF
+
\ No newline at end of file
diff --git a/base/versions.gradle b/base/versions.gradle
index b8ac739..050b956 100644
--- a/base/versions.gradle
+++ b/base/versions.gradle
@@ -7,6 +7,7 @@ versions.androidx_core = "1.1.0"
versions.androidx_constraint_layout = "1.1.3"
versions.androidx_lifecycle = "2.2.0"
versions.androidx_recycler_view = "1.1.0"
+versions.androidx_navigation = "2.2.0"
versions.okhttp_logging_interceptor = "4.3.1"
versions.retrofit = "2.7.1"
versions.timber = "4.7.1"
@@ -18,6 +19,7 @@ versions.junit = "4.13"
versions.test = "1.2.0"
versions.test_ext = "1.1.1"
versions.espresso = "3.2.0"
+versions.lottie = "3.3.1"
def deps = [:]
@@ -33,6 +35,8 @@ androidx.constraint_layout = "androidx.constraintlayout:constraintlayout:$versio
androidx.lifecycle_ext = "androidx.lifecycle:lifecycle-extensions:$versions.androidx_lifecycle"
androidx.lifecycle_viewmodel = "androidx.lifecycle:lifecycle-viewmodel-ktx:$versions.androidx_lifecycle"
androidx.recycler_view = "androidx.recyclerview:recyclerview:$versions.androidx_recycler_view"
+androidx.navigation_fragment = "androidx.navigation:navigation-fragment-ktx:$versions.androidx_navigation"
+androidx.navigation_ui = "androidx.navigation:navigation-ui-ktx:$versions.androidx_navigation"
deps.androidx = androidx
def retrofit = [:]
@@ -72,5 +76,6 @@ deps.testing = testing
deps.okhttp_logging_interceptor = "com.squareup.okhttp3:logging-interceptor:$versions.okhttp_logging_interceptor"
deps.timber = "com.jakewharton.timber:timber:$versions.timber"
+deps.lottie = "com.airbnb.android:lottie:$versions.lottie"
ext.deps = deps
diff --git a/build.gradle b/build.gradle
index 46f5087..155de55 100644
--- a/build.gradle
+++ b/build.gradle
@@ -39,7 +39,3 @@ detekt {
task clean(type: Delete) {
delete rootProject.buildDir
}
-
-repositories {
- google()
-}
diff --git a/core.gradle b/core.gradle
new file mode 100644
index 0000000..e0be423
--- /dev/null
+++ b/core.gradle
@@ -0,0 +1,29 @@
+apply from: '../versions.gradle'
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+apply plugin: 'kotlin-kapt'
+
+android {
+ compileSdkVersion build_versions.target_sdk
+ buildToolsVersion build_versions.build_tools
+
+ defaultConfig {
+ minSdkVersion build_versions.min_sdk
+ targetSdkVersion build_versions.target_sdk
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ testOptions {
+ unitTests.returnDefaultValues = true
+ }
+
+ lintOptions {
+ abortOnError false
+ }
+}
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index c723749..0e9eb61 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,2 +1,2 @@
-include ':app', ':base'
+include ':app', ':base', ':userlist'
rootProject.name='Friendlists'
diff --git a/tools/detekt.yml b/tools/detekt.yml
index 58e7062..0d5938a 100644
--- a/tools/detekt.yml
+++ b/tools/detekt.yml
@@ -63,7 +63,7 @@ complexity:
threshold: 150
LongMethod:
active: true
- threshold: 20
+ threshold: 40
LongParameterList:
active: false
threshold: 6
diff --git a/userlist/.gitignore b/userlist/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/userlist/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/userlist/build.gradle b/userlist/build.gradle
new file mode 100644
index 0000000..298aaa6
--- /dev/null
+++ b/userlist/build.gradle
@@ -0,0 +1,6 @@
+apply plugin: 'com.android.library'
+apply from: '../core.gradle'
+
+dependencies {
+ implementation project(':base')
+}
diff --git a/userlist/consumer-rules.pro b/userlist/consumer-rules.pro
new file mode 100644
index 0000000..e69de29
diff --git a/userlist/proguard-rules.pro b/userlist/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/userlist/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/userlist/src/main/AndroidManifest.xml b/userlist/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..d577ea7
--- /dev/null
+++ b/userlist/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
diff --git a/userlist/src/main/java/com/hako/userlist/di/UserlistModules.kt b/userlist/src/main/java/com/hako/userlist/di/UserlistModules.kt
new file mode 100644
index 0000000..a69b104
--- /dev/null
+++ b/userlist/src/main/java/com/hako/userlist/di/UserlistModules.kt
@@ -0,0 +1,17 @@
+package com.hako.userlist.di
+
+import com.hako.base.domain.network.RemoteClient
+import com.hako.userlist.domain.datasource.UserlistDatasource
+import com.hako.userlist.domain.datasource.UserlistRemoteApi
+import com.hako.userlist.domain.usecase.GetUsers
+import com.hako.userlist.viewmodel.UserlistViewmodel
+import org.koin.androidx.viewmodel.dsl.viewModel
+import org.koin.dsl.module
+
+val userlistModules = module {
+ factory { get().getClient(UserlistRemoteApi::class.java) }
+ factory { UserlistDatasource() }
+ factory { GetUsers(get()) }
+
+ viewModel { UserlistViewmodel() }
+}
\ No newline at end of file
diff --git a/userlist/src/main/java/com/hako/userlist/domain/datasource/UserlistDatasource.kt b/userlist/src/main/java/com/hako/userlist/domain/datasource/UserlistDatasource.kt
new file mode 100644
index 0000000..c9a6710
--- /dev/null
+++ b/userlist/src/main/java/com/hako/userlist/domain/datasource/UserlistDatasource.kt
@@ -0,0 +1,13 @@
+package com.hako.userlist.domain.datasource
+
+import com.hako.userlist.model.User
+import io.reactivex.Single
+import org.koin.core.KoinComponent
+import org.koin.core.get
+
+class UserlistDatasource : KoinComponent, UserlistRemoteApi {
+
+ private val api: UserlistRemoteApi = get()
+
+ override fun getUsers(): Single> = api.getUsers()
+}
\ No newline at end of file
diff --git a/userlist/src/main/java/com/hako/userlist/domain/datasource/UserlistRemoteApi.kt b/userlist/src/main/java/com/hako/userlist/domain/datasource/UserlistRemoteApi.kt
new file mode 100644
index 0000000..8ffd5b5
--- /dev/null
+++ b/userlist/src/main/java/com/hako/userlist/domain/datasource/UserlistRemoteApi.kt
@@ -0,0 +1,11 @@
+package com.hako.userlist.domain.datasource
+
+import com.hako.userlist.model.User
+import io.reactivex.Single
+import retrofit2.http.GET
+
+interface UserlistRemoteApi {
+
+ @GET("/users")
+ fun getUsers(): Single>
+}
\ No newline at end of file
diff --git a/userlist/src/main/java/com/hako/userlist/domain/usecase/GetUsers.kt b/userlist/src/main/java/com/hako/userlist/domain/usecase/GetUsers.kt
new file mode 100644
index 0000000..939394d
--- /dev/null
+++ b/userlist/src/main/java/com/hako/userlist/domain/usecase/GetUsers.kt
@@ -0,0 +1,43 @@
+package com.hako.userlist.domain.usecase
+
+import com.hako.base.domain.UseCase
+import com.hako.base.domain.database.dao.UserDao
+import com.hako.userlist.domain.datasource.UserlistDatasource
+import com.hako.userlist.model.UserViewable
+import com.hako.userlist.model.toUserEntity
+import com.hako.userlist.model.toUserViewable
+import io.reactivex.Single
+import io.reactivex.schedulers.Schedulers
+import org.koin.core.KoinComponent
+import org.koin.core.get
+
+class GetUsers(private val dao: UserDao) : KoinComponent,
+ UseCase {
+
+ private val api: UserlistDatasource = get()
+
+ override fun execute(
+ onSuccess: (List) -> Unit,
+ onError: (Throwable) -> Unit,
+ onLoading: () -> Unit
+ ) {
+ Single.fromCallable { dao.getAllUsers() }
+ .subscribeOn(Schedulers.io())
+ .doOnError { onError(it) }
+ .doOnSuccess { dbUsers ->
+ if (dbUsers.isEmpty() || dbUsers.count() == 0) {
+ api.getUsers()
+ .doOnSuccess {
+ dao.saveAll(it.map { user -> user.toUserEntity() })
+ onSuccess(dao.getAllUsers().map { user -> user.toUserViewable() })
+ }
+ .doOnSubscribe { onLoading() }
+ .subscribeOn(Schedulers.io())
+ .subscribe({}, { onError(it) })
+ } else {
+ onSuccess(dbUsers.map { it.toUserViewable() })
+ }
+ }
+ .subscribe()
+ }
+}
diff --git a/userlist/src/main/java/com/hako/userlist/feature/UserlistFragment.kt b/userlist/src/main/java/com/hako/userlist/feature/UserlistFragment.kt
new file mode 100644
index 0000000..7f02c67
--- /dev/null
+++ b/userlist/src/main/java/com/hako/userlist/feature/UserlistFragment.kt
@@ -0,0 +1,83 @@
+package com.hako.userlist.feature
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.hako.base.domain.network.RequestStatus
+import com.hako.base.extensions.gone
+import com.hako.base.extensions.observeNonNull
+import com.hako.base.extensions.toast
+import com.hako.base.extensions.visible
+import com.hako.userlist.model.UserViewable
+import com.hako.userlist.viewmodel.UserlistViewmodel
+import com.hako.userlist.widget.UserlistAdapter
+import com.hako.friendlist_userlist.R
+import kotlinx.android.synthetic.main.fragment_userlist.*
+import org.koin.androidx.viewmodel.ext.android.viewModel
+import timber.log.Timber
+
+class UserlistFragment : Fragment() {
+
+ private val viewModel: UserlistViewmodel by viewModel()
+ private val chatAdapter by lazy { UserlistAdapter() }
+
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
+ ): View = inflater.inflate(R.layout.fragment_userlist, container, false)
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ setRecycler()
+ setObservers()
+ viewModel.fetchUsers()
+ }
+
+ private fun setObservers() {
+ viewModel.data.observeNonNull(this) {
+ it.either(::handleFetchError, ::handleFetchSuccess)
+ }
+
+ viewModel.requestStatus.observeNonNull(this) {
+ when (it) {
+ RequestStatus.Ready -> {
+ fragment_userlist_error_overlay.gone()
+ fragment_userlist_loading_overlay.gone()
+ }
+ RequestStatus.Loading -> {
+ fragment_userlist_error_overlay.gone()
+ fragment_userlist_loading_overlay.visible()
+ }
+ RequestStatus.Errored -> {
+ fragment_userlist_error_overlay.visible()
+ fragment_userlist_loading_overlay.gone()
+ }
+ }
+ }
+ }
+
+ private fun handleFetchError(throwable: Throwable) {
+ Timber.e(throwable)
+ }
+
+ private fun handleFetchSuccess(users: List) {
+ chatAdapter.addAll(users)
+ }
+
+ private fun setRecycler() {
+ fragment_userlist_recycler_container.apply {
+ layoutManager = LinearLayoutManager(context)
+ adapter = chatAdapter.apply {
+ onItemClick = {
+ context.toast(it.realName)
+ }
+
+ onFavoriteClick = {
+ context.toast(it.userName)
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/userlist/src/main/java/com/hako/userlist/model/UserModels.kt b/userlist/src/main/java/com/hako/userlist/model/UserModels.kt
new file mode 100644
index 0000000..b89a2aa
--- /dev/null
+++ b/userlist/src/main/java/com/hako/userlist/model/UserModels.kt
@@ -0,0 +1,28 @@
+package com.hako.userlist.model
+
+import android.os.Parcelable
+import com.google.gson.annotations.SerializedName
+import com.hako.base.domain.database.entities.UserEntity
+import kotlinx.android.parcel.Parcelize
+
+@Parcelize
+data class User(
+ @SerializedName("id") val id: Int,
+ @SerializedName("name") val realName: String,
+ @SerializedName("username") val userName: String,
+ @SerializedName("email") val email: String,
+ @SerializedName("phone") val phone: String,
+ @SerializedName("website") val website: String
+) : Parcelable
+
+data class UserViewable(
+ val id: Int,
+ val realName: String,
+ val userName: String,
+ var isFavorite: Boolean = false
+)
+
+fun User.toUserEntity() = UserEntity(this.id, this.realName, this.userName, this.email, this.phone, this.website)
+
+fun UserEntity.toUserViewable() = UserViewable(this.id, this.realName, this.userName)
+
diff --git a/userlist/src/main/java/com/hako/userlist/viewmodel/UserlistViewmodel.kt b/userlist/src/main/java/com/hako/userlist/viewmodel/UserlistViewmodel.kt
new file mode 100644
index 0000000..abe56e5
--- /dev/null
+++ b/userlist/src/main/java/com/hako/userlist/viewmodel/UserlistViewmodel.kt
@@ -0,0 +1,36 @@
+package com.hako.userlist.viewmodel
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.hako.base.domain.network.RequestStatus
+import com.hako.base.domain.network.RequestStatus.Ready
+import com.hako.base.domain.network.RequestStatus.Loading
+import com.hako.base.domain.network.RequestStatus.Errored
+import com.hako.base.domain.Either
+import com.hako.userlist.domain.usecase.GetUsers
+import com.hako.userlist.model.UserViewable
+import org.koin.core.KoinComponent
+import org.koin.core.get
+
+class UserlistViewmodel : ViewModel(), KoinComponent {
+
+ val data = MutableLiveData>>()
+ val requestStatus = MutableLiveData()
+
+ private val getUsers: GetUsers = get()
+
+ fun fetchUsers() {
+ getUsers.execute(
+ onSuccess = {
+ requestStatus.postValue(Ready)
+ data.postValue(Either.Right(it))
+ },
+ onLoading = {
+ requestStatus.postValue(Loading)
+ },
+ onError = {
+ requestStatus.postValue(Errored)
+ data.postValue(Either.Left(it))
+ })
+ }
+}
diff --git a/userlist/src/main/java/com/hako/userlist/widget/UserlistAdapter.kt b/userlist/src/main/java/com/hako/userlist/widget/UserlistAdapter.kt
new file mode 100644
index 0000000..fe5f979
--- /dev/null
+++ b/userlist/src/main/java/com/hako/userlist/widget/UserlistAdapter.kt
@@ -0,0 +1,62 @@
+package com.hako.userlist.widget
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import com.hako.base.extensions.autoNotify
+import com.hako.userlist.model.UserViewable
+import com.hako.friendlist_userlist.R
+import kotlinx.android.synthetic.main.item_user_card.view.*
+import kotlin.properties.Delegates
+
+class UserlistAdapter : RecyclerView.Adapter() {
+
+ private var items by Delegates.observable(emptyList()) { _, oldList, newList ->
+ autoNotify(oldList, newList) { old, new -> old.id == new.id }
+ notifyDataSetChanged()
+ }
+
+ var onItemClick: (UserViewable) -> Unit = { }
+ var onFavoriteClick: (UserViewable) -> Unit = { }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder =
+ UserViewHolder(
+ LayoutInflater
+ .from(parent.context)
+ .inflate(R.layout.item_user_card, parent, false),
+ onItemClick,
+ onFavoriteClick
+ )
+
+ fun getItem(position: Int) = items[position]
+
+ fun addAll(list: List) {
+ items = list
+ }
+
+ override fun getItemCount() = items.size
+
+ override fun onBindViewHolder(viewholder: RecyclerView.ViewHolder, position: Int) =
+ when (viewholder) {
+ is UserViewHolder -> viewholder.bind(items[position])
+ else -> throw NoWhenBranchMatchedException("Undefined viewholder")
+ }
+}
+
+class UserViewHolder(private val view: View,
+ private val onItemClick: (UserViewable) -> Unit,
+ private val onFavoriteClick: (UserViewable) -> Unit) :
+ RecyclerView.ViewHolder(view) {
+
+ fun bind(user: UserViewable) = with(view) {
+ item_user_card_real_name.text = user.realName
+ item_user_card_user_name.text = user.userName
+ item_user_card_container.setOnClickListener {
+ onItemClick(user)
+ }
+ item_user_card_like_button.setOnClickListener {
+ onFavoriteClick(user)
+ }
+ }
+}
\ No newline at end of file
diff --git a/userlist/src/main/res/layout/fragment_userlist.xml b/userlist/src/main/res/layout/fragment_userlist.xml
new file mode 100644
index 0000000..ef7295d
--- /dev/null
+++ b/userlist/src/main/res/layout/fragment_userlist.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/userlist/src/main/res/layout/item_user_card.xml b/userlist/src/main/res/layout/item_user_card.xml
new file mode 100644
index 0000000..524ef91
--- /dev/null
+++ b/userlist/src/main/res/layout/item_user_card.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/userlist/src/main/res/navigation/userlist_navigation.xml b/userlist/src/main/res/navigation/userlist_navigation.xml
new file mode 100644
index 0000000..20c9258
--- /dev/null
+++ b/userlist/src/main/res/navigation/userlist_navigation.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/userlist/src/main/res/values/strings.xml b/userlist/src/main/res/values/strings.xml
new file mode 100644
index 0000000..1d5d98a
--- /dev/null
+++ b/userlist/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ userlist
+