mirror of
https://github.com/imcarlost/Friendlists.git
synced 2026-04-10 02:46:54 -04:00
create module and implement it
This commit is contained in:
@@ -3,7 +3,7 @@ package com.hako.albumlist.domain.usecase
|
|||||||
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.AlbumViewable
|
||||||
import com.hako.albumlist.model.toAlbumEntity
|
import com.hako.albumlist.model.toAlbumEntity
|
||||||
import com.hako.albumlist.model.toUserViewable
|
import com.hako.albumlist.model.toAlbumViewable
|
||||||
import com.hako.base.domain.database.dao.AlbumDao
|
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
|
||||||
@@ -28,13 +28,13 @@ class GetAlbum(private val dao: AlbumDao) : KoinComponent {
|
|||||||
api.getAlbums(userId)
|
api.getAlbums(userId)
|
||||||
.doOnSuccess {
|
.doOnSuccess {
|
||||||
dao.saveAll(it.map { album -> album.toAlbumEntity() })
|
dao.saveAll(it.map { album -> album.toAlbumEntity() })
|
||||||
onSuccess(dao.getAlbums(userId).map { album -> album.toUserViewable() })
|
onSuccess(dao.getAlbums(userId).map { album -> album.toAlbumViewable() })
|
||||||
}
|
}
|
||||||
.doOnSubscribe { onLoading() }
|
.doOnSubscribe { onLoading() }
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.subscribe({}, { onError(it) })
|
.subscribe({}, { onError(it) })
|
||||||
} else {
|
} else {
|
||||||
onSuccess(dbAlbum.map { it.toUserViewable() })
|
onSuccess(dbAlbum.map { it.toAlbumViewable() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.subscribe()
|
.subscribe()
|
||||||
|
|||||||
@@ -20,5 +20,5 @@ data class AlbumViewable(
|
|||||||
|
|
||||||
fun Album.toAlbumEntity() = AlbumEntity(this.id, this.userId, this.title)
|
fun Album.toAlbumEntity() = AlbumEntity(this.id, this.userId, this.title)
|
||||||
|
|
||||||
fun AlbumEntity.toUserViewable() = AlbumViewable(this.id, this.userId, this.title)
|
fun AlbumEntity.toAlbumViewable() = AlbumViewable(this.id, this.userId, this.title)
|
||||||
|
|
||||||
|
|||||||
@@ -17,10 +17,10 @@ class AlbumlistViewmodel : ViewModel(), KoinComponent {
|
|||||||
val data = MutableLiveData<Either<Throwable, List<AlbumViewable>>>()
|
val data = MutableLiveData<Either<Throwable, List<AlbumViewable>>>()
|
||||||
val requestStatus = MutableLiveData<RequestStatus>()
|
val requestStatus = MutableLiveData<RequestStatus>()
|
||||||
|
|
||||||
private val getUsers: GetAlbum = get()
|
private val getAlbum: GetAlbum = get()
|
||||||
|
|
||||||
fun fetchAlbums(userId: Int) {
|
fun fetchAlbums(userId: Int) {
|
||||||
getUsers.execute(
|
getAlbum.execute(
|
||||||
userId,
|
userId,
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
requestStatus.postValue(Ready)
|
requestStatus.postValue(Ready)
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class AlbumlistAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
|||||||
var onItemClick: (AlbumViewable) -> Unit = { }
|
var onItemClick: (AlbumViewable) -> Unit = { }
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder =
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder =
|
||||||
UserViewHolder(
|
AlbumViewHolder(
|
||||||
LayoutInflater
|
LayoutInflater
|
||||||
.from(parent.context)
|
.from(parent.context)
|
||||||
.inflate(R.layout.item_album_card, parent, false),
|
.inflate(R.layout.item_album_card, parent, false),
|
||||||
@@ -37,12 +37,12 @@ class AlbumlistAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
|||||||
|
|
||||||
override fun onBindViewHolder(viewholder: RecyclerView.ViewHolder, position: Int) =
|
override fun onBindViewHolder(viewholder: RecyclerView.ViewHolder, position: Int) =
|
||||||
when (viewholder) {
|
when (viewholder) {
|
||||||
is UserViewHolder -> viewholder.bind(items[position])
|
is AlbumViewHolder -> viewholder.bind(items[position])
|
||||||
else -> throw NoWhenBranchMatchedException("Undefined viewholder")
|
else -> throw NoWhenBranchMatchedException("Undefined viewholder")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class UserViewHolder(private val view: View,
|
class AlbumViewHolder(private val view: View,
|
||||||
private val onItemClick: (AlbumViewable) -> Unit) :
|
private val onItemClick: (AlbumViewable) -> Unit) :
|
||||||
RecyclerView.ViewHolder(view) {
|
RecyclerView.ViewHolder(view) {
|
||||||
|
|
||||||
|
|||||||
@@ -14,12 +14,15 @@
|
|||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/item_album_card_album_name"
|
android:id="@+id/item_album_card_album_name"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="24dp"
|
android:layout_marginStart="24dp"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:ellipsize="end"
|
||||||
android:textSize="18sp"
|
android:textSize="18sp"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:text="Album name" />
|
tools:text="Album name" />
|
||||||
|
|||||||
@@ -9,6 +9,6 @@
|
|||||||
android:id="@+id/albumlistFragment"
|
android:id="@+id/albumlistFragment"
|
||||||
android:name="com.hako.albumlist.feature.AlbumlistFragment"
|
android:name="com.hako.albumlist.feature.AlbumlistFragment"
|
||||||
tools:layout="@layout/fragment_albumlist"
|
tools:layout="@layout/fragment_albumlist"
|
||||||
android:label="AlbumListFragment" />
|
android:label="AlbumlistFragment" />
|
||||||
|
|
||||||
</navigation>
|
</navigation>
|
||||||
@@ -50,4 +50,5 @@ dependencies {
|
|||||||
implementation project(":base")
|
implementation project(":base")
|
||||||
implementation project(":userlist")
|
implementation project(":userlist")
|
||||||
implementation project(":albumlist")
|
implementation project(":albumlist")
|
||||||
|
implementation project(":photolist")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import android.app.Application
|
|||||||
import com.hako.albumlist.di.albumListModules
|
import com.hako.albumlist.di.albumListModules
|
||||||
import com.hako.userlist.di.userlistModules
|
import com.hako.userlist.di.userlistModules
|
||||||
import com.hako.friendlists.di.appModules
|
import com.hako.friendlists.di.appModules
|
||||||
|
import com.hako.photolist.di.photoListModules
|
||||||
import org.koin.android.ext.koin.androidContext
|
import org.koin.android.ext.koin.androidContext
|
||||||
import org.koin.core.context.startKoin
|
import org.koin.core.context.startKoin
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
@@ -28,7 +29,8 @@ class MainApplication : Application() {
|
|||||||
listOf(
|
listOf(
|
||||||
appModules,
|
appModules,
|
||||||
userlistModules,
|
userlistModules,
|
||||||
albumListModules
|
albumListModules,
|
||||||
|
photoListModules
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,12 @@
|
|||||||
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:id="@+id/main_navigation"
|
android:id="@+id/main_navigation"
|
||||||
app:startDestination="@id/userlist_navigation">
|
app:startDestination="@id/photolist_navigation">
|
||||||
|
|
||||||
<include app:graph="@navigation/userlist_navigation" />
|
<include app:graph="@navigation/userlist_navigation" />
|
||||||
|
|
||||||
<include app:graph="@navigation/albumlist_navigation" />
|
<include app:graph="@navigation/albumlist_navigation" />
|
||||||
|
|
||||||
|
<include app:graph="@navigation/photolist_navigation" />
|
||||||
|
|
||||||
</navigation>
|
</navigation>
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
package com.hako.base
|
|
||||||
|
|
||||||
import androidx.test.platform.app.InstrumentationRegistry
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
||||||
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
|
|
||||||
import org.junit.Assert.*
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instrumented test, which will execute on an Android device.
|
|
||||||
*
|
|
||||||
* See [testing documentation](http://d.android.com/tools/testing).
|
|
||||||
*/
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
|
||||||
class ExampleInstrumentedTest {
|
|
||||||
@Test
|
|
||||||
fun useAppContext() {
|
|
||||||
// Context of the app under test.
|
|
||||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
|
||||||
assertEquals("com.hako.base.test", appContext.packageName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -18,6 +18,9 @@ interface PhotoDao {
|
|||||||
@get:Query("SELECT * FROM ${PhotoEntity.TABLE_NAME}")
|
@get:Query("SELECT * FROM ${PhotoEntity.TABLE_NAME}")
|
||||||
val all: List<PhotoEntity>
|
val all: List<PhotoEntity>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM ${PhotoEntity.TABLE_NAME} WHERE albumId = :albumId ORDER BY id ASC")
|
||||||
|
fun getPhotos(albumId: Int): List<PhotoEntity>
|
||||||
|
|
||||||
@Query("SELECT COUNT(*) FROM ${PhotoEntity.TABLE_NAME}")
|
@Query("SELECT COUNT(*) FROM ${PhotoEntity.TABLE_NAME}")
|
||||||
fun count(): Int
|
fun count(): Int
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
package com.hako.base
|
|
||||||
|
|
||||||
import org.junit.Test
|
|
||||||
|
|
||||||
import org.junit.Assert.*
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Example local unit test, which will execute on the development machine (host).
|
|
||||||
*
|
|
||||||
* See [testing documentation](http://d.android.com/tools/testing).
|
|
||||||
*/
|
|
||||||
class ExampleUnitTest {
|
|
||||||
@Test
|
|
||||||
fun addition_isCorrect() {
|
|
||||||
assertEquals(4, 2 + 2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1
photolist/.gitignore
vendored
Normal file
1
photolist/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
6
photolist/build.gradle
Normal file
6
photolist/build.gradle
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
apply plugin: 'com.android.library'
|
||||||
|
apply from: '../core.gradle'
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation project(':base')
|
||||||
|
}
|
||||||
0
photolist/consumer-rules.pro
Normal file
0
photolist/consumer-rules.pro
Normal file
21
photolist/proguard-rules.pro
vendored
Normal file
21
photolist/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
||||||
2
photolist/src/main/AndroidManifest.xml
Normal file
2
photolist/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.hako.photolist" />
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.hako.photolist.di
|
||||||
|
|
||||||
|
import com.hako.base.domain.network.RemoteClient
|
||||||
|
import com.hako.photolist.domain.datasource.PhotolistRemoteApi
|
||||||
|
import com.hako.photolist.domain.usecase.GetPhoto
|
||||||
|
import com.hako.photolist.viewmodel.PhotolistViewmodel
|
||||||
|
import org.koin.androidx.viewmodel.dsl.viewModel
|
||||||
|
import org.koin.dsl.module
|
||||||
|
|
||||||
|
val photoListModules = module {
|
||||||
|
factory { get<RemoteClient>().getClient(PhotolistRemoteApi::class.java) }
|
||||||
|
factory { GetPhoto(get()) }
|
||||||
|
|
||||||
|
viewModel { PhotolistViewmodel() }
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.hako.photolist.domain.datasource
|
||||||
|
|
||||||
|
import com.hako.photolist.model.Photo
|
||||||
|
import io.reactivex.Single
|
||||||
|
import retrofit2.http.GET
|
||||||
|
import retrofit2.http.Query
|
||||||
|
|
||||||
|
interface PhotolistRemoteApi {
|
||||||
|
|
||||||
|
@GET("/photos")
|
||||||
|
fun getPhotos(
|
||||||
|
@Query("albumId") albumId: Int
|
||||||
|
): Single<List<Photo>>
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package com.hako.photolist.domain.usecase
|
||||||
|
|
||||||
|
import com.hako.base.domain.database.dao.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()
|
||||||
|
|
||||||
|
fun execute(
|
||||||
|
albumId: Int,
|
||||||
|
onSuccess: (List<PhotoViewable>) -> Unit,
|
||||||
|
onError: (Throwable) -> Unit,
|
||||||
|
onLoading: () -> Unit
|
||||||
|
) {
|
||||||
|
Single.fromCallable { dao.getPhotos(albumId) }
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.doOnError { onError(it) }
|
||||||
|
.doOnSuccess { dbAlbum ->
|
||||||
|
if (dbAlbum.isEmpty() || dbAlbum.count() == 0) {
|
||||||
|
api.getPhotos(albumId)
|
||||||
|
.doOnSuccess {
|
||||||
|
dao.saveAll(it.map { album -> album.toPhotoEntity() })
|
||||||
|
onSuccess(dao.getPhotos(albumId).map { album -> album.toPhotoViewable() })
|
||||||
|
}
|
||||||
|
.doOnSubscribe { onLoading() }
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.subscribe({}, { onError(it) })
|
||||||
|
} else {
|
||||||
|
onSuccess(dbAlbum.map { it.toPhotoViewable() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.subscribe()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package com.hako.photolist.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.photolist.R
|
||||||
|
import com.hako.photolist.model.PhotoViewable
|
||||||
|
import com.hako.photolist.viewmodel.PhotolistViewmodel
|
||||||
|
import com.hako.photolist.widget.PhotolistAdapter
|
||||||
|
import kotlinx.android.synthetic.main.fragment_photolist.*
|
||||||
|
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
|
class PhotolistFragment : Fragment() {
|
||||||
|
|
||||||
|
private val viewModel: PhotolistViewmodel by viewModel()
|
||||||
|
private val listAdapter by lazy { PhotolistAdapter() }
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
||||||
|
): View = inflater.inflate(R.layout.fragment_photolist, container, false)
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
setRecycler()
|
||||||
|
setObservers()
|
||||||
|
// TODO: Get album by bundle
|
||||||
|
viewModel.fetchPhotos(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setObservers() {
|
||||||
|
viewModel.data.observeNonNull(this) {
|
||||||
|
it.either(::handleFetchError, ::handleFetchSuccess)
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.requestStatus.observeNonNull(this) {
|
||||||
|
when (it) {
|
||||||
|
RequestStatus.Ready -> {
|
||||||
|
fragment_photolist_error_overlay.gone()
|
||||||
|
fragment_photolist_loading_overlay.gone()
|
||||||
|
}
|
||||||
|
RequestStatus.Loading -> {
|
||||||
|
fragment_photolist_error_overlay.gone()
|
||||||
|
fragment_photolist_loading_overlay.visible()
|
||||||
|
}
|
||||||
|
RequestStatus.Errored -> {
|
||||||
|
fragment_photolist_error_overlay.visible()
|
||||||
|
fragment_photolist_loading_overlay.gone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleFetchError(throwable: Throwable) {
|
||||||
|
Timber.e(throwable)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleFetchSuccess(photos: List<PhotoViewable>) {
|
||||||
|
listAdapter.addAll(photos)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setRecycler() {
|
||||||
|
fragment_photolist_recycler_container.apply {
|
||||||
|
layoutManager = LinearLayoutManager(context)
|
||||||
|
adapter = listAdapter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.hako.photolist.model
|
||||||
|
|
||||||
|
import android.os.Parcelable
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
import com.hako.base.domain.database.entities.PhotoEntity
|
||||||
|
import kotlinx.android.parcel.Parcelize
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
data class Photo(
|
||||||
|
@SerializedName("id") val id: Int,
|
||||||
|
@SerializedName("albumId") val albumId: Int,
|
||||||
|
@SerializedName("title") val title: String,
|
||||||
|
@SerializedName("url") val photoUrl: String,
|
||||||
|
@SerializedName("thumbnailUrl") val thumbnailUrl: String
|
||||||
|
) : Parcelable
|
||||||
|
|
||||||
|
data class PhotoViewable(
|
||||||
|
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 PhotoEntity.toPhotoViewable() = PhotoViewable(this.id, this.albumId, this.title, this.photoUrl)
|
||||||
|
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package com.hako.photolist.viewmodel
|
||||||
|
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import com.hako.base.domain.network.RequestStatus
|
||||||
|
import com.hako.base.domain.network.RequestStatus.Ready
|
||||||
|
import com.hako.base.domain.network.RequestStatus.Loading
|
||||||
|
import com.hako.base.domain.network.RequestStatus.Errored
|
||||||
|
import com.hako.base.domain.Either
|
||||||
|
import com.hako.photolist.domain.usecase.GetPhoto
|
||||||
|
import com.hako.photolist.model.PhotoViewable
|
||||||
|
import org.koin.core.KoinComponent
|
||||||
|
import org.koin.core.get
|
||||||
|
|
||||||
|
class PhotolistViewmodel : ViewModel(), KoinComponent {
|
||||||
|
|
||||||
|
val data = MutableLiveData<Either<Throwable, List<PhotoViewable>>>()
|
||||||
|
val requestStatus = MutableLiveData<RequestStatus>()
|
||||||
|
|
||||||
|
private val getPhoto: GetPhoto = get()
|
||||||
|
|
||||||
|
fun fetchPhotos(albumId: Int) {
|
||||||
|
getPhoto.execute(
|
||||||
|
albumId,
|
||||||
|
onSuccess = {
|
||||||
|
requestStatus.postValue(Ready)
|
||||||
|
data.postValue(Either.Right(it))
|
||||||
|
},
|
||||||
|
onLoading = {
|
||||||
|
requestStatus.postValue(Loading)
|
||||||
|
},
|
||||||
|
onError = {
|
||||||
|
requestStatus.postValue(Errored)
|
||||||
|
data.postValue(Either.Left(it))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package com.hako.photolist.widget
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.hako.base.extensions.autoNotify
|
||||||
|
import com.hako.photolist.R
|
||||||
|
import com.hako.photolist.model.PhotoViewable
|
||||||
|
import kotlinx.android.synthetic.main.item_photo_card.view.*
|
||||||
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
|
class PhotolistAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
|
|
||||||
|
private var items by Delegates.observable(emptyList<PhotoViewable>()) { _, oldList, newList ->
|
||||||
|
autoNotify(oldList, newList) { old, new -> old.id == new.id }
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder =
|
||||||
|
PhotoViewHolder(
|
||||||
|
LayoutInflater
|
||||||
|
.from(parent.context)
|
||||||
|
.inflate(R.layout.item_photo_card, parent, false)
|
||||||
|
)
|
||||||
|
|
||||||
|
fun getItem(position: Int) = items[position]
|
||||||
|
|
||||||
|
fun addAll(list: List<PhotoViewable>) {
|
||||||
|
items = list
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount() = items.size
|
||||||
|
|
||||||
|
override fun onBindViewHolder(viewholder: RecyclerView.ViewHolder, position: Int) =
|
||||||
|
when (viewholder) {
|
||||||
|
is PhotoViewHolder -> viewholder.bind(items[position])
|
||||||
|
else -> throw NoWhenBranchMatchedException("Undefined viewholder")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PhotoViewHolder(private val view: View) :
|
||||||
|
RecyclerView.ViewHolder(view) {
|
||||||
|
|
||||||
|
fun bind(photo: PhotoViewable) = with(view) {
|
||||||
|
item_photo_card_title.text = photo.title
|
||||||
|
|
||||||
|
// TODO load image
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
photolist/src/main/res/drawable/img_photo_placeholder.png
Normal file
BIN
photolist/src/main/res/drawable/img_photo_placeholder.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.7 KiB |
38
photolist/src/main/res/layout/fragment_photolist.xml
Normal file
38
photolist/src/main/res/layout/fragment_photolist.xml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:id="@+id/fragment_photolist_base">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/fragment_photolist_recycler_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<com.hako.base.widgets.LoadingOverlay
|
||||||
|
android:id="@+id/fragment_photolist_loading_overlay"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<com.hako.base.widgets.NetworkErrorOverlay
|
||||||
|
android:id="@+id/fragment_photolist_error_overlay"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
50
photolist/src/main/res/layout/item_photo_card.xml
Normal file
50
photolist/src/main/res/layout/item_photo_card.xml
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/item_photo_card_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:background="@drawable/bg_card"
|
||||||
|
android:elevation="2dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/item_photo_card_photo"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:srcCompat="@drawable/img_photo_placeholder"
|
||||||
|
android:contentDescription="Album photo" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/item_photo_card_footer_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="70dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/item_photo_card_photo">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/item_photo_card_title"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:text="Album name" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
14
photolist/src/main/res/navigation/photolist_navigation.xml
Normal file
14
photolist/src/main/res/navigation/photolist_navigation.xml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/photolist_navigation"
|
||||||
|
app:startDestination="@id/photolistFragment">
|
||||||
|
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/photolistFragment"
|
||||||
|
android:name="com.hako.photolist.feature.PhotolistFragment"
|
||||||
|
tools:layout="@layout/fragment_photolist"
|
||||||
|
android:label="PhotolistFragment" />
|
||||||
|
|
||||||
|
</navigation>
|
||||||
3
photolist/src/main/res/values/strings.xml
Normal file
3
photolist/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<resources>
|
||||||
|
<string name="app_name">photolist</string>
|
||||||
|
</resources>
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
include ':app', ':base', ':userlist', ':albumlist'
|
include ':app', ':base', ':userlist', ':albumlist', ':photolist'
|
||||||
rootProject.name='Friendlists'
|
rootProject.name='Friendlists'
|
||||||
|
|||||||
@@ -3,12 +3,12 @@
|
|||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/userlist_navigation"
|
android:id="@+id/userlist_navigation"
|
||||||
app:startDestination="@id/userListFragment">
|
app:startDestination="@id/userlistFragment">
|
||||||
|
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/userListFragment"
|
android:id="@+id/userlistFragment"
|
||||||
android:name="com.hako.userlist.feature.UserlistFragment"
|
android:name="com.hako.userlist.feature.UserlistFragment"
|
||||||
tools:layout="@layout/fragment_userlist"
|
tools:layout="@layout/fragment_userlist"
|
||||||
android:label="UserListFragment" />
|
android:label="UserlistFragment" />
|
||||||
|
|
||||||
</navigation>
|
</navigation>
|
||||||
Reference in New Issue
Block a user