diff --git a/app/src/main/java/com/ericampire/android/androidstudycase/app/di/RoomModule.kt b/app/src/main/java/com/ericampire/android/androidstudycase/app/di/RoomModule.kt index 2a543b2..4702c10 100644 --- a/app/src/main/java/com/ericampire/android/androidstudycase/app/di/RoomModule.kt +++ b/app/src/main/java/com/ericampire/android/androidstudycase/app/di/RoomModule.kt @@ -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.BlogDao import com.ericampire.android.androidstudycase.data.room.LottieFilesDao +import com.ericampire.android.androidstudycase.data.room.UserDao import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -25,6 +26,11 @@ object RoomModule { return db.build() } + @Provides + fun provideUserDao(appDatabase: AppDatabase): UserDao { + return appDatabase.userDao + } + @Provides fun provideBlogDao(appDatabase: AppDatabase): BlogDao { return appDatabase.blogDao diff --git a/app/src/main/java/com/ericampire/android/androidstudycase/app/room/AppDatabase.kt b/app/src/main/java/com/ericampire/android/androidstudycase/app/room/AppDatabase.kt index ad42854..429d1db 100644 --- a/app/src/main/java/com/ericampire/android/androidstudycase/app/room/AppDatabase.kt +++ b/app/src/main/java/com/ericampire/android/androidstudycase/app/room/AppDatabase.kt @@ -2,22 +2,23 @@ package com.ericampire.android.androidstudycase.app.room import androidx.room.Database import androidx.room.RoomDatabase -import androidx.room.TypeConverters import com.ericampire.android.androidstudycase.data.room.AnimatorDao import com.ericampire.android.androidstudycase.data.room.BlogDao 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.Blog 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( - entities = [Blog::class, Animator::class, Lottiefile::class], + entities = [Blog::class, Animator::class, Lottiefile::class, User::class], version = 2, exportSchema = false ) abstract class AppDatabase : RoomDatabase() { abstract val blogDao: BlogDao + abstract val userDao: UserDao abstract val animatorDao: AnimatorDao abstract val lottieFileDao: LottieFilesDao } \ No newline at end of file diff --git a/app/src/main/java/com/ericampire/android/androidstudycase/presentation/screen/home/ui/FeaturedLottieFileView.kt b/app/src/main/java/com/ericampire/android/androidstudycase/presentation/screen/home/ui/FeaturedLottieFileView.kt index ce616d6..fec6709 100644 --- a/app/src/main/java/com/ericampire/android/androidstudycase/presentation/screen/home/ui/FeaturedLottieFileView.kt +++ b/app/src/main/java/com/ericampire/android/androidstudycase/presentation/screen/home/ui/FeaturedLottieFileView.kt @@ -2,7 +2,6 @@ package com.ericampire.android.androidstudycase.presentation.screen.home.ui import androidx.compose.foundation.background import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.CornerSize import androidx.compose.material.Card import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.MaterialTheme @@ -12,13 +11,11 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier 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.PreviewParameter import androidx.compose.ui.unit.dp import com.airbnb.lottie.compose.* 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.AppColor import com.ericampire.android.androidstudycase.util.LottieFileProvider @@ -70,13 +67,11 @@ fun FeaturedLottieFileView( maxLines = 1, text = lottiefile.name, style = MaterialTheme.typography.h6, - textAlign = TextAlign.Center, ) Text( text = lottiefile.createdBy?.name ?: "", maxLines = 1, style = MaterialTheme.typography.caption, - textAlign = TextAlign.Center, ) } ) diff --git a/app/src/main/java/com/ericampire/android/androidstudycase/presentation/theme/Color.kt b/app/src/main/java/com/ericampire/android/androidstudycase/presentation/theme/Color.kt index 19ad51b..a358ff4 100644 --- a/app/src/main/java/com/ericampire/android/androidstudycase/presentation/theme/Color.kt +++ b/app/src/main/java/com/ericampire/android/androidstudycase/presentation/theme/Color.kt @@ -5,6 +5,8 @@ import androidx.compose.ui.graphics.Color object AppColor { + val Teal = Color(0xFF1C7373) + val PaleBlue = Color(0xFFD3F6F6) val PrimaryColor = Color(0xFF2BEAED) val PrimaryColorDark = Color(0xFF006B78) val Purple700 = Color(0xFF3700B3) diff --git a/data/src/main/java/com/ericampire/android/androidstudycase/data/datasource/animator/AnimatorDataSource.kt b/data/src/main/java/com/ericampire/android/androidstudycase/data/datasource/animator/AnimatorDataSource.kt index 33619a3..88bdc9e 100644 --- a/data/src/main/java/com/ericampire/android/androidstudycase/data/datasource/animator/AnimatorDataSource.kt +++ b/data/src/main/java/com/ericampire/android/androidstudycase/data/datasource/animator/AnimatorDataSource.kt @@ -1,9 +1,11 @@ package com.ericampire.android.androidstudycase.data.datasource.animator import com.ericampire.android.androidstudycase.domain.entity.Animator +import com.ericampire.android.androidstudycase.util.Result +import kotlinx.coroutines.flow.Flow interface AnimatorDataSource { - suspend fun findAll(): List + fun findAll(): Flow>> suspend fun save(animator: Animator) } \ No newline at end of file diff --git a/data/src/main/java/com/ericampire/android/androidstudycase/data/datasource/animator/LocalAnimatorDataSource.kt b/data/src/main/java/com/ericampire/android/androidstudycase/data/datasource/animator/LocalAnimatorDataSource.kt index 3ab5bfe..a9aa6e5 100644 --- a/data/src/main/java/com/ericampire/android/androidstudycase/data/datasource/animator/LocalAnimatorDataSource.kt +++ b/data/src/main/java/com/ericampire/android/androidstudycase/data/datasource/animator/LocalAnimatorDataSource.kt @@ -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.domain.entity.Animator +import com.ericampire.android.androidstudycase.util.Result +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map import javax.inject.Inject class LocalAnimatorDataSource @Inject constructor( private val animatorDao: AnimatorDao ) : AnimatorDataSource { - override suspend fun findAll(): List { - return animatorDao.findAll() + override fun findAll(): Flow>> { + return animatorDao.findAll().map { + Result.Success(it) + } } override suspend fun save(animator: Animator) { diff --git a/data/src/main/java/com/ericampire/android/androidstudycase/data/datasource/animator/RemoteAnimatorDataSource.kt b/data/src/main/java/com/ericampire/android/androidstudycase/data/datasource/animator/RemoteAnimatorDataSource.kt index fd4a517..25cddfa 100644 --- a/data/src/main/java/com/ericampire/android/androidstudycase/data/datasource/animator/RemoteAnimatorDataSource.kt +++ b/data/src/main/java/com/ericampire/android/androidstudycase/data/datasource/animator/RemoteAnimatorDataSource.kt @@ -13,9 +13,16 @@ import javax.inject.Inject class RemoteAnimatorDataSource @Inject constructor( private val httpClient: HttpClient ) : AnimatorDataSource { - override suspend fun findAll(): List { - val data = httpClient.get(ApiUrl.Animator.featured) - return data.animatorAnimatorData.featuredAnimators.results + + override fun findAll(): Flow>> { + return flow { + try { + val data = httpClient.get(ApiUrl.Animator.featured) + emit(Result.Success(data.animatorAnimatorData.featuredAnimators.results)) + } catch (e: Exception) { + emit(Result.Error(e)) + } + } } override suspend fun save(animator: Animator) { diff --git a/data/src/main/java/com/ericampire/android/androidstudycase/data/di/DataSourceModule.kt b/data/src/main/java/com/ericampire/android/androidstudycase/data/di/DataSourceModule.kt index 4c1c7bd..f1eb7eb 100644 --- a/data/src/main/java/com/ericampire/android/androidstudycase/data/di/DataSourceModule.kt +++ b/data/src/main/java/com/ericampire/android/androidstudycase/data/di/DataSourceModule.kt @@ -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.LottieFileDataSource 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.BlogDao import com.ericampire.android.androidstudycase.data.room.LottieFilesDao +import com.ericampire.android.androidstudycase.data.room.UserDao import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -62,6 +65,13 @@ object DataSourceModule { return LocalBlogDataSource(dao) } + @Provides + fun provideLocalUserDataSource( + dao: UserDao + ): UserDataSource { + return LocalUserDataSource(dao) + } + @Provides fun provideRemoteAnimatorDataSource( httpClient: HttpClient diff --git a/data/src/main/java/com/ericampire/android/androidstudycase/data/di/RepositoryModule.kt b/data/src/main/java/com/ericampire/android/androidstudycase/data/di/RepositoryModule.kt index c2a6aad..10ba4d6 100644 --- a/data/src/main/java/com/ericampire/android/androidstudycase/data/di/RepositoryModule.kt +++ b/data/src/main/java/com/ericampire/android/androidstudycase/data/di/RepositoryModule.kt @@ -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.lottiefiles.LocalLottieFileDataSource 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.BlogRepositoryImpl 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.BlogRepository import com.ericampire.android.androidstudycase.domain.repository.LottieFileRepository +import com.ericampire.android.androidstudycase.domain.repository.UserRepository import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -26,18 +29,27 @@ object RepositoryModule { fun provideAnimatorRepository( localDataSource: LocalAnimatorDataSource, remoteDataSource: RemoteAnimatorDataSource, - ) : AnimatorRepository { + ): AnimatorRepository { return AnimatorRepositoryImpl( localDataSource = localDataSource, remoteDataSource = remoteDataSource ) } + @Provides + fun provideUserRepository( + localDataSource: LocalUserDataSource, + ): UserRepository { + return UserRepositoryImpl( + localDataSource = localDataSource, + ) + } + @Provides fun provideLottieFileRepository( localDataSource: LocalLottieFileDataSource, remoteDataSource: RemoteLottieFileDataSource, - ) : LottieFileRepository { + ): LottieFileRepository { return LottieFileRepositoryImpl( localDataSource = localDataSource, remoteDataSource = remoteDataSource diff --git a/data/src/main/java/com/ericampire/android/androidstudycase/data/repository/AnimatorRepositoryImpl.kt b/data/src/main/java/com/ericampire/android/androidstudycase/data/repository/AnimatorRepositoryImpl.kt index 28cffc1..d6699bc 100644 --- a/data/src/main/java/com/ericampire/android/androidstudycase/data/repository/AnimatorRepositoryImpl.kt +++ b/data/src/main/java/com/ericampire/android/androidstudycase/data/repository/AnimatorRepositoryImpl.kt @@ -6,8 +6,6 @@ import com.ericampire.android.androidstudycase.domain.repository.AnimatorReposit import com.ericampire.android.androidstudycase.util.Result import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.map import javax.inject.Inject @@ -16,19 +14,19 @@ class AnimatorRepositoryImpl @Inject constructor( private val localDataSource: AnimatorDataSource, ) : AnimatorRepository { override fun findAll(): Flow>> { - return flow { - try { - refreshData() - emit(Result.Success(localDataSource.findAll())) - } catch (e: Exception) { - emit(Result.Error(e)) + refreshData() + return localDataSource.findAll() + } + + private fun refreshData() { + 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) - } - } } \ No newline at end of file diff --git a/data/src/main/java/com/ericampire/android/androidstudycase/data/repository/BlogRepositoryImpl.kt b/data/src/main/java/com/ericampire/android/androidstudycase/data/repository/BlogRepositoryImpl.kt index 2f9c89d..6253384 100644 --- a/data/src/main/java/com/ericampire/android/androidstudycase/data/repository/BlogRepositoryImpl.kt +++ b/data/src/main/java/com/ericampire/android/androidstudycase/data/repository/BlogRepositoryImpl.kt @@ -1,14 +1,11 @@ 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.domain.entity.Animator 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.util.Result import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.collect import javax.inject.Inject class BlogRepositoryImpl @Inject constructor( @@ -16,19 +13,19 @@ class BlogRepositoryImpl @Inject constructor( private val localDataSource: BlogDataSource, ) : BlogRepository { override fun findAll(): Flow>> { - return flow { - try { - refreshData() - emit(Result.Success(localDataSource.findAll())) - } catch (e: Exception) { - emit(Result.Error(e)) + refreshData() + return localDataSource.findAll() + } + + private fun refreshData() { + 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) - } - } } \ No newline at end of file diff --git a/data/src/main/java/com/ericampire/android/androidstudycase/data/repository/LottieFileRepositoryImpl.kt b/data/src/main/java/com/ericampire/android/androidstudycase/data/repository/LottieFileRepositoryImpl.kt index 46875c7..20ee702 100644 --- a/data/src/main/java/com/ericampire/android/androidstudycase/data/repository/LottieFileRepositoryImpl.kt +++ b/data/src/main/java/com/ericampire/android/androidstudycase/data/repository/LottieFileRepositoryImpl.kt @@ -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.util.Result import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.collect import javax.inject.Inject class LottieFileRepositoryImpl @Inject constructor( private val localDataSource: LottieFileDataSource, private val remoteDataSource: LottieFileDataSource, ) : LottieFileRepository { + override fun findRecent(): Flow>> { - return flow { - try { - val recentFiles = remoteDataSource.findRecent() - refreshData(recentFiles) - emit(Result.Success(localDataSource.findRecent())) - } catch (e: Exception) { - emit(Result.Error(e)) - } - } + val recentFiles = remoteDataSource.findRecent() + refreshData(recentFiles) + return recentFiles } - private suspend fun refreshData(data: List) { - data.forEach { - localDataSource.save(it) + private fun refreshData(data: Flow>>) { + suspend { + data.collect { + if (it is Result.Success) { + it.data.forEach { file -> localDataSource.save(file) } + } + } } } override fun findPopular(): Flow>> { - return flow { - try { - val recentFiles = remoteDataSource.findPopular() - refreshData(recentFiles) - emit(Result.Success(localDataSource.findPopular())) - } catch (e: Exception) { - emit(Result.Error(e)) - } - } + val files = remoteDataSource.findPopular() + refreshData(files) + return files } override fun findFeatured(): Flow>> { - return flow { - try { - val recentFiles = remoteDataSource.findFeatured() - refreshData(recentFiles) - emit(Result.Success(localDataSource.findFeatured())) - } catch (e: Exception) { - emit(Result.Error(e)) - } - } + val files = remoteDataSource.findFeatured() + refreshData(files) + return files } } \ No newline at end of file diff --git a/data/src/main/java/com/ericampire/android/androidstudycase/data/room/AnimatorDao.kt b/data/src/main/java/com/ericampire/android/androidstudycase/data/room/AnimatorDao.kt index ab34562..e2cf6af 100644 --- a/data/src/main/java/com/ericampire/android/androidstudycase/data/room/AnimatorDao.kt +++ b/data/src/main/java/com/ericampire/android/androidstudycase/data/room/AnimatorDao.kt @@ -5,6 +5,7 @@ import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query import com.ericampire.android.androidstudycase.domain.entity.Animator +import kotlinx.coroutines.flow.Flow @Dao interface AnimatorDao { @@ -13,5 +14,5 @@ interface AnimatorDao { suspend fun save(animator: Animator) @Query("SELECT * FROM Animator") - suspend fun findAll(): List + fun findAll(): Flow> } \ No newline at end of file diff --git a/data/src/main/java/com/ericampire/android/androidstudycase/data/room/LottieFilesDao.kt b/data/src/main/java/com/ericampire/android/androidstudycase/data/room/LottieFilesDao.kt index ff45cea..b148139 100644 --- a/data/src/main/java/com/ericampire/android/androidstudycase/data/room/LottieFilesDao.kt +++ b/data/src/main/java/com/ericampire/android/androidstudycase/data/room/LottieFilesDao.kt @@ -5,6 +5,7 @@ import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query import com.ericampire.android.androidstudycase.domain.entity.Lottiefile +import kotlinx.coroutines.flow.Flow @Dao interface LottieFilesDao { @@ -12,11 +13,11 @@ interface LottieFilesDao { suspend fun save(lottiefile: Lottiefile) @Query("SELECT * FROM Lottiefile") - suspend fun findPopular(): List + fun findPopular(): Flow> @Query("SELECT * FROM Lottiefile") - suspend fun findFeatured(): List + fun findFeatured(): Flow> @Query("SELECT * FROM Lottiefile") - suspend fun findRecent(): List + fun findRecent(): Flow> } \ No newline at end of file diff --git a/domain/src/main/java/com/ericampire/android/androidstudycase/domain/entity/Lottiefile.kt b/domain/src/main/java/com/ericampire/android/androidstudycase/domain/entity/Lottiefile.kt index 9348c18..ff5cf7e 100644 --- a/domain/src/main/java/com/ericampire/android/androidstudycase/domain/entity/Lottiefile.kt +++ b/domain/src/main/java/com/ericampire/android/androidstudycase/domain/entity/Lottiefile.kt @@ -1,7 +1,8 @@ package com.ericampire.android.androidstudycase.domain.entity +import androidx.room.ColumnInfo +import androidx.room.Embedded import androidx.room.Entity -import androidx.room.Ignore import androidx.room.PrimaryKey import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -40,11 +41,9 @@ data class Lottiefile( var gifUrl: String? = "", var videoUrl: String? = "", var imageUrl: String? = "", - var name: String = "", -// @TypeConverters(DateConverter::class) -// @Serializable(with = DateSerializer::class) + @ColumnInfo(name = "file_name") var name: String = "", var createdAt: String = "", - @Ignore var createdBy: Animator? = null + @Embedded var createdBy: Animator? = null ) diff --git a/domain/src/test/java/com/ericampire/android/androidstudycase/domain/usecase/SaveUserUseCaseTest.kt b/domain/src/test/java/com/ericampire/android/androidstudycase/domain/usecase/SaveUserUseCaseTest.kt new file mode 100644 index 0000000..03d2de3 --- /dev/null +++ b/domain/src/test/java/com/ericampire/android/androidstudycase/domain/usecase/SaveUserUseCaseTest.kt @@ -0,0 +1,3 @@ +package com.ericampire.android.androidstudycase.domain.usecase + +internal class SaveUserUseCaseTest \ No newline at end of file diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml index 8dd94ab..ee4289a 100644 --- a/i18n/src/main/res/values/strings.xml +++ b/i18n/src/main/res/values/strings.xml @@ -12,4 +12,6 @@ Browse all free Animation Access to 1000s of Lottie animations Go to Explore + Login + Hello Stranger \ No newline at end of file