Compare commits
8 Commits
cassiopeia
...
migrate-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d6241ca6eb | ||
| 18f53543d4 | |||
|
|
ee869dacfc | ||
|
|
0fe35da8ba | ||
|
|
a3e61a8297 | ||
|
|
e14458589b | ||
|
|
984602ec7e | ||
|
|
e61c123068 |
@@ -1,8 +1,12 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply from: '../core.gradle'
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
buildConfigField "String", "DB_NAME", '"albumlist.db"'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':base')
|
||||
testImplementation project(':testing')
|
||||
androidTestImplementation project(':testing')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.hako.albumlist.di
|
||||
|
||||
import androidx.room.Room
|
||||
import com.hako.albumlist.BuildConfig
|
||||
import com.hako.albumlist.domain.clients.LocalClient
|
||||
import com.hako.albumlist.domain.datasource.AlbumlistRemoteApi
|
||||
import com.hako.albumlist.domain.usecase.GetAlbum
|
||||
import com.hako.albumlist.viewmodel.AlbumlistViewmodel
|
||||
@@ -8,8 +11,12 @@ import org.koin.androidx.viewmodel.dsl.viewModel
|
||||
import org.koin.dsl.module
|
||||
|
||||
val albumListModules = module {
|
||||
|
||||
single { Room.databaseBuilder(get(), LocalClient::class.java, BuildConfig.DB_NAME).build() }
|
||||
factory { get<LocalClient>().albumDao() }
|
||||
|
||||
factory { get<RemoteClient>().getClient(AlbumlistRemoteApi::class.java) }
|
||||
factory { GetAlbum(get()) }
|
||||
factory { GetAlbum(get(), get()) }
|
||||
|
||||
viewModel { AlbumlistViewmodel() }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.hako.albumlist.domain.clients
|
||||
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import com.hako.albumlist.domain.datasource.AlbumDao
|
||||
import com.hako.albumlist.model.AlbumEntity
|
||||
|
||||
@Database(entities = [AlbumEntity::class], version = 1, exportSchema = false)
|
||||
abstract class LocalClient : RoomDatabase() {
|
||||
abstract fun albumDao(): AlbumDao
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.hako.base.domain.database.dao
|
||||
package com.hako.albumlist.domain.datasource
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import com.hako.base.domain.database.entities.AlbumEntity
|
||||
import com.hako.albumlist.model.AlbumEntity
|
||||
|
||||
@Dao
|
||||
interface AlbumDao {
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.hako.albumlist.domain.datasource
|
||||
|
||||
import com.hako.albumlist.model.Album
|
||||
import com.hako.albumlist.model.AlbumRemote
|
||||
import io.reactivex.Single
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Query
|
||||
@@ -10,5 +10,5 @@ interface AlbumlistRemoteApi {
|
||||
@GET("/albums")
|
||||
fun getAlbums(
|
||||
@Query("userId") userId: Int
|
||||
): Single<List<Album>>
|
||||
): Single<List<AlbumRemote>>
|
||||
}
|
||||
@@ -1,22 +1,18 @@
|
||||
package com.hako.albumlist.domain.usecase
|
||||
|
||||
import com.hako.albumlist.domain.datasource.AlbumDao
|
||||
import com.hako.albumlist.domain.datasource.AlbumlistRemoteApi
|
||||
import com.hako.albumlist.model.AlbumViewable
|
||||
import com.hako.albumlist.model.Album
|
||||
import com.hako.albumlist.model.toAlbumEntity
|
||||
import com.hako.albumlist.model.toAlbumViewable
|
||||
import com.hako.base.domain.database.dao.AlbumDao
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import org.koin.core.KoinComponent
|
||||
import org.koin.core.get
|
||||
|
||||
class GetAlbum(private val dao: AlbumDao) : KoinComponent {
|
||||
|
||||
private val api: AlbumlistRemoteApi = get()
|
||||
class GetAlbum(private val dao: AlbumDao, private val api: AlbumlistRemoteApi) {
|
||||
|
||||
fun execute(
|
||||
userId: Int,
|
||||
onSuccess: (List<AlbumViewable>) -> Unit,
|
||||
onSuccess: (List<Album>) -> Unit,
|
||||
onError: (Throwable) -> Unit,
|
||||
onLoading: () -> Unit
|
||||
) {
|
||||
|
||||
@@ -7,7 +7,7 @@ import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.hako.albumlist.R
|
||||
import com.hako.albumlist.model.AlbumViewable
|
||||
import com.hako.albumlist.model.Album
|
||||
import com.hako.albumlist.navigation.AlbumlistNavigation
|
||||
import com.hako.albumlist.viewmodel.AlbumlistViewmodel
|
||||
import com.hako.albumlist.widget.AlbumlistAdapter
|
||||
@@ -72,7 +72,7 @@ class AlbumlistFragment : Fragment() {
|
||||
Timber.e(throwable)
|
||||
}
|
||||
|
||||
private fun handleFetchSuccess(users: List<AlbumViewable>) {
|
||||
private fun handleFetchSuccess(users: List<Album>) {
|
||||
listAdapter.addAll(users)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,24 +1,38 @@
|
||||
package com.hako.albumlist.model
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.hako.base.domain.database.entities.AlbumEntity
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
|
||||
@Parcelize
|
||||
data class Album(
|
||||
data class AlbumRemote(
|
||||
@SerializedName("id") val id: Int,
|
||||
@SerializedName("userId") val userId: Int,
|
||||
@SerializedName("title") val title: String
|
||||
) : Parcelable
|
||||
|
||||
data class AlbumViewable(
|
||||
@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"
|
||||
}
|
||||
}
|
||||
|
||||
data class Album(
|
||||
val id: Int,
|
||||
val userId: Int,
|
||||
val title: String
|
||||
)
|
||||
|
||||
fun Album.toAlbumEntity() = AlbumEntity(this.id, this.userId, this.title)
|
||||
fun AlbumRemote.toAlbumEntity() = AlbumEntity(this.id, this.userId, this.title)
|
||||
|
||||
fun AlbumEntity.toAlbumViewable() = AlbumViewable(this.id, this.userId, this.title)
|
||||
fun AlbumEntity.toAlbumViewable() = Album(this.id, this.userId, this.title)
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ package com.hako.albumlist.viewmodel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.hako.albumlist.domain.usecase.GetAlbum
|
||||
import com.hako.albumlist.model.AlbumViewable
|
||||
import com.hako.albumlist.model.Album
|
||||
import com.hako.base.domain.network.RequestStatus
|
||||
import com.hako.base.domain.network.RequestStatus.Ready
|
||||
import com.hako.base.domain.network.RequestStatus.Loading
|
||||
@@ -14,7 +14,7 @@ import org.koin.core.get
|
||||
|
||||
class AlbumlistViewmodel : ViewModel(), KoinComponent {
|
||||
|
||||
val data = MutableLiveData<Either<Throwable, List<AlbumViewable>>>()
|
||||
val data = MutableLiveData<Either<Throwable, List<Album>>>()
|
||||
val requestStatus = MutableLiveData<RequestStatus>()
|
||||
|
||||
private val getAlbum: GetAlbum = get()
|
||||
|
||||
@@ -5,19 +5,19 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.hako.albumlist.R
|
||||
import com.hako.albumlist.model.AlbumViewable
|
||||
import com.hako.albumlist.model.Album
|
||||
import com.hako.base.extensions.autoNotify
|
||||
import kotlinx.android.synthetic.main.item_album_card.view.*
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
class AlbumlistAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
private var items by Delegates.observable(emptyList<AlbumViewable>()) { _, oldList, newList ->
|
||||
private var items by Delegates.observable(emptyList<Album>()) { _, oldList, newList ->
|
||||
autoNotify(oldList, newList) { old, new -> old.id == new.id }
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
var onItemClick: (AlbumViewable) -> Unit = { }
|
||||
var onItemClick: (Album) -> Unit = { }
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder =
|
||||
AlbumViewHolder(
|
||||
@@ -29,7 +29,7 @@ class AlbumlistAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
fun getItem(position: Int) = items[position]
|
||||
|
||||
fun addAll(list: List<AlbumViewable>) {
|
||||
fun addAll(list: List<Album>) {
|
||||
items = list
|
||||
}
|
||||
|
||||
@@ -43,10 +43,10 @@ class AlbumlistAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
}
|
||||
|
||||
class AlbumViewHolder(private val view: View,
|
||||
private val onItemClick: (AlbumViewable) -> Unit) :
|
||||
private val onItemClick: (Album) -> Unit) :
|
||||
RecyclerView.ViewHolder(view) {
|
||||
|
||||
fun bind(album: AlbumViewable) = with(view) {
|
||||
fun bind(album: Album) = with(view) {
|
||||
item_album_card_album_name.text = album.title
|
||||
item_album_card_container.setOnClickListener {
|
||||
onItemClick(album)
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
apply from: '../versions.gradle'
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'kotlin-android'
|
||||
id 'kotlin-android-extensions'
|
||||
id 'kotlin-kapt'
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion build_versions.target_sdk
|
||||
buildToolsVersion build_versions.build_tools
|
||||
compileSdkVersion libs.versions.target_sdk.get().toInteger()
|
||||
buildToolsVersion libs.versions.build_tools.get()
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.hako.friendlists"
|
||||
minSdkVersion build_versions.min_sdk
|
||||
targetSdkVersion build_versions.target_sdk
|
||||
minSdkVersion libs.versions.min_sdk.get().toInteger()
|
||||
targetSdkVersion libs.versions.target_sdk.get().toInteger()
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
buildConfigField "String", "DB_NAME", '"friendlists.db"'
|
||||
buildConfigField "String", "BASE_ENDPOINT", '"https://jsonplaceholder.typicode.com/"'
|
||||
}
|
||||
|
||||
@@ -57,6 +57,4 @@ dependencies {
|
||||
implementation project(":userlist")
|
||||
implementation project(":albumlist")
|
||||
implementation project(":photolist")
|
||||
testImplementation project(':testing')
|
||||
androidTestImplementation project(':testing')
|
||||
}
|
||||
|
||||
BIN
app/src/main/ic_launcher-web.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
@@ -1,7 +1,5 @@
|
||||
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.base.navigation.NavigationRouter
|
||||
import com.hako.friendlists.BuildConfig
|
||||
@@ -11,12 +9,6 @@ import org.koin.androidx.viewmodel.dsl.viewModel
|
||||
import org.koin.dsl.module
|
||||
|
||||
val appModules = module {
|
||||
// Room database
|
||||
single { Room.databaseBuilder(get(), DatabaseClient::class.java, BuildConfig.DB_NAME).build() }
|
||||
factory { get<DatabaseClient>().userDao() }
|
||||
factory { get<DatabaseClient>().albumDao() }
|
||||
factory { get<DatabaseClient>().photoDao() }
|
||||
|
||||
// Retrofit
|
||||
single { RemoteClient(BuildConfig.BASE_ENDPOINT) }
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.4 KiB |
BIN
app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 1.5 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 3.5 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 5.4 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 8.1 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
4
app/src/main/res/values/ic_launcher_background.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#FFFFFF</color>
|
||||
</resources>
|
||||
@@ -1,16 +1,15 @@
|
||||
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
|
||||
compileSdkVersion libs.versions.target_sdk.get().toInteger()
|
||||
buildToolsVersion libs.versions.build_tools.get()
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion build_versions.min_sdk
|
||||
targetSdkVersion build_versions.target_sdk
|
||||
minSdkVersion libs.versions.min_sdk.get().toInteger()
|
||||
targetSdkVersion libs.versions.target_sdk.get().toInteger()
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
@@ -29,29 +28,29 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
kapt deps.room.compiler
|
||||
api deps.kotlin.std_lib
|
||||
api deps.androidx.appcompat
|
||||
api deps.androidx.core_ktx
|
||||
api deps.androidx.constraint_layout
|
||||
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
|
||||
api deps.room.runtime
|
||||
api deps.koin.core
|
||||
api deps.koin.scope
|
||||
api deps.koin.viewmodel
|
||||
api deps.rx.core
|
||||
api deps.rx.android
|
||||
api deps.okhttp_logging_interceptor
|
||||
api deps.timber
|
||||
api deps.lottie
|
||||
api deps.picasso
|
||||
kapt libs.room.compiler
|
||||
api libs.kotlin.std_lib
|
||||
api libs.androidx.appcompat
|
||||
api libs.androidx.core_ktx
|
||||
api libs.androidx.constraint_layout
|
||||
api libs.androidx.lifecycle_ext
|
||||
api libs.androidx.lifecycle_viewmodel
|
||||
api libs.androidx.recycler_view
|
||||
api libs.androidx.navigation_fragment
|
||||
api libs.androidx.navigation_ui
|
||||
api libs.retrofit.runtime
|
||||
api libs.retrofit.gson
|
||||
api libs.retrofit.rx
|
||||
api libs.room.runtime
|
||||
api libs.koin.core
|
||||
api libs.koin.scope
|
||||
api libs.koin.viewmodel
|
||||
api libs.rx.core
|
||||
api libs.rx.android
|
||||
api libs.okhttp_logging_interceptor
|
||||
api libs.timber
|
||||
api libs.lottie
|
||||
api libs.picasso
|
||||
testImplementation project(':testing')
|
||||
androidTestImplementation project(':testing')
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
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
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
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"
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
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"
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
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,
|
||||
val isFavorite: Boolean = false
|
||||
) {
|
||||
companion object {
|
||||
const val TABLE_NAME = "users"
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
ext.deps = [:]
|
||||
|
||||
def versions = [:]
|
||||
versions.kotlin = "1.3.41"
|
||||
versions.androidx_appcompat = "1.1.0"
|
||||
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"
|
||||
versions.room = "2.2.3"
|
||||
versions.koin = "2.0.1"
|
||||
versions.rx_core = "2.2.17"
|
||||
versions.rx_android = "2.1.1"
|
||||
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"
|
||||
versions.picasso = "2.71828"
|
||||
|
||||
def deps = [:]
|
||||
|
||||
def kotlin = [:]
|
||||
kotlin.version = "$versions.kotlin"
|
||||
kotlin.std_lib = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$versions.kotlin"
|
||||
deps.kotlin = kotlin
|
||||
|
||||
def androidx = [:]
|
||||
androidx.appcompat = "androidx.appcompat:appcompat:$versions.androidx_appcompat"
|
||||
androidx.core_ktx = "androidx.core:core-ktx:$versions.androidx_core"
|
||||
androidx.constraint_layout = "androidx.constraintlayout:constraintlayout:$versions.androidx_constraint_layout"
|
||||
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 = [:]
|
||||
retrofit.runtime = "com.squareup.retrofit2:retrofit:$versions.retrofit"
|
||||
retrofit.gson = "com.squareup.retrofit2:converter-gson:$versions.retrofit"
|
||||
retrofit.mock = "com.squareup.retrofit2:retrofit-mock:$versions.retrofit"
|
||||
retrofit.rx = "com.squareup.retrofit2:adapter-rxjava2:$versions.retrofit"
|
||||
deps.retrofit = retrofit
|
||||
|
||||
def room = [:]
|
||||
room.runtime = "androidx.room:room-runtime:$versions.room"
|
||||
room.compiler = "androidx.room:room-compiler:$versions.room"
|
||||
deps.room = room
|
||||
|
||||
def koin = [:]
|
||||
koin.core = "org.koin:koin-android:$versions.koin"
|
||||
koin.scope = "org.koin:koin-androidx-scope:$versions.koin"
|
||||
koin.viewmodel = "org.koin:koin-androidx-viewmodel:$versions.koin"
|
||||
deps.koin = koin
|
||||
|
||||
def rx = [:]
|
||||
rx.core = "io.reactivex.rxjava2:rxjava:$versions.rx_core"
|
||||
rx.android = "io.reactivex.rxjava2:rxandroid:$versions.rx_android"
|
||||
deps.rx = rx
|
||||
|
||||
def testing = [:]
|
||||
testing.junit = "junit:junit:$versions.junit"
|
||||
testing.core = "androidx.test:core:$versions.test"
|
||||
testing.rules = "androidx.test:rules:$versions.test"
|
||||
testing.runner = "androidx.test:runner:$versions.test"
|
||||
testing.ext = "androidx.test.ext:junit:$versions.test_ext"
|
||||
testing.koin = "org.koin:koin-test:$versions.koin"
|
||||
testing.espresso = "androidx.test.espresso:espresso-core:$versions.espresso"
|
||||
deps.testing = testing
|
||||
|
||||
// Singles
|
||||
|
||||
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"
|
||||
deps.picasso = "com.squareup.picasso:picasso:$versions.picasso"
|
||||
|
||||
ext.deps = deps
|
||||
12
build.gradle
@@ -1,14 +1,12 @@
|
||||
buildscript {
|
||||
apply from: 'versions.gradle'
|
||||
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath deps.project.kotlin
|
||||
classpath deps.project.gradle
|
||||
classpath libs.plugins.kotlin
|
||||
classpath libs.plugins.gradle
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +15,11 @@ plugins {
|
||||
}
|
||||
|
||||
allprojects {
|
||||
addRepos(repositories)
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
|
||||
}
|
||||
}
|
||||
|
||||
detekt {
|
||||
|
||||
1
codemagic.yaml
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
15
core.gradle
@@ -1,16 +1,15 @@
|
||||
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
|
||||
compileSdkVersion libs.versions.target_sdk.get().toInteger()
|
||||
buildToolsVersion libs.versions.build_tools.get()
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion build_versions.min_sdk
|
||||
targetSdkVersion build_versions.target_sdk
|
||||
minSdkVersion libs.versions.min_sdk.get().toInteger()
|
||||
targetSdkVersion libs.versions.target_sdk.get().toInteger()
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
@@ -30,4 +29,8 @@ android {
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
kapt libs.room.compiler
|
||||
}
|
||||
|
||||
64
gradle/libs.versions.toml
Normal file
@@ -0,0 +1,64 @@
|
||||
[versions]
|
||||
kotlin = "1.3.41"
|
||||
androidx_appcompat = "1.1.0"
|
||||
androidx_core = "1.1.0"
|
||||
androidx_constraint_layout = "1.1.3"
|
||||
androidx_lifecycle = "2.2.0"
|
||||
androidx_recycler_view = "1.1.0"
|
||||
androidx_navigation = "2.2.0"
|
||||
okhttp_logging_interceptor = "4.3.1"
|
||||
retrofit = "2.7.1"
|
||||
timber = "4.7.1"
|
||||
room = "2.2.3"
|
||||
koin = "2.0.1"
|
||||
rx_core = "2.2.17"
|
||||
rx_android = "2.1.1"
|
||||
junit = "4.13"
|
||||
test = "1.2.0"
|
||||
test_ext = "1.1.1"
|
||||
espresso = "3.2.0"
|
||||
lottie = "3.3.1"
|
||||
picasso = "2.71828"
|
||||
gradle = "3.5.3"
|
||||
|
||||
[plugins]
|
||||
kotlin = { id = "org.jetbrains.kotlin.kotlin-gradle-plugin", version.ref = "kotlin" }
|
||||
gradle = { id = "com.android.tools.build.gradle", version.ref = "gradle" }
|
||||
|
||||
[libraries]
|
||||
kotlin_std_lib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib-jdk7", version.ref = "kotlin" }
|
||||
androidx_appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidx_appcompat" }
|
||||
androidx_core_ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidx_core" }
|
||||
androidx_constraint_layout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "androidx_constraint_layout" }
|
||||
androidx_lifecycle_ext = { group = "androidx.lifecycle", name = "lifecycle-extensions", version.ref = "androidx_lifecycle" }
|
||||
androidx_lifecycle_viewmodel = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "androidx_lifecycle" }
|
||||
androidx_recycler_view = { group = "androidx.recyclerview", name = "recyclerview", version.ref = "androidx_recycler_view" }
|
||||
androidx_navigation_fragment = { group = "androidx.navigation", name = "navigation-fragment-ktx", version.ref = "androidx_navigation" }
|
||||
androidx_navigation_ui = { group = "androidx.navigation", name = "navigation-ui-ktx", version.ref = "androidx_navigation" }
|
||||
retrofit_runtime = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
|
||||
retrofit_gson = { group = "com.squareup.retrofit2", name = "converter-gson", version.ref = "retrofit" }
|
||||
retrofit_mock = { group = "com.squareup.retrofit2", name = "retrofit-mock", version.ref = "retrofit" }
|
||||
retrofit_rx = { group = "com.squareup.retrofit2", name = "adapter-rxjava2", version.ref = "retrofit" }
|
||||
room_runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
|
||||
room_compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }
|
||||
koin_core = { group = "org.koin", name = "koin-android", version.ref = "koin" }
|
||||
koin_scope = { group = "org.koin", name = "koin-androidx-scope", version.ref = "koin" }
|
||||
koin_viewmodel = { group = "org.koin", name = "koin-androidx-viewmodel", version.ref = "koin" }
|
||||
rx_core = { group = "io.reactivex.rxjava2", name = "rxjava", version.ref = "rx_core" }
|
||||
rx_android = { group = "io.reactivex.rxjava2", name = "rxandroid", version.ref = "rx_android" }
|
||||
testing_junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||
testing_core = { group = "androidx.test", name = "core", version.ref = "test" }
|
||||
testing_rules = { group = "androidx.test", name = "rules", version.ref = "test" }
|
||||
testing_runner = { group = "androidx.test", name = "runner", version.ref = "test" }
|
||||
testing_ext = { group = "androidx.test.ext", name = "junit", version.ref = "test_ext" }
|
||||
testing_koin = { group = "org.koin", name = "koin-test", version.ref = "koin" }
|
||||
testing_espresso = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso" }
|
||||
okhttp_logging_interceptor = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp_logging_interceptor" }
|
||||
timber = { group = "com.jakewharton.timber", name = "timber", version.ref = "timber" }
|
||||
lottie = { group = "com.airbnb.android", name = "lottie", version.ref = "lottie" }
|
||||
picasso = { group = "com.squareup.picasso", name = "picasso", version.ref = "picasso" }
|
||||
|
||||
[build_versions]
|
||||
min_sdk = 21
|
||||
target_sdk = 29
|
||||
build_tools = "29.0.3"
|
||||
@@ -1,8 +1,12 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply from: '../core.gradle'
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
buildConfigField "String", "DB_NAME", '"photolist.db"'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':base')
|
||||
testImplementation project(':testing')
|
||||
androidTestImplementation project(':testing')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package com.hako.photolist.di
|
||||
|
||||
import androidx.room.Room
|
||||
import com.hako.base.domain.network.RemoteClient
|
||||
import com.hako.photolist.BuildConfig
|
||||
import com.hako.photolist.domain.clients.LocalClient
|
||||
import com.hako.photolist.domain.datasource.PhotolistRemoteApi
|
||||
import com.hako.photolist.domain.usecase.GetPhoto
|
||||
import com.hako.photolist.viewmodel.PhotolistViewmodel
|
||||
@@ -8,8 +11,12 @@ import org.koin.androidx.viewmodel.dsl.viewModel
|
||||
import org.koin.dsl.module
|
||||
|
||||
val photoListModules = module {
|
||||
|
||||
single { Room.databaseBuilder(get(), LocalClient::class.java, BuildConfig.DB_NAME).build() }
|
||||
factory { get<LocalClient>().photoDao() }
|
||||
|
||||
factory { get<RemoteClient>().getClient(PhotolistRemoteApi::class.java) }
|
||||
factory { GetPhoto(get()) }
|
||||
factory { GetPhoto(get(), get()) }
|
||||
|
||||
viewModel { PhotolistViewmodel() }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.hako.photolist.domain.clients
|
||||
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import com.hako.photolist.domain.datasource.PhotoDao
|
||||
import com.hako.photolist.model.PhotoEntity
|
||||
|
||||
@Database(entities = [PhotoEntity::class], version = 1, exportSchema = false)
|
||||
abstract class LocalClient : RoomDatabase() {
|
||||
abstract fun photoDao(): PhotoDao
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.hako.base.domain.database.dao
|
||||
package com.hako.photolist.domain.datasource
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import com.hako.base.domain.database.entities.PhotoEntity
|
||||
import com.hako.photolist.model.PhotoEntity
|
||||
|
||||
@Dao
|
||||
interface PhotoDao {
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.hako.photolist.domain.datasource
|
||||
|
||||
import com.hako.photolist.model.Photo
|
||||
import com.hako.photolist.model.PhotoRemote
|
||||
import io.reactivex.Single
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Query
|
||||
@@ -10,5 +10,5 @@ interface PhotolistRemoteApi {
|
||||
@GET("/photos")
|
||||
fun getPhotos(
|
||||
@Query("albumId") albumId: Int
|
||||
): Single<List<Photo>>
|
||||
): Single<List<PhotoRemote>>
|
||||
}
|
||||
@@ -1,20 +1,16 @@
|
||||
package com.hako.photolist.domain.usecase
|
||||
|
||||
import com.hako.base.domain.database.dao.PhotoDao
|
||||
import com.hako.photolist.domain.datasource.PhotoDao
|
||||
import com.hako.photolist.domain.datasource.PhotolistRemoteApi
|
||||
import com.hako.photolist.model.*
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import org.koin.core.KoinComponent
|
||||
import org.koin.core.get
|
||||
|
||||
class GetPhoto(private val dao: PhotoDao) : KoinComponent {
|
||||
|
||||
private val api: PhotolistRemoteApi = get()
|
||||
class GetPhoto(private val dao: PhotoDao, private val api: PhotolistRemoteApi) {
|
||||
|
||||
fun execute(
|
||||
albumId: Int,
|
||||
onSuccess: (List<PhotoViewable>) -> Unit,
|
||||
onSuccess: (List<Photo>) -> Unit,
|
||||
onError: (Throwable) -> Unit,
|
||||
onLoading: () -> Unit
|
||||
) {
|
||||
|
||||
@@ -11,7 +11,7 @@ import com.hako.base.extensions.gone
|
||||
import com.hako.base.extensions.observeNonNull
|
||||
import com.hako.base.extensions.visible
|
||||
import com.hako.photolist.R
|
||||
import com.hako.photolist.model.PhotoViewable
|
||||
import com.hako.photolist.model.Photo
|
||||
import com.hako.photolist.viewmodel.PhotolistViewmodel
|
||||
import com.hako.photolist.widget.PhotolistAdapter
|
||||
import kotlinx.android.synthetic.main.fragment_photolist.*
|
||||
@@ -68,7 +68,7 @@ class PhotolistFragment : Fragment() {
|
||||
Timber.e(throwable)
|
||||
}
|
||||
|
||||
private fun handleFetchSuccess(photos: List<PhotoViewable>) {
|
||||
private fun handleFetchSuccess(photos: List<Photo>) {
|
||||
listAdapter.addAll(photos)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
package com.hako.photolist.model
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.hako.base.domain.database.entities.PhotoEntity
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
|
||||
@Parcelize
|
||||
data class Photo(
|
||||
data class PhotoRemote(
|
||||
@SerializedName("id") val id: Int,
|
||||
@SerializedName("albumId") val albumId: Int,
|
||||
@SerializedName("title") val title: String,
|
||||
@@ -14,14 +16,28 @@ data class Photo(
|
||||
@SerializedName("thumbnailUrl") val thumbnailUrl: String
|
||||
) : Parcelable
|
||||
|
||||
data class PhotoViewable(
|
||||
@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"
|
||||
}
|
||||
}
|
||||
|
||||
data class Photo(
|
||||
val id: Int,
|
||||
val albumId: Int,
|
||||
val title: String,
|
||||
val photoUrl: String
|
||||
)
|
||||
|
||||
fun Photo.toPhotoEntity() = PhotoEntity(this.id, this.albumId, this.title, this.photoUrl, this.thumbnailUrl)
|
||||
fun PhotoRemote.toPhotoEntity() = PhotoEntity(this.id, this.albumId, this.title, this.photoUrl, this.thumbnailUrl)
|
||||
|
||||
fun PhotoEntity.toPhotoViewable() = PhotoViewable(this.id, this.albumId, this.title, this.photoUrl)
|
||||
fun PhotoEntity.toPhotoViewable() = Photo(this.id, this.albumId, this.title, this.photoUrl)
|
||||
|
||||
|
||||
@@ -8,13 +8,13 @@ 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.photolist.domain.usecase.GetPhoto
|
||||
import com.hako.photolist.model.PhotoViewable
|
||||
import com.hako.photolist.model.Photo
|
||||
import org.koin.core.KoinComponent
|
||||
import org.koin.core.get
|
||||
|
||||
class PhotolistViewmodel : ViewModel(), KoinComponent {
|
||||
|
||||
val data = MutableLiveData<Either<Throwable, List<PhotoViewable>>>()
|
||||
val data = MutableLiveData<Either<Throwable, List<Photo>>>()
|
||||
val requestStatus = MutableLiveData<RequestStatus>()
|
||||
|
||||
private val getPhoto: GetPhoto = get()
|
||||
|
||||
@@ -6,7 +6,7 @@ import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.hako.base.extensions.autoNotify
|
||||
import com.hako.photolist.R
|
||||
import com.hako.photolist.model.PhotoViewable
|
||||
import com.hako.photolist.model.Photo
|
||||
import com.squareup.picasso.Picasso
|
||||
import kotlinx.android.synthetic.main.item_photo_card.view.*
|
||||
import org.koin.core.KoinComponent
|
||||
@@ -15,7 +15,7 @@ import kotlin.properties.Delegates
|
||||
|
||||
class PhotolistAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
private var items by Delegates.observable(emptyList<PhotoViewable>()) { _, oldList, newList ->
|
||||
private var items by Delegates.observable(emptyList<Photo>()) { _, oldList, newList ->
|
||||
autoNotify(oldList, newList) { old, new -> old.id == new.id }
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
@@ -29,7 +29,7 @@ class PhotolistAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
fun getItem(position: Int) = items[position]
|
||||
|
||||
fun addAll(list: List<PhotoViewable>) {
|
||||
fun addAll(list: List<Photo>) {
|
||||
items = list
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ class PhotoViewHolder(private val view: View) :
|
||||
|
||||
private val picasso: Picasso by inject()
|
||||
|
||||
fun bind(photo: PhotoViewable) = with(view) {
|
||||
fun bind(photo: Photo) = with(view) {
|
||||
picasso.load(photo.photoUrl)
|
||||
.placeholder(R.drawable.img_photo_placeholder)
|
||||
.fit()
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply from: '../core.gradle'
|
||||
apply from: 'versions.gradle'
|
||||
|
||||
dependencies {
|
||||
api deps.testing.junit
|
||||
api deps.testing.koin
|
||||
api deps.testing.core
|
||||
api deps.testing.rules
|
||||
api deps.testing.runner
|
||||
api deps.testing.ext
|
||||
api deps.testing.espresso
|
||||
api deps.testing.fragments
|
||||
}
|
||||
api libs.testing.junit
|
||||
api libs.testing.koin
|
||||
api libs.testing.core
|
||||
api libs.testing.rules
|
||||
api libs.testing.runner
|
||||
api libs.testing.ext
|
||||
api libs.testing.espresso
|
||||
}
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply from: '../core.gradle'
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
buildConfigField "String", "DB_NAME", '"userlist.db"'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':base')
|
||||
testImplementation project(':testing')
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package com.hako.userlist.feature
|
||||
|
||||
import com.hako.base.domain.database.dao.UserDao
|
||||
import com.hako.base.domain.database.entities.UserEntity
|
||||
import com.hako.userlist.domain.datasource.UserDao
|
||||
import com.hako.userlist.domain.datasource.UserlistRemoteApi
|
||||
import com.hako.userlist.model.User
|
||||
import com.hako.userlist.model.UserEntity
|
||||
import com.hako.userlist.model.UserRemote
|
||||
import io.reactivex.Single
|
||||
|
||||
class MockUserDao(private val userList: List<UserEntity>) : UserDao {
|
||||
@@ -22,8 +22,8 @@ class MockUserDao(private val userList: List<UserEntity>) : UserDao {
|
||||
override fun nukeDatabase() {}
|
||||
}
|
||||
|
||||
class MockUserApi(private val userList: List<User>) : UserlistRemoteApi {
|
||||
class MockUserApi(private val userRemoteList: List<UserRemote>) : UserlistRemoteApi {
|
||||
override fun getUsers() = Single.fromCallable { getAllUsers() }
|
||||
|
||||
private fun getAllUsers() = userList
|
||||
private fun getAllUsers() = userRemoteList
|
||||
}
|
||||
@@ -2,14 +2,14 @@ package com.hako.userlist.feature
|
||||
|
||||
import androidx.fragment.app.testing.launchFragmentInContainer
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import com.hako.base.domain.database.dao.UserDao
|
||||
import com.hako.base.domain.database.entities.UserEntity
|
||||
import com.hako.testing.isTextDisplayed
|
||||
import com.hako.userlist.domain.datasource.UserDao
|
||||
import com.hako.userlist.domain.datasource.UserlistRemoteApi
|
||||
import com.hako.userlist.domain.usecase.GetFavoriteUsers
|
||||
import com.hako.userlist.domain.usecase.GetUsers
|
||||
import com.hako.userlist.domain.usecase.SetFavoriteStatus
|
||||
import com.hako.userlist.model.User
|
||||
import com.hako.userlist.model.UserEntity
|
||||
import com.hako.userlist.model.UserRemote
|
||||
import com.hako.userlist.model.toUserEntity
|
||||
import com.hako.userlist.viewmodel.UserlistViewmodel
|
||||
import org.junit.After
|
||||
@@ -28,7 +28,7 @@ class UserlistFragmentTest {
|
||||
startKoin {
|
||||
InstrumentationRegistry.getInstrumentation().targetContext
|
||||
modules(module {
|
||||
factory { GetUsers(get()) }
|
||||
factory { GetUsers(get(), get()) }
|
||||
factory { GetFavoriteUsers(get()) }
|
||||
factory { SetFavoriteStatus(get()) }
|
||||
viewModel { UserlistViewmodel() }
|
||||
@@ -92,7 +92,7 @@ class UserlistRobot {
|
||||
}
|
||||
|
||||
private fun loadTwoBasicUsers() = listOf(
|
||||
User(
|
||||
UserRemote(
|
||||
1,
|
||||
"Marian Arriaga",
|
||||
"mariancita",
|
||||
@@ -100,7 +100,7 @@ class UserlistRobot {
|
||||
"+56873912",
|
||||
"www.test.com"
|
||||
),
|
||||
User(
|
||||
UserRemote(
|
||||
2,
|
||||
"Carlos Martinez",
|
||||
"carlitos",
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.hako.friendlist_userlist" />
|
||||
package="com.hako.userlist" />
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package com.hako.userlist.di
|
||||
|
||||
import androidx.room.Room
|
||||
import com.hako.base.domain.network.RemoteClient
|
||||
import com.hako.userlist.BuildConfig
|
||||
import com.hako.userlist.domain.clients.LocalClient
|
||||
import com.hako.userlist.domain.datasource.UserlistRemoteApi
|
||||
import com.hako.userlist.domain.usecase.GetFavoriteUsers
|
||||
import com.hako.userlist.domain.usecase.GetUsers
|
||||
@@ -10,8 +13,12 @@ import org.koin.androidx.viewmodel.dsl.viewModel
|
||||
import org.koin.dsl.module
|
||||
|
||||
val userlistModules = module {
|
||||
|
||||
single { Room.databaseBuilder(get(), LocalClient::class.java, BuildConfig.DB_NAME).build() }
|
||||
factory { get<LocalClient>().userDao() }
|
||||
|
||||
factory { get<RemoteClient>().getClient(UserlistRemoteApi::class.java) }
|
||||
factory { GetUsers(get()) }
|
||||
factory { GetUsers(get(), get()) }
|
||||
factory { GetFavoriteUsers(get()) }
|
||||
factory { SetFavoriteStatus(get()) }
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.hako.userlist.domain.clients
|
||||
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import com.hako.userlist.domain.datasource.UserDao
|
||||
import com.hako.userlist.model.UserEntity
|
||||
|
||||
@Database(entities = [UserEntity::class], version = 1, exportSchema = false)
|
||||
abstract class LocalClient : RoomDatabase() {
|
||||
abstract fun userDao(): UserDao
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.hako.base.domain.database.dao
|
||||
package com.hako.userlist.domain.datasource
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import com.hako.base.domain.database.entities.UserEntity
|
||||
import com.hako.userlist.model.UserEntity
|
||||
|
||||
@Dao
|
||||
interface UserDao {
|
||||
@@ -1,11 +1,11 @@
|
||||
package com.hako.userlist.domain.datasource
|
||||
|
||||
import com.hako.userlist.model.User
|
||||
import com.hako.userlist.model.UserRemote
|
||||
import io.reactivex.Single
|
||||
import retrofit2.http.GET
|
||||
|
||||
interface UserlistRemoteApi {
|
||||
|
||||
@GET("/users")
|
||||
fun getUsers(): Single<List<User>>
|
||||
fun getUsers(): Single<List<UserRemote>>
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.hako.userlist.domain.usecase
|
||||
|
||||
import com.hako.base.domain.database.dao.UserDao
|
||||
import com.hako.userlist.model.UserViewable
|
||||
import com.hako.userlist.domain.datasource.UserDao
|
||||
import com.hako.userlist.model.User
|
||||
import com.hako.userlist.model.toUserViewable
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
@@ -9,7 +9,7 @@ import io.reactivex.schedulers.Schedulers
|
||||
class GetFavoriteUsers(private val dao: UserDao) {
|
||||
|
||||
fun execute(
|
||||
onSuccess: (List<UserViewable>) -> Unit,
|
||||
onSuccess: (List<User>) -> Unit,
|
||||
onEmpty: () -> Unit,
|
||||
onError: (Throwable) -> Unit
|
||||
) {
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
package com.hako.userlist.domain.usecase
|
||||
|
||||
import com.hako.base.domain.database.dao.UserDao
|
||||
import com.hako.userlist.domain.datasource.UserDao
|
||||
import com.hako.userlist.domain.datasource.UserlistRemoteApi
|
||||
import com.hako.userlist.model.UserViewable
|
||||
import com.hako.userlist.model.User
|
||||
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 {
|
||||
|
||||
private val api: UserlistRemoteApi = get()
|
||||
class GetUsers(private val dao: UserDao, private val api: UserlistRemoteApi) {
|
||||
|
||||
fun execute(
|
||||
onSuccess: (List<UserViewable>) -> Unit,
|
||||
onSuccess: (List<User>) -> Unit,
|
||||
onError: (Throwable) -> Unit,
|
||||
onLoading: () -> Unit,
|
||||
onEmpty: () -> Unit
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.hako.userlist.domain.usecase
|
||||
|
||||
import com.hako.base.domain.database.dao.UserDao
|
||||
import com.hako.userlist.domain.datasource.UserDao
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
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.visible
|
||||
import com.hako.base.navigation.NavigationRouter
|
||||
import com.hako.base.navigation.ShowFabButton
|
||||
import com.hako.userlist.model.User
|
||||
import com.hako.userlist.viewmodel.UserlistViewmodel
|
||||
import com.hako.userlist.widget.UserlistAdapter
|
||||
import com.hako.userlist.R
|
||||
import com.hako.userlist.navigation.UserlistNavigation
|
||||
import kotlinx.android.synthetic.main.fragment_userlist.*
|
||||
import org.koin.android.ext.android.inject
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
import timber.log.Timber
|
||||
|
||||
abstract class BaseUserlistFragment : Fragment(), ShowFabButton {
|
||||
|
||||
val viewModel: UserlistViewmodel by viewModel()
|
||||
private val listAdapter by lazy { UserlistAdapter() }
|
||||
private val navigation: NavigationRouter by inject()
|
||||
|
||||
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()
|
||||
doRequest()
|
||||
}
|
||||
|
||||
abstract fun doRequest()
|
||||
|
||||
override fun fabButtonPressed(): () -> Unit = {
|
||||
navigation.sendNavigation(UserlistNavigation.ClickedOnFab)
|
||||
}
|
||||
|
||||
private fun setObservers() {
|
||||
viewModel.userList.observeNonNull(this) {
|
||||
it.either(::handleFetchError, ::handleFetchSuccess)
|
||||
}
|
||||
|
||||
viewModel.requestStatus.observeNonNull(this) {
|
||||
when (it) {
|
||||
RequestStatus.Ready -> {
|
||||
fragment_userlist_error_overlay.gone()
|
||||
fragment_userlist_loading_overlay.gone()
|
||||
fragment_userlist_empty_overlay.gone()
|
||||
}
|
||||
RequestStatus.Loading -> {
|
||||
fragment_userlist_error_overlay.gone()
|
||||
fragment_userlist_loading_overlay.visible()
|
||||
fragment_userlist_empty_overlay.gone()
|
||||
}
|
||||
RequestStatus.Errored -> {
|
||||
fragment_userlist_error_overlay.visible()
|
||||
fragment_userlist_loading_overlay.gone()
|
||||
fragment_userlist_empty_overlay.gone()
|
||||
}
|
||||
RequestStatus.Empty -> {
|
||||
fragment_userlist_error_overlay.gone()
|
||||
fragment_userlist_loading_overlay.gone()
|
||||
fragment_userlist_empty_overlay.visible()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.emptyMessage.observeNonNull(this) {
|
||||
fragment_userlist_empty_overlay.setLabel(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleFetchError(throwable: Throwable) {
|
||||
Timber.e(throwable)
|
||||
}
|
||||
|
||||
private fun handleFetchSuccess(users: List<User>) {
|
||||
listAdapter.addAll(users)
|
||||
}
|
||||
|
||||
private fun setRecycler() {
|
||||
fragment_userlist_recycler_container.apply {
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = listAdapter.apply {
|
||||
onItemClick = {
|
||||
navigation.sendNavigation(UserlistNavigation.ClickedOnUser(it.id, it.realName))
|
||||
}
|
||||
|
||||
onFavoriteClick = {
|
||||
viewModel.updateUserFavoriteStatus(it.id, !it.isFavorite)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
package com.hako.userlist.feature
|
||||
|
||||
class FavoriteUserlistFragment : UserlistFragment() {
|
||||
override fun doRequest() {
|
||||
viewModel.fetchFavoriteUsers()
|
||||
}
|
||||
class FavoriteUserlistFragment : BaseUserlistFragment() {
|
||||
override fun doRequest() = viewModel.fetchFavoriteUsers()
|
||||
|
||||
override fun shouldShowFabButton() = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,109 +1,7 @@
|
||||
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.visible
|
||||
import com.hako.base.navigation.NavigationRouter
|
||||
import com.hako.base.navigation.ShowFabButton
|
||||
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 com.hako.userlist.navigation.UserlistNavigation
|
||||
import kotlinx.android.synthetic.main.fragment_userlist.*
|
||||
import org.koin.android.ext.android.inject
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
import timber.log.Timber
|
||||
|
||||
open class UserlistFragment : Fragment(), ShowFabButton {
|
||||
|
||||
val viewModel: UserlistViewmodel by viewModel()
|
||||
private val listAdapter by lazy { UserlistAdapter() }
|
||||
private val navigation: NavigationRouter by inject()
|
||||
|
||||
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()
|
||||
doRequest()
|
||||
}
|
||||
|
||||
open fun doRequest() {
|
||||
viewModel.fetchUsers()
|
||||
}
|
||||
class UserlistFragment : BaseUserlistFragment() {
|
||||
override fun doRequest() = viewModel.fetchUsers()
|
||||
|
||||
override fun shouldShowFabButton() = true
|
||||
|
||||
override fun fabButtonPressed(): () -> Unit = {
|
||||
navigation.sendNavigation(UserlistNavigation.ClickedOnFab)
|
||||
}
|
||||
|
||||
private fun setObservers() {
|
||||
viewModel.userList.observeNonNull(this) {
|
||||
it.either(::handleFetchError, ::handleFetchSuccess)
|
||||
}
|
||||
|
||||
viewModel.requestStatus.observeNonNull(this) {
|
||||
when (it) {
|
||||
RequestStatus.Ready -> {
|
||||
fragment_userlist_error_overlay.gone()
|
||||
fragment_userlist_loading_overlay.gone()
|
||||
fragment_userlist_empty_overlay.gone()
|
||||
}
|
||||
RequestStatus.Loading -> {
|
||||
fragment_userlist_error_overlay.gone()
|
||||
fragment_userlist_loading_overlay.visible()
|
||||
fragment_userlist_empty_overlay.gone()
|
||||
}
|
||||
RequestStatus.Errored -> {
|
||||
fragment_userlist_error_overlay.visible()
|
||||
fragment_userlist_loading_overlay.gone()
|
||||
fragment_userlist_empty_overlay.gone()
|
||||
}
|
||||
RequestStatus.Empty -> {
|
||||
fragment_userlist_error_overlay.gone()
|
||||
fragment_userlist_loading_overlay.gone()
|
||||
fragment_userlist_empty_overlay.visible()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.emptyMessage.observeNonNull(this) {
|
||||
fragment_userlist_empty_overlay.setLabel(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleFetchError(throwable: Throwable) {
|
||||
Timber.e(throwable)
|
||||
}
|
||||
|
||||
private fun handleFetchSuccess(users: List<UserViewable>) {
|
||||
listAdapter.addAll(users)
|
||||
}
|
||||
|
||||
private fun setRecycler() {
|
||||
fragment_userlist_recycler_container.apply {
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = listAdapter.apply {
|
||||
onItemClick = {
|
||||
navigation.sendNavigation(UserlistNavigation.ClickedOnUser(it.id, it.realName))
|
||||
}
|
||||
|
||||
onFavoriteClick = {
|
||||
viewModel.updateUserFavoriteStatus(it.id, !it.isFavorite)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
package com.hako.userlist.model
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.hako.base.domain.database.entities.UserEntity
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
|
||||
@Parcelize
|
||||
data class User(
|
||||
data class UserRemote(
|
||||
@SerializedName("id") val id: Int,
|
||||
@SerializedName("name") val realName: String,
|
||||
@SerializedName("username") val userName: String,
|
||||
@@ -15,14 +17,30 @@ data class User(
|
||||
@SerializedName("website") val website: String
|
||||
) : Parcelable
|
||||
|
||||
data class UserViewable(
|
||||
@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,
|
||||
val isFavorite: Boolean = false
|
||||
) {
|
||||
companion object {
|
||||
const val TABLE_NAME = "users"
|
||||
}
|
||||
}
|
||||
|
||||
data class User(
|
||||
val id: Int,
|
||||
val realName: String,
|
||||
val userName: String,
|
||||
var isFavorite: Boolean
|
||||
)
|
||||
|
||||
fun User.toUserEntity() = UserEntity(this.id, this.realName, this.userName, this.email, this.phone, this.website)
|
||||
fun UserRemote.toUserEntity() = UserEntity(this.id, this.realName, this.userName, this.email, this.phone, this.website)
|
||||
|
||||
fun UserEntity.toUserViewable() = UserViewable(this.id, this.realName, this.userName, this.isFavorite)
|
||||
fun UserEntity.toUserViewable() = User(this.id, this.realName, this.userName, this.isFavorite)
|
||||
|
||||
|
||||
@@ -8,13 +8,13 @@ import com.hako.base.domain.network.RequestStatus.*
|
||||
import com.hako.userlist.domain.usecase.GetFavoriteUsers
|
||||
import com.hako.userlist.domain.usecase.GetUsers
|
||||
import com.hako.userlist.domain.usecase.SetFavoriteStatus
|
||||
import com.hako.userlist.model.UserViewable
|
||||
import com.hako.userlist.model.User
|
||||
import org.koin.core.KoinComponent
|
||||
import org.koin.core.get
|
||||
|
||||
class UserlistViewmodel : ViewModel(), KoinComponent {
|
||||
|
||||
val userList = MutableLiveData<Either<Throwable, List<UserViewable>>>()
|
||||
val userList = MutableLiveData<Either<Throwable, List<User>>>()
|
||||
val favoriteError = MutableLiveData<Int>()
|
||||
val emptyMessage = MutableLiveData<String>()
|
||||
val requestStatus = MutableLiveData<RequestStatus>()
|
||||
|
||||
@@ -5,20 +5,20 @@ 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 com.hako.userlist.model.User
|
||||
import com.hako.userlist.R
|
||||
import kotlinx.android.synthetic.main.item_user_card.view.*
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
class UserlistAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
private var items by Delegates.observable(emptyList<UserViewable>()) { _, oldList, newList ->
|
||||
private var items by Delegates.observable(emptyList<User>()) { _, oldList, newList ->
|
||||
autoNotify(oldList, newList) { old, new -> old.id == new.id }
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
var onItemClick: (UserViewable) -> Unit = { }
|
||||
var onFavoriteClick: (UserViewable) -> Unit = { }
|
||||
var onItemClick: (User) -> Unit = { }
|
||||
var onFavoriteClick: (User) -> Unit = { }
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder =
|
||||
UserViewHolder(
|
||||
@@ -31,7 +31,7 @@ class UserlistAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
fun getItem(position: Int) = items[position]
|
||||
|
||||
fun addAll(list: List<UserViewable>) {
|
||||
fun addAll(list: List<User>) {
|
||||
items = list
|
||||
}
|
||||
|
||||
@@ -46,12 +46,12 @@ class UserlistAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
class UserViewHolder(
|
||||
private val view: View,
|
||||
private val onItemClick: (UserViewable) -> Unit,
|
||||
private val onFavoriteClick: (UserViewable) -> Unit
|
||||
private val onItemClick: (User) -> Unit,
|
||||
private val onFavoriteClick: (User) -> Unit
|
||||
) :
|
||||
RecyclerView.ViewHolder(view) {
|
||||
|
||||
fun bind(user: UserViewable) = with(view) {
|
||||
fun bind(user: User) = with(view) {
|
||||
item_user_card_real_name.text = user.realName
|
||||
item_user_card_user_name.text = user.userName
|
||||
if (user.isFavorite) {
|
||||
|
||||
@@ -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
|
||||
|
||||
static def addRepos(RepositoryHandler handler) {
|
||||
handler.google()
|
||||
handler.jcenter()
|
||||
handler.maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
|
||||
}
|
||||
|
||||
ext.addRepos = this.&addRepos
|
||||