Refactoring

This commit is contained in:
2021-09-06 23:00:01 +02:00
parent 605659a224
commit 1367955396
17 changed files with 114 additions and 86 deletions
@@ -6,6 +6,7 @@ import com.ericampire.android.androidstudycase.app.room.AppDatabase
import com.ericampire.android.androidstudycase.data.room.AnimatorDao import com.ericampire.android.androidstudycase.data.room.AnimatorDao
import com.ericampire.android.androidstudycase.data.room.BlogDao import com.ericampire.android.androidstudycase.data.room.BlogDao
import com.ericampire.android.androidstudycase.data.room.LottieFilesDao import com.ericampire.android.androidstudycase.data.room.LottieFilesDao
import com.ericampire.android.androidstudycase.data.room.UserDao
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
@@ -25,6 +26,11 @@ object RoomModule {
return db.build() return db.build()
} }
@Provides
fun provideUserDao(appDatabase: AppDatabase): UserDao {
return appDatabase.userDao
}
@Provides @Provides
fun provideBlogDao(appDatabase: AppDatabase): BlogDao { fun provideBlogDao(appDatabase: AppDatabase): BlogDao {
return appDatabase.blogDao return appDatabase.blogDao
@@ -2,22 +2,23 @@ package com.ericampire.android.androidstudycase.app.room
import androidx.room.Database import androidx.room.Database
import androidx.room.RoomDatabase import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import com.ericampire.android.androidstudycase.data.room.AnimatorDao import com.ericampire.android.androidstudycase.data.room.AnimatorDao
import com.ericampire.android.androidstudycase.data.room.BlogDao import com.ericampire.android.androidstudycase.data.room.BlogDao
import com.ericampire.android.androidstudycase.data.room.LottieFilesDao import com.ericampire.android.androidstudycase.data.room.LottieFilesDao
import com.ericampire.android.androidstudycase.data.room.UserDao
import com.ericampire.android.androidstudycase.domain.entity.Animator import com.ericampire.android.androidstudycase.domain.entity.Animator
import com.ericampire.android.androidstudycase.domain.entity.Blog import com.ericampire.android.androidstudycase.domain.entity.Blog
import com.ericampire.android.androidstudycase.domain.entity.Lottiefile import com.ericampire.android.androidstudycase.domain.entity.Lottiefile
import com.ericampire.android.androidstudycase.util.room.DateConverter import com.ericampire.android.androidstudycase.domain.entity.User
@Database( @Database(
entities = [Blog::class, Animator::class, Lottiefile::class], entities = [Blog::class, Animator::class, Lottiefile::class, User::class],
version = 2, version = 2,
exportSchema = false exportSchema = false
) )
abstract class AppDatabase : RoomDatabase() { abstract class AppDatabase : RoomDatabase() {
abstract val blogDao: BlogDao abstract val blogDao: BlogDao
abstract val userDao: UserDao
abstract val animatorDao: AnimatorDao abstract val animatorDao: AnimatorDao
abstract val lottieFileDao: LottieFilesDao abstract val lottieFileDao: LottieFilesDao
} }
@@ -2,7 +2,6 @@ package com.ericampire.android.androidstudycase.presentation.screen.home.ui
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.material.Card import androidx.compose.material.Card
import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
@@ -12,13 +11,11 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.airbnb.lottie.compose.* import com.airbnb.lottie.compose.*
import com.ericampire.android.androidstudycase.domain.entity.Lottiefile import com.ericampire.android.androidstudycase.domain.entity.Lottiefile
import com.ericampire.android.androidstudycase.presentation.screen.explore.ui.LottieFileItemView
import com.ericampire.android.androidstudycase.presentation.theme.AndroidStudyCaseTheme import com.ericampire.android.androidstudycase.presentation.theme.AndroidStudyCaseTheme
import com.ericampire.android.androidstudycase.presentation.theme.AppColor import com.ericampire.android.androidstudycase.presentation.theme.AppColor
import com.ericampire.android.androidstudycase.util.LottieFileProvider import com.ericampire.android.androidstudycase.util.LottieFileProvider
@@ -70,13 +67,11 @@ fun FeaturedLottieFileView(
maxLines = 1, maxLines = 1,
text = lottiefile.name, text = lottiefile.name,
style = MaterialTheme.typography.h6, style = MaterialTheme.typography.h6,
textAlign = TextAlign.Center,
) )
Text( Text(
text = lottiefile.createdBy?.name ?: "", text = lottiefile.createdBy?.name ?: "",
maxLines = 1, maxLines = 1,
style = MaterialTheme.typography.caption, style = MaterialTheme.typography.caption,
textAlign = TextAlign.Center,
) )
} }
) )
@@ -5,6 +5,8 @@ import androidx.compose.ui.graphics.Color
object AppColor { object AppColor {
val Teal = Color(0xFF1C7373)
val PaleBlue = Color(0xFFD3F6F6)
val PrimaryColor = Color(0xFF2BEAED) val PrimaryColor = Color(0xFF2BEAED)
val PrimaryColorDark = Color(0xFF006B78) val PrimaryColorDark = Color(0xFF006B78)
val Purple700 = Color(0xFF3700B3) val Purple700 = Color(0xFF3700B3)
@@ -1,9 +1,11 @@
package com.ericampire.android.androidstudycase.data.datasource.animator package com.ericampire.android.androidstudycase.data.datasource.animator
import com.ericampire.android.androidstudycase.domain.entity.Animator import com.ericampire.android.androidstudycase.domain.entity.Animator
import com.ericampire.android.androidstudycase.util.Result
import kotlinx.coroutines.flow.Flow
interface AnimatorDataSource { interface AnimatorDataSource {
suspend fun findAll(): List<Animator> fun findAll(): Flow<Result<List<Animator>>>
suspend fun save(animator: Animator) suspend fun save(animator: Animator)
} }
@@ -2,14 +2,19 @@ package com.ericampire.android.androidstudycase.data.datasource.animator
import com.ericampire.android.androidstudycase.data.room.AnimatorDao import com.ericampire.android.androidstudycase.data.room.AnimatorDao
import com.ericampire.android.androidstudycase.domain.entity.Animator import com.ericampire.android.androidstudycase.domain.entity.Animator
import com.ericampire.android.androidstudycase.util.Result
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import javax.inject.Inject import javax.inject.Inject
class LocalAnimatorDataSource @Inject constructor( class LocalAnimatorDataSource @Inject constructor(
private val animatorDao: AnimatorDao private val animatorDao: AnimatorDao
) : AnimatorDataSource { ) : AnimatorDataSource {
override suspend fun findAll(): List<Animator> { override fun findAll(): Flow<Result<List<Animator>>> {
return animatorDao.findAll() return animatorDao.findAll().map {
Result.Success(it)
}
} }
override suspend fun save(animator: Animator) { override suspend fun save(animator: Animator) {
@@ -13,9 +13,16 @@ import javax.inject.Inject
class RemoteAnimatorDataSource @Inject constructor( class RemoteAnimatorDataSource @Inject constructor(
private val httpClient: HttpClient private val httpClient: HttpClient
) : AnimatorDataSource { ) : AnimatorDataSource {
override suspend fun findAll(): List<Animator> {
val data = httpClient.get<AnimatorApiResponse>(ApiUrl.Animator.featured) override fun findAll(): Flow<Result<List<Animator>>> {
return data.animatorAnimatorData.featuredAnimators.results return flow {
try {
val data = httpClient.get<AnimatorApiResponse>(ApiUrl.Animator.featured)
emit(Result.Success(data.animatorAnimatorData.featuredAnimators.results))
} catch (e: Exception) {
emit(Result.Error(e))
}
}
} }
override suspend fun save(animator: Animator) { override suspend fun save(animator: Animator) {
@@ -9,9 +9,12 @@ import com.ericampire.android.androidstudycase.data.datasource.blog.RemoteBlogDa
import com.ericampire.android.androidstudycase.data.datasource.lottiefiles.LocalLottieFileDataSource import com.ericampire.android.androidstudycase.data.datasource.lottiefiles.LocalLottieFileDataSource
import com.ericampire.android.androidstudycase.data.datasource.lottiefiles.LottieFileDataSource import com.ericampire.android.androidstudycase.data.datasource.lottiefiles.LottieFileDataSource
import com.ericampire.android.androidstudycase.data.datasource.lottiefiles.RemoteLottieFileDataSource import com.ericampire.android.androidstudycase.data.datasource.lottiefiles.RemoteLottieFileDataSource
import com.ericampire.android.androidstudycase.data.datasource.user.LocalUserDataSource
import com.ericampire.android.androidstudycase.data.datasource.user.UserDataSource
import com.ericampire.android.androidstudycase.data.room.AnimatorDao import com.ericampire.android.androidstudycase.data.room.AnimatorDao
import com.ericampire.android.androidstudycase.data.room.BlogDao import com.ericampire.android.androidstudycase.data.room.BlogDao
import com.ericampire.android.androidstudycase.data.room.LottieFilesDao import com.ericampire.android.androidstudycase.data.room.LottieFilesDao
import com.ericampire.android.androidstudycase.data.room.UserDao
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
@@ -62,6 +65,13 @@ object DataSourceModule {
return LocalBlogDataSource(dao) return LocalBlogDataSource(dao)
} }
@Provides
fun provideLocalUserDataSource(
dao: UserDao
): UserDataSource {
return LocalUserDataSource(dao)
}
@Provides @Provides
fun provideRemoteAnimatorDataSource( fun provideRemoteAnimatorDataSource(
httpClient: HttpClient httpClient: HttpClient
@@ -6,12 +6,15 @@ import com.ericampire.android.androidstudycase.data.datasource.blog.LocalBlogDat
import com.ericampire.android.androidstudycase.data.datasource.blog.RemoteBlogDataSource import com.ericampire.android.androidstudycase.data.datasource.blog.RemoteBlogDataSource
import com.ericampire.android.androidstudycase.data.datasource.lottiefiles.LocalLottieFileDataSource import com.ericampire.android.androidstudycase.data.datasource.lottiefiles.LocalLottieFileDataSource
import com.ericampire.android.androidstudycase.data.datasource.lottiefiles.RemoteLottieFileDataSource import com.ericampire.android.androidstudycase.data.datasource.lottiefiles.RemoteLottieFileDataSource
import com.ericampire.android.androidstudycase.data.datasource.user.LocalUserDataSource
import com.ericampire.android.androidstudycase.data.repository.AnimatorRepositoryImpl import com.ericampire.android.androidstudycase.data.repository.AnimatorRepositoryImpl
import com.ericampire.android.androidstudycase.data.repository.BlogRepositoryImpl import com.ericampire.android.androidstudycase.data.repository.BlogRepositoryImpl
import com.ericampire.android.androidstudycase.data.repository.LottieFileRepositoryImpl import com.ericampire.android.androidstudycase.data.repository.LottieFileRepositoryImpl
import com.ericampire.android.androidstudycase.data.repository.UserRepositoryImpl
import com.ericampire.android.androidstudycase.domain.repository.AnimatorRepository import com.ericampire.android.androidstudycase.domain.repository.AnimatorRepository
import com.ericampire.android.androidstudycase.domain.repository.BlogRepository import com.ericampire.android.androidstudycase.domain.repository.BlogRepository
import com.ericampire.android.androidstudycase.domain.repository.LottieFileRepository import com.ericampire.android.androidstudycase.domain.repository.LottieFileRepository
import com.ericampire.android.androidstudycase.domain.repository.UserRepository
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
@@ -26,18 +29,27 @@ object RepositoryModule {
fun provideAnimatorRepository( fun provideAnimatorRepository(
localDataSource: LocalAnimatorDataSource, localDataSource: LocalAnimatorDataSource,
remoteDataSource: RemoteAnimatorDataSource, remoteDataSource: RemoteAnimatorDataSource,
) : AnimatorRepository { ): AnimatorRepository {
return AnimatorRepositoryImpl( return AnimatorRepositoryImpl(
localDataSource = localDataSource, localDataSource = localDataSource,
remoteDataSource = remoteDataSource remoteDataSource = remoteDataSource
) )
} }
@Provides
fun provideUserRepository(
localDataSource: LocalUserDataSource,
): UserRepository {
return UserRepositoryImpl(
localDataSource = localDataSource,
)
}
@Provides @Provides
fun provideLottieFileRepository( fun provideLottieFileRepository(
localDataSource: LocalLottieFileDataSource, localDataSource: LocalLottieFileDataSource,
remoteDataSource: RemoteLottieFileDataSource, remoteDataSource: RemoteLottieFileDataSource,
) : LottieFileRepository { ): LottieFileRepository {
return LottieFileRepositoryImpl( return LottieFileRepositoryImpl(
localDataSource = localDataSource, localDataSource = localDataSource,
remoteDataSource = remoteDataSource remoteDataSource = remoteDataSource
@@ -6,8 +6,6 @@ import com.ericampire.android.androidstudycase.domain.repository.AnimatorReposit
import com.ericampire.android.androidstudycase.util.Result import com.ericampire.android.androidstudycase.util.Result
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import javax.inject.Inject import javax.inject.Inject
@@ -16,19 +14,19 @@ class AnimatorRepositoryImpl @Inject constructor(
private val localDataSource: AnimatorDataSource, private val localDataSource: AnimatorDataSource,
) : AnimatorRepository { ) : AnimatorRepository {
override fun findAll(): Flow<Result<List<Animator>>> { override fun findAll(): Flow<Result<List<Animator>>> {
return flow { refreshData()
try { return localDataSource.findAll()
refreshData() }
emit(Result.Success(localDataSource.findAll()))
} catch (e: Exception) { private fun refreshData() {
emit(Result.Error(e)) suspend {
remoteDataSource.findAll().collect {
if (it is Result.Success) {
it.data.forEach { animator ->
localDataSource.save(animator)
}
}
} }
} }
} }
private suspend fun refreshData() {
remoteDataSource.findAll().forEach {
localDataSource.save(it)
}
}
} }
@@ -1,14 +1,11 @@
package com.ericampire.android.androidstudycase.data.repository package com.ericampire.android.androidstudycase.data.repository
import com.ericampire.android.androidstudycase.data.datasource.animator.AnimatorDataSource
import com.ericampire.android.androidstudycase.data.datasource.blog.BlogDataSource import com.ericampire.android.androidstudycase.data.datasource.blog.BlogDataSource
import com.ericampire.android.androidstudycase.domain.entity.Animator
import com.ericampire.android.androidstudycase.domain.entity.Blog import com.ericampire.android.androidstudycase.domain.entity.Blog
import com.ericampire.android.androidstudycase.domain.repository.AnimatorRepository
import com.ericampire.android.androidstudycase.domain.repository.BlogRepository import com.ericampire.android.androidstudycase.domain.repository.BlogRepository
import com.ericampire.android.androidstudycase.util.Result import com.ericampire.android.androidstudycase.util.Result
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.collect
import javax.inject.Inject import javax.inject.Inject
class BlogRepositoryImpl @Inject constructor( class BlogRepositoryImpl @Inject constructor(
@@ -16,19 +13,19 @@ class BlogRepositoryImpl @Inject constructor(
private val localDataSource: BlogDataSource, private val localDataSource: BlogDataSource,
) : BlogRepository { ) : BlogRepository {
override fun findAll(): Flow<Result<List<Blog>>> { override fun findAll(): Flow<Result<List<Blog>>> {
return flow { refreshData()
try { return localDataSource.findAll()
refreshData() }
emit(Result.Success(localDataSource.findAll()))
} catch (e: Exception) { private fun refreshData() {
emit(Result.Error(e)) suspend {
remoteDataSource.findAll().collect {
if (it is Result.Success) {
it.data.forEach { blog ->
localDataSource.save(blog)
}
}
} }
} }
} }
private suspend fun refreshData() {
remoteDataSource.findAll().forEach {
localDataSource.save(it)
}
}
} }
@@ -5,52 +5,39 @@ import com.ericampire.android.androidstudycase.domain.entity.Lottiefile
import com.ericampire.android.androidstudycase.domain.repository.LottieFileRepository import com.ericampire.android.androidstudycase.domain.repository.LottieFileRepository
import com.ericampire.android.androidstudycase.util.Result import com.ericampire.android.androidstudycase.util.Result
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.collect
import javax.inject.Inject import javax.inject.Inject
class LottieFileRepositoryImpl @Inject constructor( class LottieFileRepositoryImpl @Inject constructor(
private val localDataSource: LottieFileDataSource, private val localDataSource: LottieFileDataSource,
private val remoteDataSource: LottieFileDataSource, private val remoteDataSource: LottieFileDataSource,
) : LottieFileRepository { ) : LottieFileRepository {
override fun findRecent(): Flow<Result<List<Lottiefile>>> { override fun findRecent(): Flow<Result<List<Lottiefile>>> {
return flow { val recentFiles = remoteDataSource.findRecent()
try { refreshData(recentFiles)
val recentFiles = remoteDataSource.findRecent() return recentFiles
refreshData(recentFiles)
emit(Result.Success(localDataSource.findRecent()))
} catch (e: Exception) {
emit(Result.Error(e))
}
}
} }
private suspend fun refreshData(data: List<Lottiefile>) { private fun refreshData(data: Flow<Result<List<Lottiefile>>>) {
data.forEach { suspend {
localDataSource.save(it) data.collect {
if (it is Result.Success) {
it.data.forEach { file -> localDataSource.save(file) }
}
}
} }
} }
override fun findPopular(): Flow<Result<List<Lottiefile>>> { override fun findPopular(): Flow<Result<List<Lottiefile>>> {
return flow { val files = remoteDataSource.findPopular()
try { refreshData(files)
val recentFiles = remoteDataSource.findPopular() return files
refreshData(recentFiles)
emit(Result.Success(localDataSource.findPopular()))
} catch (e: Exception) {
emit(Result.Error(e))
}
}
} }
override fun findFeatured(): Flow<Result<List<Lottiefile>>> { override fun findFeatured(): Flow<Result<List<Lottiefile>>> {
return flow { val files = remoteDataSource.findFeatured()
try { refreshData(files)
val recentFiles = remoteDataSource.findFeatured() return files
refreshData(recentFiles)
emit(Result.Success(localDataSource.findFeatured()))
} catch (e: Exception) {
emit(Result.Error(e))
}
}
} }
} }
@@ -5,6 +5,7 @@ import androidx.room.Insert
import androidx.room.OnConflictStrategy import androidx.room.OnConflictStrategy
import androidx.room.Query import androidx.room.Query
import com.ericampire.android.androidstudycase.domain.entity.Animator import com.ericampire.android.androidstudycase.domain.entity.Animator
import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface AnimatorDao { interface AnimatorDao {
@@ -13,5 +14,5 @@ interface AnimatorDao {
suspend fun save(animator: Animator) suspend fun save(animator: Animator)
@Query("SELECT * FROM Animator") @Query("SELECT * FROM Animator")
suspend fun findAll(): List<Animator> fun findAll(): Flow<List<Animator>>
} }
@@ -5,6 +5,7 @@ import androidx.room.Insert
import androidx.room.OnConflictStrategy import androidx.room.OnConflictStrategy
import androidx.room.Query import androidx.room.Query
import com.ericampire.android.androidstudycase.domain.entity.Lottiefile import com.ericampire.android.androidstudycase.domain.entity.Lottiefile
import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface LottieFilesDao { interface LottieFilesDao {
@@ -12,11 +13,11 @@ interface LottieFilesDao {
suspend fun save(lottiefile: Lottiefile) suspend fun save(lottiefile: Lottiefile)
@Query("SELECT * FROM Lottiefile") @Query("SELECT * FROM Lottiefile")
suspend fun findPopular(): List<Lottiefile> fun findPopular(): Flow<List<Lottiefile>>
@Query("SELECT * FROM Lottiefile") @Query("SELECT * FROM Lottiefile")
suspend fun findFeatured(): List<Lottiefile> fun findFeatured(): Flow<List<Lottiefile>>
@Query("SELECT * FROM Lottiefile") @Query("SELECT * FROM Lottiefile")
suspend fun findRecent(): List<Lottiefile> fun findRecent(): Flow<List<Lottiefile>>
} }
@@ -1,7 +1,8 @@
package com.ericampire.android.androidstudycase.domain.entity package com.ericampire.android.androidstudycase.domain.entity
import androidx.room.ColumnInfo
import androidx.room.Embedded
import androidx.room.Entity import androidx.room.Entity
import androidx.room.Ignore
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@@ -40,11 +41,9 @@ data class Lottiefile(
var gifUrl: String? = "", var gifUrl: String? = "",
var videoUrl: String? = "", var videoUrl: String? = "",
var imageUrl: String? = "", var imageUrl: String? = "",
var name: String = "", @ColumnInfo(name = "file_name") var name: String = "",
// @TypeConverters(DateConverter::class)
// @Serializable(with = DateSerializer::class)
var createdAt: String = "", var createdAt: String = "",
@Ignore var createdBy: Animator? = null @Embedded var createdBy: Animator? = null
) )
@@ -0,0 +1,3 @@
package com.ericampire.android.androidstudycase.domain.usecase
internal class SaveUserUseCaseTest
+2
View File
@@ -12,4 +12,6 @@
<string name="txt_browse_all">Browse all free Animation</string> <string name="txt_browse_all">Browse all free Animation</string>
<string name="txt_browse_all_desc">Access to 1000s of Lottie animations</string> <string name="txt_browse_all_desc">Access to 1000s of Lottie animations</string>
<string name="txt_go_to_explore">Go to Explore</string> <string name="txt_go_to_explore">Go to Explore</string>
<string name="txt_login">Login</string>
<string name="txt_hello_stranger">Hello Stranger</string>
</resources> </resources>