7 Commits

Author SHA1 Message Date
18f53543d4 Add codemagic 2024-02-26 12:34:09 -03:00
Carlos Martinez
ee869dacfc Merge pull request #13 from hakodeveloper/fix-userlist-fragments
Fix Userlist Hierarchy
2020-08-08 01:41:33 -04:00
Carlos Martinez
0fe35da8ba Change: fragment hieranchy 2020-08-08 01:32:05 -04:00
Carlos Martinez
a3e61a8297 Merge pull request #12 from hakodeveloper/structure/change-app-icon
Change app icon
2020-02-14 15:25:05 -03:00
Carlos Martinez
e14458589b change icon 2020-02-14 15:18:04 -03:00
Carlos Martinez
984602ec7e Merge pull request #11 from hakodeveloper/structure/clean-up-code
Migrate to multi-database structure
2020-02-13 15:01:40 -03:00
Carlos Martinez
e61c123068 migrate to multidatabase structure and clean up code 2020-02-13 12:40:28 -03:00
65 changed files with 336 additions and 301 deletions

View File

@@ -1,8 +1,12 @@
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
apply from: '../core.gradle' apply from: '../core.gradle'
android {
defaultConfig {
buildConfigField "String", "DB_NAME", '"albumlist.db"'
}
}
dependencies { dependencies {
implementation project(':base') implementation project(':base')
testImplementation project(':testing')
androidTestImplementation project(':testing')
} }

View File

@@ -1,5 +1,8 @@
package com.hako.albumlist.di 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.datasource.AlbumlistRemoteApi
import com.hako.albumlist.domain.usecase.GetAlbum import com.hako.albumlist.domain.usecase.GetAlbum
import com.hako.albumlist.viewmodel.AlbumlistViewmodel import com.hako.albumlist.viewmodel.AlbumlistViewmodel
@@ -8,8 +11,12 @@ import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module import org.koin.dsl.module
val albumListModules = 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 { get<RemoteClient>().getClient(AlbumlistRemoteApi::class.java) }
factory { GetAlbum(get()) } factory { GetAlbum(get(), get()) }
viewModel { AlbumlistViewmodel() } viewModel { AlbumlistViewmodel() }
} }

View File

@@ -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
}

View File

@@ -1,10 +1,10 @@
package com.hako.base.domain.database.dao package com.hako.albumlist.domain.datasource
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Insert import androidx.room.Insert
import androidx.room.OnConflictStrategy import androidx.room.OnConflictStrategy
import androidx.room.Query import androidx.room.Query
import com.hako.base.domain.database.entities.AlbumEntity import com.hako.albumlist.model.AlbumEntity
@Dao @Dao
interface AlbumDao { interface AlbumDao {

View File

@@ -1,6 +1,6 @@
package com.hako.albumlist.domain.datasource package com.hako.albumlist.domain.datasource
import com.hako.albumlist.model.Album import com.hako.albumlist.model.AlbumRemote
import io.reactivex.Single import io.reactivex.Single
import retrofit2.http.GET import retrofit2.http.GET
import retrofit2.http.Query import retrofit2.http.Query
@@ -10,5 +10,5 @@ interface AlbumlistRemoteApi {
@GET("/albums") @GET("/albums")
fun getAlbums( fun getAlbums(
@Query("userId") userId: Int @Query("userId") userId: Int
): Single<List<Album>> ): Single<List<AlbumRemote>>
} }

View File

@@ -1,22 +1,18 @@
package com.hako.albumlist.domain.usecase package com.hako.albumlist.domain.usecase
import com.hako.albumlist.domain.datasource.AlbumDao
import com.hako.albumlist.domain.datasource.AlbumlistRemoteApi 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.toAlbumEntity
import com.hako.albumlist.model.toAlbumViewable import com.hako.albumlist.model.toAlbumViewable
import com.hako.base.domain.database.dao.AlbumDao
import io.reactivex.Single import io.reactivex.Single
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import org.koin.core.KoinComponent
import org.koin.core.get
class GetAlbum(private val dao: AlbumDao) : KoinComponent { class GetAlbum(private val dao: AlbumDao, private val api: AlbumlistRemoteApi) {
private val api: AlbumlistRemoteApi = get()
fun execute( fun execute(
userId: Int, userId: Int,
onSuccess: (List<AlbumViewable>) -> Unit, onSuccess: (List<Album>) -> Unit,
onError: (Throwable) -> Unit, onError: (Throwable) -> Unit,
onLoading: () -> Unit onLoading: () -> Unit
) { ) {

View File

@@ -7,7 +7,7 @@ import android.view.ViewGroup
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.hako.albumlist.R 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.navigation.AlbumlistNavigation
import com.hako.albumlist.viewmodel.AlbumlistViewmodel import com.hako.albumlist.viewmodel.AlbumlistViewmodel
import com.hako.albumlist.widget.AlbumlistAdapter import com.hako.albumlist.widget.AlbumlistAdapter
@@ -72,7 +72,7 @@ class AlbumlistFragment : Fragment() {
Timber.e(throwable) Timber.e(throwable)
} }
private fun handleFetchSuccess(users: List<AlbumViewable>) { private fun handleFetchSuccess(users: List<Album>) {
listAdapter.addAll(users) listAdapter.addAll(users)
} }

View File

@@ -1,24 +1,38 @@
package com.hako.albumlist.model package com.hako.albumlist.model
import android.os.Parcelable import android.os.Parcelable
import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import com.hako.base.domain.database.entities.AlbumEntity
import kotlinx.android.parcel.Parcelize import kotlinx.android.parcel.Parcelize
@Parcelize @Parcelize
data class Album( data class AlbumRemote(
@SerializedName("id") val id: Int, @SerializedName("id") val id: Int,
@SerializedName("userId") val userId: Int, @SerializedName("userId") val userId: Int,
@SerializedName("title") val title: String @SerializedName("title") val title: String
) : Parcelable ) : 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 id: Int,
val userId: Int, val userId: Int,
val title: String 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)

View File

@@ -3,7 +3,7 @@ package com.hako.albumlist.viewmodel
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import com.hako.albumlist.domain.usecase.GetAlbum 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
import com.hako.base.domain.network.RequestStatus.Ready import com.hako.base.domain.network.RequestStatus.Ready
import com.hako.base.domain.network.RequestStatus.Loading import com.hako.base.domain.network.RequestStatus.Loading
@@ -14,7 +14,7 @@ import org.koin.core.get
class AlbumlistViewmodel : ViewModel(), KoinComponent { class AlbumlistViewmodel : ViewModel(), KoinComponent {
val data = MutableLiveData<Either<Throwable, List<AlbumViewable>>>() val data = MutableLiveData<Either<Throwable, List<Album>>>()
val requestStatus = MutableLiveData<RequestStatus>() val requestStatus = MutableLiveData<RequestStatus>()
private val getAlbum: GetAlbum = get() private val getAlbum: GetAlbum = get()

View File

@@ -5,19 +5,19 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.hako.albumlist.R import com.hako.albumlist.R
import com.hako.albumlist.model.AlbumViewable import com.hako.albumlist.model.Album
import com.hako.base.extensions.autoNotify import com.hako.base.extensions.autoNotify
import kotlinx.android.synthetic.main.item_album_card.view.* import kotlinx.android.synthetic.main.item_album_card.view.*
import kotlin.properties.Delegates import kotlin.properties.Delegates
class AlbumlistAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() { 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 } autoNotify(oldList, newList) { old, new -> old.id == new.id }
notifyDataSetChanged() notifyDataSetChanged()
} }
var onItemClick: (AlbumViewable) -> Unit = { } var onItemClick: (Album) -> Unit = { }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder = override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder =
AlbumViewHolder( AlbumViewHolder(
@@ -29,7 +29,7 @@ class AlbumlistAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
fun getItem(position: Int) = items[position] fun getItem(position: Int) = items[position]
fun addAll(list: List<AlbumViewable>) { fun addAll(list: List<Album>) {
items = list items = list
} }
@@ -43,10 +43,10 @@ class AlbumlistAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
} }
class AlbumViewHolder(private val view: View, class AlbumViewHolder(private val view: View,
private val onItemClick: (AlbumViewable) -> Unit) : private val onItemClick: (Album) -> Unit) :
RecyclerView.ViewHolder(view) { 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_album_name.text = album.title
item_album_card_container.setOnClickListener { item_album_card_container.setOnClickListener {
onItemClick(album) onItemClick(album)

View File

@@ -16,7 +16,6 @@ android {
versionName "1.0" versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
buildConfigField "String", "DB_NAME", '"friendlists.db"'
buildConfigField "String", "BASE_ENDPOINT", '"https://jsonplaceholder.typicode.com/"' buildConfigField "String", "BASE_ENDPOINT", '"https://jsonplaceholder.typicode.com/"'
} }
@@ -57,6 +56,4 @@ dependencies {
implementation project(":userlist") implementation project(":userlist")
implementation project(":albumlist") implementation project(":albumlist")
implementation project(":photolist") implementation project(":photolist")
testImplementation project(':testing')
androidTestImplementation project(':testing')
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@@ -1,7 +1,5 @@
package com.hako.friendlists.di 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.domain.network.RemoteClient
import com.hako.base.navigation.NavigationRouter import com.hako.base.navigation.NavigationRouter
import com.hako.friendlists.BuildConfig import com.hako.friendlists.BuildConfig
@@ -11,12 +9,6 @@ import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module import org.koin.dsl.module
val appModules = 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 // Retrofit
single { RemoteClient(BuildConfig.BASE_ENDPOINT) } single { RemoteClient(BuildConfig.BASE_ENDPOINT) }

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" /> <background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground" /> <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon> </adaptive-icon>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" /> <background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground" /> <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon> </adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#FFFFFF</color>
</resources>

View File

@@ -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
}

View File

@@ -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"
}
}

View File

@@ -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"
}
}

View File

@@ -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
codemagic.yaml Normal file
View File

@@ -0,0 +1 @@

View File

@@ -30,4 +30,8 @@ android {
lintOptions { lintOptions {
abortOnError false abortOnError false
} }
}
dependencies {
kapt deps.room.compiler
} }

View File

@@ -1,8 +1,12 @@
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
apply from: '../core.gradle' apply from: '../core.gradle'
android {
defaultConfig {
buildConfigField "String", "DB_NAME", '"photolist.db"'
}
}
dependencies { dependencies {
implementation project(':base') implementation project(':base')
testImplementation project(':testing')
androidTestImplementation project(':testing')
} }

View File

@@ -1,6 +1,9 @@
package com.hako.photolist.di package com.hako.photolist.di
import androidx.room.Room
import com.hako.base.domain.network.RemoteClient 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.datasource.PhotolistRemoteApi
import com.hako.photolist.domain.usecase.GetPhoto import com.hako.photolist.domain.usecase.GetPhoto
import com.hako.photolist.viewmodel.PhotolistViewmodel import com.hako.photolist.viewmodel.PhotolistViewmodel
@@ -8,8 +11,12 @@ import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module import org.koin.dsl.module
val photoListModules = 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 { get<RemoteClient>().getClient(PhotolistRemoteApi::class.java) }
factory { GetPhoto(get()) } factory { GetPhoto(get(), get()) }
viewModel { PhotolistViewmodel() } viewModel { PhotolistViewmodel() }
} }

View File

@@ -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
}

View File

@@ -1,10 +1,10 @@
package com.hako.base.domain.database.dao package com.hako.photolist.domain.datasource
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Insert import androidx.room.Insert
import androidx.room.OnConflictStrategy import androidx.room.OnConflictStrategy
import androidx.room.Query import androidx.room.Query
import com.hako.base.domain.database.entities.PhotoEntity import com.hako.photolist.model.PhotoEntity
@Dao @Dao
interface PhotoDao { interface PhotoDao {

View File

@@ -1,6 +1,6 @@
package com.hako.photolist.domain.datasource package com.hako.photolist.domain.datasource
import com.hako.photolist.model.Photo import com.hako.photolist.model.PhotoRemote
import io.reactivex.Single import io.reactivex.Single
import retrofit2.http.GET import retrofit2.http.GET
import retrofit2.http.Query import retrofit2.http.Query
@@ -10,5 +10,5 @@ interface PhotolistRemoteApi {
@GET("/photos") @GET("/photos")
fun getPhotos( fun getPhotos(
@Query("albumId") albumId: Int @Query("albumId") albumId: Int
): Single<List<Photo>> ): Single<List<PhotoRemote>>
} }

View File

@@ -1,20 +1,16 @@
package com.hako.photolist.domain.usecase 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.domain.datasource.PhotolistRemoteApi
import com.hako.photolist.model.* import com.hako.photolist.model.*
import io.reactivex.Single import io.reactivex.Single
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import org.koin.core.KoinComponent
import org.koin.core.get
class GetPhoto(private val dao: PhotoDao) : KoinComponent { class GetPhoto(private val dao: PhotoDao, private val api: PhotolistRemoteApi) {
private val api: PhotolistRemoteApi = get()
fun execute( fun execute(
albumId: Int, albumId: Int,
onSuccess: (List<PhotoViewable>) -> Unit, onSuccess: (List<Photo>) -> Unit,
onError: (Throwable) -> Unit, onError: (Throwable) -> Unit,
onLoading: () -> Unit onLoading: () -> Unit
) { ) {

View File

@@ -11,7 +11,7 @@ import com.hako.base.extensions.gone
import com.hako.base.extensions.observeNonNull import com.hako.base.extensions.observeNonNull
import com.hako.base.extensions.visible import com.hako.base.extensions.visible
import com.hako.photolist.R 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.viewmodel.PhotolistViewmodel
import com.hako.photolist.widget.PhotolistAdapter import com.hako.photolist.widget.PhotolistAdapter
import kotlinx.android.synthetic.main.fragment_photolist.* import kotlinx.android.synthetic.main.fragment_photolist.*
@@ -68,7 +68,7 @@ class PhotolistFragment : Fragment() {
Timber.e(throwable) Timber.e(throwable)
} }
private fun handleFetchSuccess(photos: List<PhotoViewable>) { private fun handleFetchSuccess(photos: List<Photo>) {
listAdapter.addAll(photos) listAdapter.addAll(photos)
} }

View File

@@ -1,12 +1,14 @@
package com.hako.photolist.model package com.hako.photolist.model
import android.os.Parcelable import android.os.Parcelable
import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import com.hako.base.domain.database.entities.PhotoEntity
import kotlinx.android.parcel.Parcelize import kotlinx.android.parcel.Parcelize
@Parcelize @Parcelize
data class Photo( data class PhotoRemote(
@SerializedName("id") val id: Int, @SerializedName("id") val id: Int,
@SerializedName("albumId") val albumId: Int, @SerializedName("albumId") val albumId: Int,
@SerializedName("title") val title: String, @SerializedName("title") val title: String,
@@ -14,14 +16,28 @@ data class Photo(
@SerializedName("thumbnailUrl") val thumbnailUrl: String @SerializedName("thumbnailUrl") val thumbnailUrl: String
) : Parcelable ) : 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 id: Int,
val albumId: Int, val albumId: Int,
val title: String, val title: String,
val photoUrl: 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)

View File

@@ -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.network.RequestStatus.Errored
import com.hako.base.domain.Either import com.hako.base.domain.Either
import com.hako.photolist.domain.usecase.GetPhoto 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.KoinComponent
import org.koin.core.get import org.koin.core.get
class PhotolistViewmodel : ViewModel(), KoinComponent { class PhotolistViewmodel : ViewModel(), KoinComponent {
val data = MutableLiveData<Either<Throwable, List<PhotoViewable>>>() val data = MutableLiveData<Either<Throwable, List<Photo>>>()
val requestStatus = MutableLiveData<RequestStatus>() val requestStatus = MutableLiveData<RequestStatus>()
private val getPhoto: GetPhoto = get() private val getPhoto: GetPhoto = get()

View File

@@ -6,7 +6,7 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.hako.base.extensions.autoNotify import com.hako.base.extensions.autoNotify
import com.hako.photolist.R import com.hako.photolist.R
import com.hako.photolist.model.PhotoViewable import com.hako.photolist.model.Photo
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
import kotlinx.android.synthetic.main.item_photo_card.view.* import kotlinx.android.synthetic.main.item_photo_card.view.*
import org.koin.core.KoinComponent import org.koin.core.KoinComponent
@@ -15,7 +15,7 @@ import kotlin.properties.Delegates
class PhotolistAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() { 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 } autoNotify(oldList, newList) { old, new -> old.id == new.id }
notifyDataSetChanged() notifyDataSetChanged()
} }
@@ -29,7 +29,7 @@ class PhotolistAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
fun getItem(position: Int) = items[position] fun getItem(position: Int) = items[position]
fun addAll(list: List<PhotoViewable>) { fun addAll(list: List<Photo>) {
items = list items = list
} }
@@ -47,7 +47,7 @@ class PhotoViewHolder(private val view: View) :
private val picasso: Picasso by inject() private val picasso: Picasso by inject()
fun bind(photo: PhotoViewable) = with(view) { fun bind(photo: Photo) = with(view) {
picasso.load(photo.photoUrl) picasso.load(photo.photoUrl)
.placeholder(R.drawable.img_photo_placeholder) .placeholder(R.drawable.img_photo_placeholder)
.fit() .fit()

View File

@@ -1,6 +1,12 @@
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
apply from: '../core.gradle' apply from: '../core.gradle'
android {
defaultConfig {
buildConfigField "String", "DB_NAME", '"userlist.db"'
}
}
dependencies { dependencies {
implementation project(':base') implementation project(':base')
testImplementation project(':testing') testImplementation project(':testing')

View File

@@ -1,9 +1,9 @@
package com.hako.userlist.feature package com.hako.userlist.feature
import com.hako.base.domain.database.dao.UserDao import com.hako.userlist.domain.datasource.UserDao
import com.hako.base.domain.database.entities.UserEntity
import com.hako.userlist.domain.datasource.UserlistRemoteApi 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 import io.reactivex.Single
class MockUserDao(private val userList: List<UserEntity>) : UserDao { class MockUserDao(private val userList: List<UserEntity>) : UserDao {
@@ -22,8 +22,8 @@ class MockUserDao(private val userList: List<UserEntity>) : UserDao {
override fun nukeDatabase() {} 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() } override fun getUsers() = Single.fromCallable { getAllUsers() }
private fun getAllUsers() = userList private fun getAllUsers() = userRemoteList
} }

View File

@@ -2,14 +2,14 @@ package com.hako.userlist.feature
import androidx.fragment.app.testing.launchFragmentInContainer import androidx.fragment.app.testing.launchFragmentInContainer
import androidx.test.platform.app.InstrumentationRegistry 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.testing.isTextDisplayed
import com.hako.userlist.domain.datasource.UserDao
import com.hako.userlist.domain.datasource.UserlistRemoteApi import com.hako.userlist.domain.datasource.UserlistRemoteApi
import com.hako.userlist.domain.usecase.GetFavoriteUsers import com.hako.userlist.domain.usecase.GetFavoriteUsers
import com.hako.userlist.domain.usecase.GetUsers import com.hako.userlist.domain.usecase.GetUsers
import com.hako.userlist.domain.usecase.SetFavoriteStatus 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.model.toUserEntity
import com.hako.userlist.viewmodel.UserlistViewmodel import com.hako.userlist.viewmodel.UserlistViewmodel
import org.junit.After import org.junit.After
@@ -28,7 +28,7 @@ class UserlistFragmentTest {
startKoin { startKoin {
InstrumentationRegistry.getInstrumentation().targetContext InstrumentationRegistry.getInstrumentation().targetContext
modules(module { modules(module {
factory { GetUsers(get()) } factory { GetUsers(get(), get()) }
factory { GetFavoriteUsers(get()) } factory { GetFavoriteUsers(get()) }
factory { SetFavoriteStatus(get()) } factory { SetFavoriteStatus(get()) }
viewModel { UserlistViewmodel() } viewModel { UserlistViewmodel() }
@@ -92,7 +92,7 @@ class UserlistRobot {
} }
private fun loadTwoBasicUsers() = listOf( private fun loadTwoBasicUsers() = listOf(
User( UserRemote(
1, 1,
"Marian Arriaga", "Marian Arriaga",
"mariancita", "mariancita",
@@ -100,7 +100,7 @@ class UserlistRobot {
"+56873912", "+56873912",
"www.test.com" "www.test.com"
), ),
User( UserRemote(
2, 2,
"Carlos Martinez", "Carlos Martinez",
"carlitos", "carlitos",

View File

@@ -1,2 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.hako.friendlist_userlist" /> package="com.hako.userlist" />

View File

@@ -1,6 +1,9 @@
package com.hako.userlist.di package com.hako.userlist.di
import androidx.room.Room
import com.hako.base.domain.network.RemoteClient 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.datasource.UserlistRemoteApi
import com.hako.userlist.domain.usecase.GetFavoriteUsers import com.hako.userlist.domain.usecase.GetFavoriteUsers
import com.hako.userlist.domain.usecase.GetUsers import com.hako.userlist.domain.usecase.GetUsers
@@ -10,8 +13,12 @@ import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module import org.koin.dsl.module
val userlistModules = 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 { get<RemoteClient>().getClient(UserlistRemoteApi::class.java) }
factory { GetUsers(get()) } factory { GetUsers(get(), get()) }
factory { GetFavoriteUsers(get()) } factory { GetFavoriteUsers(get()) }
factory { SetFavoriteStatus(get()) } factory { SetFavoriteStatus(get()) }

View File

@@ -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
}

View File

@@ -1,10 +1,10 @@
package com.hako.base.domain.database.dao package com.hako.userlist.domain.datasource
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Insert import androidx.room.Insert
import androidx.room.OnConflictStrategy import androidx.room.OnConflictStrategy
import androidx.room.Query import androidx.room.Query
import com.hako.base.domain.database.entities.UserEntity import com.hako.userlist.model.UserEntity
@Dao @Dao
interface UserDao { interface UserDao {

View File

@@ -1,11 +1,11 @@
package com.hako.userlist.domain.datasource package com.hako.userlist.domain.datasource
import com.hako.userlist.model.User import com.hako.userlist.model.UserRemote
import io.reactivex.Single import io.reactivex.Single
import retrofit2.http.GET import retrofit2.http.GET
interface UserlistRemoteApi { interface UserlistRemoteApi {
@GET("/users") @GET("/users")
fun getUsers(): Single<List<User>> fun getUsers(): Single<List<UserRemote>>
} }

View File

@@ -1,7 +1,7 @@
package com.hako.userlist.domain.usecase 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.model.UserViewable import com.hako.userlist.model.User
import com.hako.userlist.model.toUserViewable import com.hako.userlist.model.toUserViewable
import io.reactivex.Single import io.reactivex.Single
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
@@ -9,7 +9,7 @@ import io.reactivex.schedulers.Schedulers
class GetFavoriteUsers(private val dao: UserDao) { class GetFavoriteUsers(private val dao: UserDao) {
fun execute( fun execute(
onSuccess: (List<UserViewable>) -> Unit, onSuccess: (List<User>) -> Unit,
onEmpty: () -> Unit, onEmpty: () -> Unit,
onError: (Throwable) -> Unit onError: (Throwable) -> Unit
) { ) {

View File

@@ -1,21 +1,17 @@
package com.hako.userlist.domain.usecase 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.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.toUserEntity
import com.hako.userlist.model.toUserViewable import com.hako.userlist.model.toUserViewable
import io.reactivex.Single import io.reactivex.Single
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import org.koin.core.KoinComponent
import org.koin.core.get
class GetUsers(private val dao: UserDao) : KoinComponent { class GetUsers(private val dao: UserDao, private val api: UserlistRemoteApi) {
private val api: UserlistRemoteApi = get()
fun execute( fun execute(
onSuccess: (List<UserViewable>) -> Unit, onSuccess: (List<User>) -> Unit,
onError: (Throwable) -> Unit, onError: (Throwable) -> Unit,
onLoading: () -> Unit, onLoading: () -> Unit,
onEmpty: () -> Unit onEmpty: () -> Unit

View File

@@ -1,6 +1,6 @@
package com.hako.userlist.domain.usecase 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.Single
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers

View File

@@ -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)
}
}
}
}
}

View File

@@ -1,9 +1,7 @@
package com.hako.userlist.feature package com.hako.userlist.feature
class FavoriteUserlistFragment : UserlistFragment() { class FavoriteUserlistFragment : BaseUserlistFragment() {
override fun doRequest() { override fun doRequest() = viewModel.fetchFavoriteUsers()
viewModel.fetchFavoriteUsers()
}
override fun shouldShowFabButton() = false override fun shouldShowFabButton() = false
} }

View File

@@ -1,109 +1,7 @@
package com.hako.userlist.feature package com.hako.userlist.feature
import android.os.Bundle class UserlistFragment : BaseUserlistFragment() {
import android.view.LayoutInflater override fun doRequest() = viewModel.fetchUsers()
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()
}
override fun shouldShowFabButton() = true 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)
}
}
}
}
}

View File

@@ -1,12 +1,14 @@
package com.hako.userlist.model package com.hako.userlist.model
import android.os.Parcelable import android.os.Parcelable
import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import com.hako.base.domain.database.entities.UserEntity
import kotlinx.android.parcel.Parcelize import kotlinx.android.parcel.Parcelize
@Parcelize @Parcelize
data class User( data class UserRemote(
@SerializedName("id") val id: Int, @SerializedName("id") val id: Int,
@SerializedName("name") val realName: String, @SerializedName("name") val realName: String,
@SerializedName("username") val userName: String, @SerializedName("username") val userName: String,
@@ -15,14 +17,30 @@ data class User(
@SerializedName("website") val website: String @SerializedName("website") val website: String
) : Parcelable ) : 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 id: Int,
val realName: String, val realName: String,
val userName: String, val userName: String,
var isFavorite: Boolean 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)

View File

@@ -8,13 +8,13 @@ import com.hako.base.domain.network.RequestStatus.*
import com.hako.userlist.domain.usecase.GetFavoriteUsers import com.hako.userlist.domain.usecase.GetFavoriteUsers
import com.hako.userlist.domain.usecase.GetUsers import com.hako.userlist.domain.usecase.GetUsers
import com.hako.userlist.domain.usecase.SetFavoriteStatus 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.KoinComponent
import org.koin.core.get import org.koin.core.get
class UserlistViewmodel : ViewModel(), KoinComponent { class UserlistViewmodel : ViewModel(), KoinComponent {
val userList = MutableLiveData<Either<Throwable, List<UserViewable>>>() val userList = MutableLiveData<Either<Throwable, List<User>>>()
val favoriteError = MutableLiveData<Int>() val favoriteError = MutableLiveData<Int>()
val emptyMessage = MutableLiveData<String>() val emptyMessage = MutableLiveData<String>()
val requestStatus = MutableLiveData<RequestStatus>() val requestStatus = MutableLiveData<RequestStatus>()

View File

@@ -5,20 +5,20 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.hako.base.extensions.autoNotify import com.hako.base.extensions.autoNotify
import com.hako.userlist.model.UserViewable import com.hako.userlist.model.User
import com.hako.friendlist_userlist.R import com.hako.userlist.R
import kotlinx.android.synthetic.main.item_user_card.view.* import kotlinx.android.synthetic.main.item_user_card.view.*
import kotlin.properties.Delegates import kotlin.properties.Delegates
class UserlistAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() { 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 } autoNotify(oldList, newList) { old, new -> old.id == new.id }
notifyDataSetChanged() notifyDataSetChanged()
} }
var onItemClick: (UserViewable) -> Unit = { } var onItemClick: (User) -> Unit = { }
var onFavoriteClick: (UserViewable) -> Unit = { } var onFavoriteClick: (User) -> Unit = { }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder = override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder =
UserViewHolder( UserViewHolder(
@@ -31,7 +31,7 @@ class UserlistAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
fun getItem(position: Int) = items[position] fun getItem(position: Int) = items[position]
fun addAll(list: List<UserViewable>) { fun addAll(list: List<User>) {
items = list items = list
} }
@@ -46,12 +46,12 @@ class UserlistAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
class UserViewHolder( class UserViewHolder(
private val view: View, private val view: View,
private val onItemClick: (UserViewable) -> Unit, private val onItemClick: (User) -> Unit,
private val onFavoriteClick: (UserViewable) -> Unit private val onFavoriteClick: (User) -> Unit
) : ) :
RecyclerView.ViewHolder(view) { 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_real_name.text = user.realName
item_user_card_user_name.text = user.userName item_user_card_user_name.text = user.userName
if (user.isFavorite) { if (user.isFavorite) {

View File

@@ -3,6 +3,7 @@ ext.deps = [:]
def versions = [:] def versions = [:]
versions.kotlin = "1.3.41" versions.kotlin = "1.3.41"
versions.gradle = "3.5.3" versions.gradle = "3.5.3"
versions.room = "2.2.3"
def deps = [:] def deps = [:]
@@ -22,6 +23,11 @@ kotlin.version = "$versions.kotlin"
kotlin.std_lib = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$versions.kotlin" kotlin.std_lib = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$versions.kotlin"
deps.kotlin = kotlin deps.kotlin = kotlin
def room = [:]
room.runtime = "androidx.room:room-runtime:$versions.room"
room.compiler = "androidx.room:room-compiler:$versions.room"
deps.room = room
ext.deps = deps ext.deps = deps
static def addRepos(RepositoryHandler handler) { static def addRepos(RepositoryHandler handler) {