diff --git a/.idea/gradle.xml b/.idea/gradle.xml index a9f4e52..a51626d 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -1,5 +1,6 @@ + diff --git a/app/build.gradle b/app/build.gradle index 7104247..fd3ddfc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,8 +1,12 @@ plugins { id 'com.android.application' id 'org.jetbrains.kotlin.android' + id 'kotlin-kapt' + id 'dagger.hilt.android.plugin' } +apply from: '../shared_dependencies.gradle' + android { compileSdk 32 @@ -36,11 +40,6 @@ android { dependencies { - implementation 'androidx.core:core-ktx:1.7.0' - implementation 'androidx.appcompat:appcompat:1.4.2' - implementation 'com.google.android.material:material:1.6.1' - implementation 'androidx.constraintlayout:constraintlayout:2.1.4' - testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.1.3' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + implementation project(':core') + } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d8b261f..2903a49 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,6 +5,7 @@ + + + \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/data/EShopRepository.kt b/core/src/main/java/com/mankart/eshop/core/data/EShopRepository.kt new file mode 100644 index 0000000..c60a47d --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/data/EShopRepository.kt @@ -0,0 +1,214 @@ +package com.mankart.eshop.core.data + +import androidx.paging.* +import com.mankart.eshop.core.data.paging.ProductRemoteMediator +import com.mankart.eshop.core.data.source.NetworkBoundResource +import com.mankart.eshop.core.data.source.local.LocalDataSource +import com.mankart.eshop.core.data.source.local.entity.ProductEntity +import com.mankart.eshop.core.data.source.remote.RemoteDataSource +import com.mankart.eshop.core.data.source.remote.network.ApiResponse +import com.mankart.eshop.core.data.source.remote.response.* +import com.mankart.eshop.core.domain.model.Cart +import com.mankart.eshop.core.domain.model.Product +import com.mankart.eshop.core.domain.model.Transaction +import com.mankart.eshop.core.domain.model.User +import com.mankart.eshop.core.domain.repository.* +import com.mankart.eshop.core.utils.AppExecutors +import com.mankart.eshop.core.utils.DataMapper +import kotlinx.coroutines.flow.* +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class EShopRepository @Inject constructor( + private val remoteDataSource: RemoteDataSource, + private val localDataSource: LocalDataSource, + private val appExecutors: AppExecutors, +): IAuthenticationRepository, + IProfileRepository, + IProductRepository, + ICartRepository, + ITransactionRepository, + IFavoriteProductRepository +{ + + // authentication + override fun postLogin(email: String, password: String): Flow> = + object: NetworkBoundResource() { + override suspend fun fetchFromApi(response: LoginResponse): User { + // store the token value to dataStore + localDataSource.saveUserToken(response.token) + return DataMapper.mapLoginResponseToDomain(response) + } + override suspend fun createCall(): Flow> = + remoteDataSource.postLogin(email, password) + }.asFlow() + + override fun postRegister(name: String, email: String, password: String): Flow> = + object: NetworkBoundResource() { + override suspend fun fetchFromApi(response: ResponseWithoutData): String = response.message + override suspend fun createCall(): Flow> = remoteDataSource.postRegister(name, email, password) + }.asFlow() + + // profile + override fun getProfile(): Flow> = + object: NetworkBoundResource() { + override suspend fun fetchFromApi(response: ProfileResponse): User { + // store email and name to dataStore + localDataSource.apply { + saveUserEmail(response.email) + saveUserName(response.name) + } + return DataMapper.mapProfileResponseToDomain(response) + } + + override suspend fun createCall(): Flow> { + val token = localDataSource.getUserToken().first() + return remoteDataSource.getProfile(token) + } + }.asFlow() + + override suspend fun logout() = localDataSource.clearCache() + + override suspend fun getName(): Flow = flow { + emit(localDataSource.getUserName().first()) + } + + override suspend fun getEmail(): Flow = flow { + emit(localDataSource.getUserEmail().first()) + } + + override suspend fun saveName(name: String) = localDataSource.saveUserName(name) + override suspend fun saveEmail(email: String) = localDataSource.saveUserEmail(email) + + + // products + @OptIn(ExperimentalPagingApi::class) + private fun getProductsPager(): Flow> { + return Pager( + config = PagingConfig( + pageSize = 10, + enablePlaceholders = false + ), + remoteMediator = ProductRemoteMediator( + remoteDataSource = remoteDataSource, + localDataSource = localDataSource + ), + pagingSourceFactory = { localDataSource.getProducts() } + ).flow + } + + override fun getProducts(): Flow> = + getProductsPager().map { pagingData -> + pagingData.map { product -> + DataMapper.mapProductEntityToDomain(product) + } + } + + override fun getProductById(id: String): Flow> = + object: NetworkBoundResource() { + override suspend fun fetchFromApi(response: ProductResponse): Product = + DataMapper.mapProductResponseToDomain(response) + + override suspend fun createCall(): Flow> = + remoteDataSource.getProductById(id) + }.asFlow() + + + // carts + override fun getCarts(): Flow> = + object: NetworkBoundResource() { + override suspend fun fetchFromApi(response: CartResponse): Cart = + DataMapper.mapCartResponseToDomain(response) + + override suspend fun createCall(): Flow> { + val token = localDataSource.getUserToken().first() + return remoteDataSource.getCarts(token) + } + }.asFlow() + + override fun addItemToCart(productId: String, quantity: Int): Flow> = + object: NetworkBoundResource() { + override suspend fun fetchFromApi(response: ResponseWithoutData): String = response.message + + override suspend fun createCall(): Flow> { + val token = localDataSource.getUserToken().first() + return remoteDataSource.postCart(token, productId, quantity) + } + }.asFlow() + + override fun updateItemInCart(itemId: String, quantity: Int): Flow> = + object: NetworkBoundResource() { + override suspend fun fetchFromApi(response: ResponseWithoutData): String = response.message + + override suspend fun createCall(): Flow> { + val token = localDataSource.getUserToken().first() + return remoteDataSource.putCart(token, itemId, quantity) + } + }.asFlow() + + override fun deleteItemFromCart(itemId: String): Flow> = + object: NetworkBoundResource() { + override suspend fun fetchFromApi(response: ResponseWithoutData): String = response.message + + override suspend fun createCall(): Flow> { + val token = localDataSource.getUserToken().first() + return remoteDataSource.deleteCart(token, itemId) + } + }.asFlow() + + + // transactions + override fun getTransactions(): Flow>> = + object: NetworkBoundResource, List>() { + override suspend fun fetchFromApi(response: List): List = + response.map { DataMapper.mapTransactionResponseToDomain(it) } + + override suspend fun createCall(): Flow>> { + val token = localDataSource.getUserToken().first() + return remoteDataSource.getTransactions(token) + } + }.asFlow() + + override fun getTransactionById(id: String): Flow> = + object: NetworkBoundResource() { + override suspend fun fetchFromApi(response: TransactionResponse): Transaction = + DataMapper.mapTransactionResponseToDomain(response) + + override suspend fun createCall(): Flow> { + val token = localDataSource.getUserToken().first() + return remoteDataSource.getTransactionById(token, id) + } + }.asFlow() + + override fun checkout(): Flow> = + object: NetworkBoundResource() { + override suspend fun fetchFromApi(response: ResponseWithoutData): String = response.message + override suspend fun createCall(): Flow> { + val token = localDataSource.getUserToken().first() + return remoteDataSource.postCheckout(token) + } + }.asFlow() + + override fun getFavoriteProducts(): Flow>> = flow { + emit(Resource.Loading()) + val loadFromDB = localDataSource.getFavouriteProducts().map { + it.map { product -> + DataMapper.mapFavouriteProductEntityToDomain(product) + } + } + emit(Resource.Success(loadFromDB.first())) + } + + override fun addFavoriteProduct(product: Product) { + val favouriteProductEntity = DataMapper.mapFavouriteProductDomainToEntity(product) + appExecutors.diskIO().execute { + localDataSource.insertFavouriteProduct(favouriteProductEntity) + } + } + + override fun deleteFavoriteProductById(productId: String) = + appExecutors.diskIO().execute { + localDataSource.deleteFavouriteProductById(productId) + } +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/data/Resource.kt b/core/src/main/java/com/mankart/eshop/core/data/Resource.kt new file mode 100644 index 0000000..6ec8441 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/data/Resource.kt @@ -0,0 +1,8 @@ +package com.mankart.eshop.core.data + +sealed class Resource(val data: T? = null, val message: String? = null) { + class Success(data: T) : Resource(data) + class Loading(data: T? = null) : Resource(data) + class Message(message: String) : Resource(message = message) + class Error(message: String, data: T? = null) : Resource(data, message) +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/data/paging/ProductRemoteMediator.kt b/core/src/main/java/com/mankart/eshop/core/data/paging/ProductRemoteMediator.kt new file mode 100644 index 0000000..a60fc85 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/data/paging/ProductRemoteMediator.kt @@ -0,0 +1,118 @@ +package com.mankart.eshop.core.data.paging + +import androidx.paging.ExperimentalPagingApi +import androidx.paging.LoadType +import androidx.paging.PagingState +import androidx.paging.RemoteMediator +import com.mankart.eshop.core.data.Resource +import com.mankart.eshop.core.data.source.NetworkBoundResource +import com.mankart.eshop.core.data.source.local.LocalDataSource +import com.mankart.eshop.core.data.source.local.entity.ProductEntity +import com.mankart.eshop.core.data.source.local.entity.RemoteKeys +import com.mankart.eshop.core.data.source.remote.RemoteDataSource +import com.mankart.eshop.core.data.source.remote.network.ApiResponse +import com.mankart.eshop.core.data.source.remote.response.ProductResponse +import com.mankart.eshop.core.utils.DataMapper +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.first +import javax.inject.Inject + +@OptIn(ExperimentalPagingApi::class) +class ProductRemoteMediator @Inject constructor( + private val localDataSource: LocalDataSource, + private val remoteDataSource: RemoteDataSource +) : RemoteMediator() { + + override suspend fun load( + loadType: LoadType, + state: PagingState + ): MediatorResult { + val page = when (loadType) { + LoadType.REFRESH -> { + val remoteKeys = getRemoteKeyClosestToCurrentPosition(state) + remoteKeys?.nextKey?.minus(1) ?: INITIAL_PAGE_INDEX + } + LoadType.PREPEND -> { + val remoteKeys = getRemoteKeyForFirstItem(state) + val prevKey = remoteKeys?.prevKey ?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null) + prevKey + } + LoadType.APPEND -> { + val remoteKeys = getRemoteKeyForLastItem(state) + val nextKey = remoteKeys?.nextKey ?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null) + nextKey + } + } + + return try { + val response = getResponse(page = page, size = state.config.pageSize).first() + val endOfPaginationReached = response.data?.isEmpty() + + if (loadType == LoadType.REFRESH) { + localDataSource.deleteRemoteKeys() + localDataSource.deleteProducts() + } + + val nextKey = if (endOfPaginationReached == true) null else page + 1 + val prevKey = if (page == INITIAL_PAGE_INDEX) null else page - 1 + val keys = response.data?.map { + RemoteKeys( + id = it.id, + prevKey = prevKey, + nextKey = nextKey + ) + } + if (keys != null) { + localDataSource.insertRemoteKey(keys) + } + + localDataSource.insertProducts(response.data?:emptyList()) + + MediatorResult.Success(endOfPaginationReached = false) + } catch (err: Exception) { + MediatorResult.Error(err) + } + } + + private suspend fun getResponse(page: Int, size: Int): Flow>> = + object: NetworkBoundResource, List>() { + override suspend fun fetchFromApi(response: List): List = + response.map { + DataMapper.mapProductsResponseToEntity(it) + } + + override suspend fun createCall(): Flow>> = + remoteDataSource.getProducts(page, size) + }.asFlow() + + override suspend fun initialize(): InitializeAction { + return InitializeAction.LAUNCH_INITIAL_REFRESH + } + + private suspend fun getRemoteKeyForLastItem(state: PagingState) : RemoteKeys? { + return state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull()?.let { data -> + localDataSource.getRemoteKey(data.id) + } + } + + private suspend fun getRemoteKeyForFirstItem(state: PagingState) : RemoteKeys? { + return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()?.let { data -> + localDataSource.getRemoteKey(data.id) + } + } + + private suspend fun getRemoteKeyClosestToCurrentPosition( + state: PagingState + ): RemoteKeys? { + return state.anchorPosition?.let { position -> + state.closestItemToPosition(position)?.id?.let { id -> + localDataSource.getRemoteKey(id) + } + } + } + + private companion object { + const val INITIAL_PAGE_INDEX = 1 + } + +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/data/source/NetworkBoundResource.kt b/core/src/main/java/com/mankart/eshop/core/data/source/NetworkBoundResource.kt new file mode 100644 index 0000000..b8f9101 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/data/source/NetworkBoundResource.kt @@ -0,0 +1,34 @@ +package com.mankart.eshop.core.data.source + +import com.mankart.eshop.core.data.Resource +import com.mankart.eshop.core.data.source.remote.network.ApiResponse +import kotlinx.coroutines.flow.* + +abstract class NetworkBoundResource { + private var result: Flow> = flow { + emit(Resource.Loading()) + when (val apiResponse = createCall().first()) { + is ApiResponse.Success -> { + emit(Resource.Success(fetchFromApi(apiResponse.data))) + } + is ApiResponse.Message -> { + emit(Resource.Message(apiResponse.message)) + } + is ApiResponse.Error -> { + onFetchFailed() + emit(Resource.Error(apiResponse.errorMessage)) + } + is ApiResponse.Empty -> { + emit(Resource.Message("Empty")) + } + } + } + + protected open fun onFetchFailed() {} + + protected abstract suspend fun fetchFromApi(response: RequestType): ResultType + + protected abstract suspend fun createCall(): Flow> + + fun asFlow(): Flow> = result +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/data/source/local/LocalDataSource.kt b/core/src/main/java/com/mankart/eshop/core/data/source/local/LocalDataSource.kt new file mode 100644 index 0000000..ac9cf2e --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/data/source/local/LocalDataSource.kt @@ -0,0 +1,51 @@ +package com.mankart.eshop.core.data.source.local + +import androidx.paging.PagingSource +import com.mankart.eshop.core.data.source.local.datastore.PreferenceDataStore +import com.mankart.eshop.core.data.source.local.entity.FavoriteProductEntity +import com.mankart.eshop.core.data.source.local.entity.ProductEntity +import com.mankart.eshop.core.data.source.local.entity.RemoteKeys +import com.mankart.eshop.core.data.source.local.room.dao.FavoriteProductDao +import com.mankart.eshop.core.data.source.local.room.dao.ProductDao +import com.mankart.eshop.core.data.source.local.room.dao.RemoteKeysDao +import kotlinx.coroutines.flow.Flow +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class LocalDataSource @Inject constructor( + private val dataStore: PreferenceDataStore, + private val productDao: ProductDao, + private val remoteKeysDao: RemoteKeysDao, + private val favouriteProductDao: FavoriteProductDao +) { + // interact with data store + fun getUserToken(): Flow = dataStore.getUserToken() + suspend fun saveUserToken(token: String) = dataStore.saveUserToken(token) + + fun getUserName(): Flow = dataStore.getUserName() + suspend fun saveUserName(name: String) = dataStore.saveUserName(name) + + fun getUserEmail(): Flow = dataStore.getUserEmail() + suspend fun saveUserEmail(email: String) = dataStore.saveUserEmail(email) + + suspend fun clearCache() = dataStore.clearCache() + + // interact with product database table product + fun getProducts(): PagingSource = productDao.getAllProducts() + suspend fun insertProducts(products: List) = productDao.insertAllProducts(products) + suspend fun deleteProducts() = productDao.deleteAllProducts() + + // interact with remote product database table favProduct + fun getFavouriteProducts(): Flow> = + favouriteProductDao.getFavoriteProducts() + fun insertFavouriteProduct(products: FavoriteProductEntity) = + favouriteProductDao.insertFavoriteProduct(products) + fun deleteFavouriteProductById(productId: String) = + favouriteProductDao.deleteFavoriteProductById(productId) + + // interact with remote keys (paging) + suspend fun getRemoteKey(id: String): RemoteKeys? = remoteKeysDao.getRemoteKey(id) + suspend fun insertRemoteKey(remoteKey: List) = remoteKeysDao.insertAll(remoteKey) + suspend fun deleteRemoteKeys() = remoteKeysDao.deleteAllRemoteKeys() +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/data/source/local/datastore/PreferenceDataStore.kt b/core/src/main/java/com/mankart/eshop/core/data/source/local/datastore/PreferenceDataStore.kt new file mode 100644 index 0000000..e0b693e --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/data/source/local/datastore/PreferenceDataStore.kt @@ -0,0 +1,50 @@ +package com.mankart.eshop.core.data.source.local.datastore + +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.stringPreferencesKey +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class PreferenceDataStore @Inject constructor(private val dataStore: DataStore) { + + fun getUserToken(): Flow = dataStore.data.map { it[USER_ACCESS_TOKEN] ?: "not_set_yet" } + + suspend fun saveUserToken(token: String) { + dataStore.edit { preferences -> + preferences[USER_ACCESS_TOKEN] = token + } + } + + fun getUserEmail(): Flow = dataStore.data.map { it[USER_EMAIL] ?: "not_set_yet" } + + suspend fun saveUserEmail(email: String) { + dataStore.edit { preferences -> + preferences[USER_EMAIL] = email + } + } + + fun getUserName(): Flow = dataStore.data.map { it[USER_NAME] ?: "not_set_yet" } + + suspend fun saveUserName(name: String) { + dataStore.edit { preferences -> + preferences[USER_NAME] = name + } + } + + suspend fun clearCache() { + dataStore.edit { + it.clear() + } + } + + companion object { + private val USER_ACCESS_TOKEN = stringPreferencesKey("user_access_token") + private val USER_EMAIL = stringPreferencesKey("user_email") + private val USER_NAME = stringPreferencesKey("user_name") + } +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/data/source/local/entity/FavoriteProductEntity.kt b/core/src/main/java/com/mankart/eshop/core/data/source/local/entity/FavoriteProductEntity.kt new file mode 100644 index 0000000..c48e300 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/data/source/local/entity/FavoriteProductEntity.kt @@ -0,0 +1,10 @@ +package com.mankart.eshop.core.data.source.local.entity + +import androidx.room.Embedded +import androidx.room.Entity + +@Entity(tableName = "favProduct", primaryKeys = ["id"]) +data class FavoriteProductEntity( + @Embedded + val product: ProductEntity, +) diff --git a/core/src/main/java/com/mankart/eshop/core/data/source/local/entity/ProductEntity.kt b/core/src/main/java/com/mankart/eshop/core/data/source/local/entity/ProductEntity.kt new file mode 100644 index 0000000..324f69f --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/data/source/local/entity/ProductEntity.kt @@ -0,0 +1,38 @@ +package com.mankart.eshop.core.data.source.local.entity + +import androidx.annotation.NonNull +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "products") +data class ProductEntity( + @PrimaryKey + @NonNull + @ColumnInfo(name = "id") + val id: String, + + @NonNull + @ColumnInfo(name = "title") + val title: String, + + @NonNull + @ColumnInfo(name = "price") + val price: Int, + + @NonNull + @ColumnInfo(name = "category") + val category: String, + + @ColumnInfo(name = "description") + val description: String, + + @ColumnInfo(name = "image") + val image: String, + + @ColumnInfo(name = "rating") + val rating: Double, + + @ColumnInfo(name = "countRate") + val countRate: Int +) diff --git a/core/src/main/java/com/mankart/eshop/core/data/source/local/entity/RemoteKeys.kt b/core/src/main/java/com/mankart/eshop/core/data/source/local/entity/RemoteKeys.kt new file mode 100644 index 0000000..3458431 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/data/source/local/entity/RemoteKeys.kt @@ -0,0 +1,11 @@ +package com.mankart.eshop.core.data.source.local.entity + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "remoteKeys") +data class RemoteKeys( + @PrimaryKey val id: String, + val nextKey: Int?, + val prevKey: Int? +) diff --git a/core/src/main/java/com/mankart/eshop/core/data/source/local/room/ProductDatabase.kt b/core/src/main/java/com/mankart/eshop/core/data/source/local/room/ProductDatabase.kt new file mode 100644 index 0000000..db1fb28 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/data/source/local/room/ProductDatabase.kt @@ -0,0 +1,20 @@ +package com.mankart.eshop.core.data.source.local.room + +import androidx.room.Database +import androidx.room.RoomDatabase +import com.mankart.eshop.core.data.source.local.entity.FavoriteProductEntity +import com.mankart.eshop.core.data.source.local.entity.ProductEntity +import com.mankart.eshop.core.data.source.local.entity.RemoteKeys +import com.mankart.eshop.core.data.source.local.room.dao.FavoriteProductDao +import com.mankart.eshop.core.data.source.local.room.dao.ProductDao +import com.mankart.eshop.core.data.source.local.room.dao.RemoteKeysDao + +@Database(entities = [ + ProductEntity::class, + RemoteKeys::class, + FavoriteProductEntity::class], version = 1, exportSchema = false) +abstract class ProductDatabase: RoomDatabase() { + abstract fun productDao(): ProductDao + abstract fun remoteKeysDao(): RemoteKeysDao + abstract fun favouriteProductDao(): FavoriteProductDao +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/data/source/local/room/dao/FavoriteProductDao.kt b/core/src/main/java/com/mankart/eshop/core/data/source/local/room/dao/FavoriteProductDao.kt new file mode 100644 index 0000000..aad976d --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/data/source/local/room/dao/FavoriteProductDao.kt @@ -0,0 +1,17 @@ +package com.mankart.eshop.core.data.source.local.room.dao + +import androidx.room.* +import com.mankart.eshop.core.data.source.local.entity.FavoriteProductEntity +import kotlinx.coroutines.flow.Flow + +@Dao +interface FavoriteProductDao { + @Query("SELECT * FROM favProduct") + fun getFavoriteProducts(): Flow> + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insertFavoriteProduct(product: FavoriteProductEntity) + + @Query("DELETE FROM favProduct WHERE id = :id") + fun deleteFavoriteProductById(id: String) +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/data/source/local/room/dao/ProductDao.kt b/core/src/main/java/com/mankart/eshop/core/data/source/local/room/dao/ProductDao.kt new file mode 100644 index 0000000..8ca0326 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/data/source/local/room/dao/ProductDao.kt @@ -0,0 +1,20 @@ +package com.mankart.eshop.core.data.source.local.room.dao + +import androidx.paging.PagingSource +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import com.mankart.eshop.core.data.source.local.entity.ProductEntity + +@Dao +interface ProductDao { + @Query("SELECT * FROM products") + fun getAllProducts(): PagingSource + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertAllProducts(products: List) + + @Query("DELETE FROM products") + suspend fun deleteAllProducts() +} diff --git a/core/src/main/java/com/mankart/eshop/core/data/source/local/room/dao/RemoteKeysDao.kt b/core/src/main/java/com/mankart/eshop/core/data/source/local/room/dao/RemoteKeysDao.kt new file mode 100644 index 0000000..dae2202 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/data/source/local/room/dao/RemoteKeysDao.kt @@ -0,0 +1,19 @@ +package com.mankart.eshop.core.data.source.local.room.dao + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import com.mankart.eshop.core.data.source.local.entity.RemoteKeys + +@Dao +interface RemoteKeysDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertAll(remoteKey: List) + + @Query("SELECT * FROM remoteKeys WHERE id = :id") + suspend fun getRemoteKey(id: String): RemoteKeys? + + @Query("DELETE FROM remoteKeys") + suspend fun deleteAllRemoteKeys() +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/data/source/remote/RemoteDataSource.kt b/core/src/main/java/com/mankart/eshop/core/data/source/remote/RemoteDataSource.kt new file mode 100644 index 0000000..01abf67 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/data/source/remote/RemoteDataSource.kt @@ -0,0 +1,162 @@ +package com.mankart.eshop.core.data.source.remote + +import com.mankart.eshop.core.data.source.remote.network.ApiResponse +import com.mankart.eshop.core.data.source.remote.network.ApiService +import com.mankart.eshop.core.data.source.remote.response.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOn +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class RemoteDataSource @Inject constructor(private val apiService: ApiService) { + + // authentication + suspend fun postLogin(email: String, password: String): Flow> { + return flow { + try { + val response = apiService.postLogin(email, password) + emit(ApiResponse.Success(response.data)) + emit(ApiResponse.Message(response.message)) + } catch (err: Exception) { + emit(ApiResponse.Error(err.toString())) + } + }.flowOn(Dispatchers.IO) + } + suspend fun postRegister(name: String, email: String, password: String): Flow> { + return flow { + try { + val response = apiService.postRegister(name, email, password) + emit(ApiResponse.Message(response.message)) + } catch (err: Exception) { + emit(ApiResponse.Error(err.toString())) + } + }.flowOn(Dispatchers.IO) + } + + + // profile + suspend fun getProfile(token: String): Flow> { + return flow { + try { + val response = apiService.getProfile(token) + emit(ApiResponse.Success(response.data)) + emit(ApiResponse.Message(response.message)) + } catch (err: Exception) { + emit(ApiResponse.Error(err.toString())) + } + }.flowOn(Dispatchers.IO) + } + + + // products + suspend fun getProducts(page: Int, size: Int): Flow>> { + return flow { + try { + val response = apiService.getProducts(page, size) + emit(ApiResponse.Success(response.data)) + emit(ApiResponse.Message(response.message)) + } catch (err: Exception) { + emit(ApiResponse.Error(err.toString())) + } + }.flowOn(Dispatchers.IO) + } + + suspend fun getProductById(id: String): Flow> { + return flow { + try { + val response = apiService.getProductById(id) + emit(ApiResponse.Success(response.data)) + emit(ApiResponse.Message(response.message)) + } catch (err: Exception) { + emit(ApiResponse.Error(err.toString())) + } + }.flowOn(Dispatchers.IO) + } + + // carts + suspend fun getCarts(token: String): Flow> { + return flow { + try { + val response = apiService.getCarts(token) + emit(ApiResponse.Success(response.data)) + emit(ApiResponse.Message(response.message)) + } catch (err: Exception) { + emit(ApiResponse.Error(err.toString())) + } + }.flowOn(Dispatchers.IO) + } + + suspend fun postCart(token: String, productId: String, quantity: Int): Flow> { + return flow { + try { + val response = apiService.postCart(token, productId, quantity) + emit(ApiResponse.Message(response.message)) + } catch (err: Exception) { + emit(ApiResponse.Error(err.toString())) + } + }.flowOn(Dispatchers.IO) + } + + suspend fun putCart(token: String, itemId: String, quantity: Int): Flow> { + return flow { + try { + val response = apiService.putCart(token, itemId, quantity) + emit(ApiResponse.Message(response.message)) + } catch (err: Exception) { + emit(ApiResponse.Error(err.toString())) + } + }.flowOn(Dispatchers.IO) + } + + suspend fun deleteCart(token: String, itemId: String): Flow> { + return flow { + try { + val response = apiService.deleteCart(token, itemId) + emit(ApiResponse.Message(response.message)) + } catch (err: Exception) { + emit(ApiResponse.Error(err.toString())) + } + }.flowOn(Dispatchers.IO) + } + + + // transactions + suspend fun getTransactions(token: String): Flow>> { + return flow { + try { + val response = apiService.getTransactions(token) + emit(ApiResponse.Success(response.data)) + emit(ApiResponse.Message(response.message)) + } catch (err: Exception) { + emit(ApiResponse.Error(err.toString())) + } + }.flowOn(Dispatchers.IO) + } + + suspend fun getTransactionById(token: String, id: String): Flow> { + return flow { + try { + val response = apiService.getTransactionById(token, id) + emit(ApiResponse.Success(response.data)) + emit(ApiResponse.Message(response.message)) + } catch (err: Exception) { + emit(ApiResponse.Error(err.toString())) + } + }.flowOn(Dispatchers.IO) + } + + suspend fun postCheckout(token: String): Flow> { + return flow { + try { + val response = apiService.postCheckout(token) + emit(ApiResponse.Message(response.message)) + } catch (err: Exception) { + emit(ApiResponse.Error(err.toString())) + } + }.flowOn(Dispatchers.IO) + } + +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/data/source/remote/network/ApiResponse.kt b/core/src/main/java/com/mankart/eshop/core/data/source/remote/network/ApiResponse.kt new file mode 100644 index 0000000..8a5c82f --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/data/source/remote/network/ApiResponse.kt @@ -0,0 +1,8 @@ +package com.mankart.eshop.core.data.source.remote.network + +sealed class ApiResponse { + data class Success(val data: T) : ApiResponse() + data class Error(val errorMessage: String) : ApiResponse() + data class Message(val message: String) : ApiResponse() + object Empty : ApiResponse() +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/data/source/remote/network/ApiService.kt b/core/src/main/java/com/mankart/eshop/core/data/source/remote/network/ApiService.kt new file mode 100644 index 0000000..d3a3e85 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/data/source/remote/network/ApiService.kt @@ -0,0 +1,77 @@ +package com.mankart.eshop.core.data.source.remote.network + +import com.mankart.eshop.core.data.source.remote.response.* +import retrofit2.http.* + +interface ApiService { + @POST("login") + fun postLogin( + @Field("email") email: String, + @Field("password") password: String + ) : ResponseWithData + + @POST("register") + fun postRegister( + @Field("name") name: String, + @Field("email") email: String, + @Field("password") password: String + ) : ResponseWithoutData + + @GET("user") + fun getProfile( + @Header("Authorization") token: String + ) : ResponseWithData + + @GET("products") + fun getProducts( + @Query("page") page: Int? = null, + @Query("size") size: Int? = null + ) : ResponseWithData> + + @GET("products/{id}") + fun getProductById( + @Path("id") id: String + ) : ResponseWithData + + @GET("carts") + fun getCarts( + @Header("Authorization") token: String + ) : ResponseWithData + + @POST("carts") + fun postCart( + @Header("Authorization") token: String, + @Body productId: String, + @Body quantity: Int + ) : ResponseWithoutData + + @PUT("carts/{id}") + fun putCart( + @Header("Authorization") token: String, + @Path("id") itemId: String, + @Query("qty") quantity: Int + ) : ResponseWithoutData + + @DELETE("carts/{id}") + fun deleteCart( + @Header("Authorization") token: String, + @Path("id") itemId: String + ) : ResponseWithoutData + + @GET("transactions") + fun getTransactions( + @Header("Authorization") token: String + ) : ResponseWithData> + + @GET("transactions/{id}") + fun getTransactionById( + @Header("Authorization") token: String, + @Path("id") id: String + ) : ResponseWithData + + @POST("checkout") + fun postCheckout( + @Header("Authorization") token: String + ) : ResponseWithoutData + +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/data/source/remote/response/CartItemResponse.kt b/core/src/main/java/com/mankart/eshop/core/data/source/remote/response/CartItemResponse.kt new file mode 100644 index 0000000..4623822 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/data/source/remote/response/CartItemResponse.kt @@ -0,0 +1,26 @@ +package com.mankart.eshop.core.data.source.remote.response + +import com.google.gson.annotations.SerializedName + +data class CartItemResponse( + @field:SerializedName("id") + val id: String, + + @field:SerializedName("productId") + val productId: String, + + @field:SerializedName("title") + val title: String, + + @field:SerializedName("price") + val price: Int, + + @field:SerializedName("description") + val description: String, + + @field:SerializedName("image") + val image: String, + + @field:SerializedName("quantity") + val quantity: Int, +) \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/data/source/remote/response/CartResponse.kt b/core/src/main/java/com/mankart/eshop/core/data/source/remote/response/CartResponse.kt new file mode 100644 index 0000000..bcab20f --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/data/source/remote/response/CartResponse.kt @@ -0,0 +1,15 @@ +package com.mankart.eshop.core.data.source.remote.response + +import com.google.gson.annotations.SerializedName + +data class CartResponse( + @field:SerializedName("totalItem") + val totalItem: Int, + + @field:SerializedName("subTotal") + val subTotal: Int, + + @field:SerializedName("cart") + val cart: List +) + diff --git a/core/src/main/java/com/mankart/eshop/core/data/source/remote/response/LoginResponse.kt b/core/src/main/java/com/mankart/eshop/core/data/source/remote/response/LoginResponse.kt new file mode 100644 index 0000000..2305891 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/data/source/remote/response/LoginResponse.kt @@ -0,0 +1,11 @@ +package com.mankart.eshop.core.data.source.remote.response + +import com.google.gson.annotations.SerializedName + +data class LoginResponse( + @field:SerializedName("id") + val id: String, + + @field:SerializedName("token") + val token: String +) diff --git a/core/src/main/java/com/mankart/eshop/core/data/source/remote/response/OrderResponse.kt b/core/src/main/java/com/mankart/eshop/core/data/source/remote/response/OrderResponse.kt new file mode 100644 index 0000000..f0244a7 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/data/source/remote/response/OrderResponse.kt @@ -0,0 +1,26 @@ +package com.mankart.eshop.core.data.source.remote.response + +import com.google.gson.annotations.SerializedName + +data class OrderResponse( + @field:SerializedName("id") + val id: String, + + @field:SerializedName("title") + val title: String, + + @field:SerializedName("price") + val price: Int, + + @field:SerializedName("quantity") + val quantity: Int, + + @field:SerializedName("image") + val image: String, + + @field:SerializedName("yourRating") + val yourRating: Int, + + @field:SerializedName("yourReview") + val yourReview: String? = "", +) diff --git a/core/src/main/java/com/mankart/eshop/core/data/source/remote/response/ProductResponse.kt b/core/src/main/java/com/mankart/eshop/core/data/source/remote/response/ProductResponse.kt new file mode 100644 index 0000000..71e1845 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/data/source/remote/response/ProductResponse.kt @@ -0,0 +1,37 @@ +package com.mankart.eshop.core.data.source.remote.response + +import com.google.gson.annotations.SerializedName + +data class ProductResponse( + @field:SerializedName("id") + val id: String, + + @field:SerializedName("title") + val title: String, + + @field:SerializedName("price") + val price: Int, + + @field:SerializedName("category") + val category: String, + + @field:SerializedName("description") + val description: String, + + @field:SerializedName("image") + val image: String, + + @field:SerializedName("rating") + val rating: Rating? = null, + + @field:SerializedName("reviews") + val reviews: List? = emptyList() +) + +data class Rating( + @field:SerializedName("rating") + val rating: Double, + + @field:SerializedName("count") + val countRate: Int +) diff --git a/core/src/main/java/com/mankart/eshop/core/data/source/remote/response/ProfileResponse.kt b/core/src/main/java/com/mankart/eshop/core/data/source/remote/response/ProfileResponse.kt new file mode 100644 index 0000000..2d6983f --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/data/source/remote/response/ProfileResponse.kt @@ -0,0 +1,11 @@ +package com.mankart.eshop.core.data.source.remote.response + +import com.google.gson.annotations.SerializedName + +data class ProfileResponse( + @field:SerializedName("email") + val email: String, + + @field:SerializedName("name") + val name: String, +) \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/data/source/remote/response/ResponseWithData.kt b/core/src/main/java/com/mankart/eshop/core/data/source/remote/response/ResponseWithData.kt new file mode 100644 index 0000000..2dde807 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/data/source/remote/response/ResponseWithData.kt @@ -0,0 +1,14 @@ +package com.mankart.eshop.core.data.source.remote.response + +import com.google.gson.annotations.SerializedName + +data class ResponseWithData( + @field:SerializedName("status") + val status: String, + + @field:SerializedName("message") + val message: String, + + @field:SerializedName("data") + val data: T +) diff --git a/core/src/main/java/com/mankart/eshop/core/data/source/remote/response/ResponseWithoutData.kt b/core/src/main/java/com/mankart/eshop/core/data/source/remote/response/ResponseWithoutData.kt new file mode 100644 index 0000000..d4494cd --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/data/source/remote/response/ResponseWithoutData.kt @@ -0,0 +1,11 @@ +package com.mankart.eshop.core.data.source.remote.response + +import com.google.gson.annotations.SerializedName + +data class ResponseWithoutData( + @field:SerializedName("status") + val status: String, + + @field:SerializedName("message") + val message: String +) diff --git a/core/src/main/java/com/mankart/eshop/core/data/source/remote/response/ReviewResponse.kt b/core/src/main/java/com/mankart/eshop/core/data/source/remote/response/ReviewResponse.kt new file mode 100644 index 0000000..d4ebe0c --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/data/source/remote/response/ReviewResponse.kt @@ -0,0 +1,14 @@ +package com.mankart.eshop.core.data.source.remote.response + +import com.google.gson.annotations.SerializedName + +data class ReviewResponse( + @field:SerializedName("user") + val user: String, + + @field:SerializedName("rate") + val rate: Int, + + @field:SerializedName("review") + val review: String? = null +) \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/data/source/remote/response/TransactionResponse.kt b/core/src/main/java/com/mankart/eshop/core/data/source/remote/response/TransactionResponse.kt new file mode 100644 index 0000000..6857620 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/data/source/remote/response/TransactionResponse.kt @@ -0,0 +1,20 @@ +package com.mankart.eshop.core.data.source.remote.response + +import com.google.gson.annotations.SerializedName + +data class TransactionResponse( + @field:SerializedName("id") + val id: String, + + @field:SerializedName("dateCreated") + val dateCreated: Long, + + @field:SerializedName("totalItem") + val totalItem: Int? = 0, + + @field:SerializedName("total") + val totalPrice: Int? = 0, + + @field:SerializedName("orders") + val orders: List? = emptyList() +) diff --git a/core/src/main/java/com/mankart/eshop/core/di/DataStoreModule.kt b/core/src/main/java/com/mankart/eshop/core/di/DataStoreModule.kt new file mode 100644 index 0000000..8f22e35 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/di/DataStoreModule.kt @@ -0,0 +1,29 @@ +package com.mankart.eshop.core.di + +import android.content.Context +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.preferencesDataStore +import com.mankart.eshop.core.data.source.local.datastore.PreferenceDataStore +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +val Context.dataStore: DataStore by preferencesDataStore(name = "application") +@Module +@InstallIn(SingletonComponent::class) +class DataStoreModule { + + @Provides + fun provideDataStore(@ApplicationContext context: Context) : DataStore = + context.dataStore + + @Provides + @Singleton + fun provideAuthPreference(dataStore: DataStore) : PreferenceDataStore = + PreferenceDataStore(dataStore) + +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/di/DatabaseModule.kt b/core/src/main/java/com/mankart/eshop/core/di/DatabaseModule.kt new file mode 100644 index 0000000..cbcfdfc --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/di/DatabaseModule.kt @@ -0,0 +1,37 @@ +package com.mankart.eshop.core.di + +import android.content.Context +import androidx.room.Room +import com.mankart.eshop.core.data.source.local.room.ProductDatabase +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +class DatabaseModule { + + // Product Database + @Singleton + @Provides + fun provideProductDatabase(@ApplicationContext context: Context) : ProductDatabase = + Room.databaseBuilder( + context, + ProductDatabase::class.java, + "products.db" + ).fallbackToDestructiveMigration().build() + + // product dao + @Provides + fun provideProductDao(productDatabase: ProductDatabase) = productDatabase.productDao() + // remote keys dao (paging) + @Provides + fun provideRemoteKeysDao(productDatabase: ProductDatabase) = productDatabase.remoteKeysDao() + // favourite product dao + @Provides + fun provideFavouriteProductDao(productDatabase: ProductDatabase) = productDatabase.favouriteProductDao() + +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/di/NetworkModule.kt b/core/src/main/java/com/mankart/eshop/core/di/NetworkModule.kt new file mode 100644 index 0000000..8932996 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/di/NetworkModule.kt @@ -0,0 +1,35 @@ +package com.mankart.eshop.core.di + +import com.mankart.eshop.core.data.source.remote.network.ApiService +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import java.util.concurrent.TimeUnit + +@Module +@InstallIn(SingletonComponent::class) +class NetworkModule { + @Provides + fun provideOkHttpClient() : OkHttpClient { + return OkHttpClient.Builder() + .addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)) + .connectTimeout(120, TimeUnit.SECONDS) + .readTimeout(120, TimeUnit.SECONDS) + .build() + } + + @Provides + fun provideApiService(client: OkHttpClient) : ApiService { + val retrofit = Retrofit.Builder() + .client(client) + .baseUrl("https://eshop.reskimulud.my.id/") + .addConverterFactory(GsonConverterFactory.create()) + .build() + return retrofit.create(ApiService::class.java) + } +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/di/RepositoryModule.kt b/core/src/main/java/com/mankart/eshop/core/di/RepositoryModule.kt new file mode 100644 index 0000000..8e2b615 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/di/RepositoryModule.kt @@ -0,0 +1,30 @@ +package com.mankart.eshop.core.di + +import com.mankart.eshop.core.data.EShopRepository +import com.mankart.eshop.core.domain.repository.* +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent + +@Module +@InstallIn(SingletonComponent::class) +abstract class RepositoryModule { + @Binds + abstract fun bindAuthenticationRepository(repository: EShopRepository) : IAuthenticationRepository + + @Binds + abstract fun bindProfileRepository(repository: EShopRepository) : IProfileRepository + + @Binds + abstract fun bindProductRepository(repository: EShopRepository) : IProductRepository + + @Binds + abstract fun bindCartRepository(repository: EShopRepository) : ICartRepository + + @Binds + abstract fun bindTransactionRepository(repository: EShopRepository) : ITransactionRepository + + @Binds + abstract fun bindFavouriteProductRepository(repository: EShopRepository) : IFavoriteProductRepository +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/domain/model/Cart.kt b/core/src/main/java/com/mankart/eshop/core/domain/model/Cart.kt new file mode 100644 index 0000000..0f3b1cf --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/domain/model/Cart.kt @@ -0,0 +1,7 @@ +package com.mankart.eshop.core.domain.model + +data class Cart( + val totalItem: Int, + val subTotal: Int, + val cart: List +) diff --git a/core/src/main/java/com/mankart/eshop/core/domain/model/CartItem.kt b/core/src/main/java/com/mankart/eshop/core/domain/model/CartItem.kt new file mode 100644 index 0000000..e9cddd3 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/domain/model/CartItem.kt @@ -0,0 +1,11 @@ +package com.mankart.eshop.core.domain.model + +data class CartItem( + val id: String, + val productId: String, + val title: String, + val price: Int, + val description: String, + val image: String, + val quantity: Int, +) diff --git a/core/src/main/java/com/mankart/eshop/core/domain/model/Order.kt b/core/src/main/java/com/mankart/eshop/core/domain/model/Order.kt new file mode 100644 index 0000000..6fc9005 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/domain/model/Order.kt @@ -0,0 +1,11 @@ +package com.mankart.eshop.core.domain.model + +data class Order( + val id: String, + val title: String, + val price: Int, + val quantity: Int, + val image: String, + val yourRating: Int, + val yourReview: String? = "", +) diff --git a/core/src/main/java/com/mankart/eshop/core/domain/model/Product.kt b/core/src/main/java/com/mankart/eshop/core/domain/model/Product.kt new file mode 100644 index 0000000..403a4a8 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/domain/model/Product.kt @@ -0,0 +1,14 @@ +package com.mankart.eshop.core.domain.model + + +data class Product( + val id: String, + val title: String, + val price: Int, + val category: String, + val description: String, + val image: String, + val rating: Double, + val countRate: Int, + val reviews: List? = emptyList() +) diff --git a/core/src/main/java/com/mankart/eshop/core/domain/model/Transaction.kt b/core/src/main/java/com/mankart/eshop/core/domain/model/Transaction.kt new file mode 100644 index 0000000..c6cc091 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/domain/model/Transaction.kt @@ -0,0 +1,10 @@ +package com.mankart.eshop.core.domain.model + + +data class Transaction( + val id: String, + val dateCreated: Long, + val totalItem: Int? = 0, + val totalPrice: Int? = 0, + val orders: List? = emptyList() +) diff --git a/core/src/main/java/com/mankart/eshop/core/domain/model/User.kt b/core/src/main/java/com/mankart/eshop/core/domain/model/User.kt new file mode 100644 index 0000000..2710c07 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/domain/model/User.kt @@ -0,0 +1,9 @@ +package com.mankart.eshop.core.domain.model + +data class User( + val id: String? = null, + val name: String? = null, + val email: String? = null, + val role: String? = null, + val token: String? = null +) diff --git a/core/src/main/java/com/mankart/eshop/core/domain/model/UserReview.kt b/core/src/main/java/com/mankart/eshop/core/domain/model/UserReview.kt new file mode 100644 index 0000000..b28106a --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/domain/model/UserReview.kt @@ -0,0 +1,7 @@ +package com.mankart.eshop.core.domain.model + +data class UserReview( + val user: String, + val rate: Int, + val review: String? = null +) diff --git a/core/src/main/java/com/mankart/eshop/core/domain/repository/IAuthenticationRepository.kt b/core/src/main/java/com/mankart/eshop/core/domain/repository/IAuthenticationRepository.kt new file mode 100644 index 0000000..bf36aeb --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/domain/repository/IAuthenticationRepository.kt @@ -0,0 +1,10 @@ +package com.mankart.eshop.core.domain.repository + +import com.mankart.eshop.core.data.Resource +import com.mankart.eshop.core.domain.model.User +import kotlinx.coroutines.flow.Flow + +interface IAuthenticationRepository { + fun postLogin(email: String, password: String): Flow> + fun postRegister(name: String, email: String, password: String): Flow> +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/domain/repository/ICartRepository.kt b/core/src/main/java/com/mankart/eshop/core/domain/repository/ICartRepository.kt new file mode 100644 index 0000000..b081074 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/domain/repository/ICartRepository.kt @@ -0,0 +1,12 @@ +package com.mankart.eshop.core.domain.repository + +import com.mankart.eshop.core.data.Resource +import com.mankart.eshop.core.domain.model.Cart +import kotlinx.coroutines.flow.Flow + +interface ICartRepository { + fun getCarts(): Flow> + fun addItemToCart(productId: String, quantity: Int): Flow> + fun updateItemInCart(itemId: String, quantity: Int): Flow> + fun deleteItemFromCart(itemId: String): Flow> +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/domain/repository/IFavoriteProductRepository.kt b/core/src/main/java/com/mankart/eshop/core/domain/repository/IFavoriteProductRepository.kt new file mode 100644 index 0000000..f5aa06b --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/domain/repository/IFavoriteProductRepository.kt @@ -0,0 +1,11 @@ +package com.mankart.eshop.core.domain.repository + +import com.mankart.eshop.core.data.Resource +import com.mankart.eshop.core.domain.model.Product +import kotlinx.coroutines.flow.Flow + +interface IFavoriteProductRepository { + fun getFavoriteProducts(): Flow>> + fun addFavoriteProduct(product: Product) + fun deleteFavoriteProductById(productId: String) +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/domain/repository/IProductRepository.kt b/core/src/main/java/com/mankart/eshop/core/domain/repository/IProductRepository.kt new file mode 100644 index 0000000..623a93c --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/domain/repository/IProductRepository.kt @@ -0,0 +1,11 @@ +package com.mankart.eshop.core.domain.repository + +import androidx.paging.PagingData +import com.mankart.eshop.core.data.Resource +import com.mankart.eshop.core.domain.model.Product +import kotlinx.coroutines.flow.Flow + +interface IProductRepository { + fun getProducts(): Flow> + fun getProductById(id: String): Flow> +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/domain/repository/IProfileRepository.kt b/core/src/main/java/com/mankart/eshop/core/domain/repository/IProfileRepository.kt new file mode 100644 index 0000000..dbb37be --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/domain/repository/IProfileRepository.kt @@ -0,0 +1,14 @@ +package com.mankart.eshop.core.domain.repository + +import com.mankart.eshop.core.data.Resource +import com.mankart.eshop.core.domain.model.User +import kotlinx.coroutines.flow.Flow + +interface IProfileRepository { + fun getProfile(): Flow> + suspend fun logout() + suspend fun getName(): Flow + suspend fun getEmail(): Flow + suspend fun saveName(name: String) + suspend fun saveEmail(email: String) +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/domain/repository/ITransactionRepository.kt b/core/src/main/java/com/mankart/eshop/core/domain/repository/ITransactionRepository.kt new file mode 100644 index 0000000..d1569e3 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/domain/repository/ITransactionRepository.kt @@ -0,0 +1,11 @@ +package com.mankart.eshop.core.domain.repository + +import com.mankart.eshop.core.data.Resource +import com.mankart.eshop.core.domain.model.Transaction +import kotlinx.coroutines.flow.Flow + +interface ITransactionRepository { + fun getTransactions(): Flow>> + fun getTransactionById(id: String): Flow> + fun checkout(): Flow> +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/domain/usecase/authentication/AuthenticationInteractor.kt b/core/src/main/java/com/mankart/eshop/core/domain/usecase/authentication/AuthenticationInteractor.kt new file mode 100644 index 0000000..afdf667 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/domain/usecase/authentication/AuthenticationInteractor.kt @@ -0,0 +1,15 @@ +package com.mankart.eshop.core.domain.usecase.authentication + +import com.mankart.eshop.core.data.Resource +import com.mankart.eshop.core.domain.model.User +import com.mankart.eshop.core.domain.repository.IAuthenticationRepository +import kotlinx.coroutines.flow.Flow +import javax.inject.Inject + +class AuthenticationInteractor @Inject constructor(private val repository: IAuthenticationRepository): AuthenticationUseCase { + override fun login(email: String, password: String): Flow> = + repository.postLogin(email, password) + + override fun register(name: String, email: String, password: String): Flow> = + repository.postRegister(name, email, password) +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/domain/usecase/authentication/AuthenticationUseCase.kt b/core/src/main/java/com/mankart/eshop/core/domain/usecase/authentication/AuthenticationUseCase.kt new file mode 100644 index 0000000..63ce7ae --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/domain/usecase/authentication/AuthenticationUseCase.kt @@ -0,0 +1,10 @@ +package com.mankart.eshop.core.domain.usecase.authentication + +import com.mankart.eshop.core.data.Resource +import com.mankart.eshop.core.domain.model.User +import kotlinx.coroutines.flow.Flow + +interface AuthenticationUseCase { + fun login(email: String, password: String): Flow> + fun register(name: String, email: String, password: String): Flow> +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/domain/usecase/cart/CartInteractor.kt b/core/src/main/java/com/mankart/eshop/core/domain/usecase/cart/CartInteractor.kt new file mode 100644 index 0000000..7642a80 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/domain/usecase/cart/CartInteractor.kt @@ -0,0 +1,20 @@ +package com.mankart.eshop.core.domain.usecase.cart + +import com.mankart.eshop.core.data.Resource +import com.mankart.eshop.core.domain.model.Cart +import com.mankart.eshop.core.domain.repository.ICartRepository +import kotlinx.coroutines.flow.Flow +import javax.inject.Inject + +class CartInteractor @Inject constructor(private val repository: ICartRepository) : CartUseCase { + override fun getCarts(): Flow> = repository.getCarts() + + override fun addItemToCart(productId: String, quantity: Int): Flow> = + repository.addItemToCart(productId, quantity) + + override fun updateItemInCart(itemId: String, quantity: Int): Flow> = + repository.updateItemInCart(itemId, quantity) + + override fun deleteItemFromCart(itemId: String): Flow> = + repository.deleteItemFromCart(itemId) +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/domain/usecase/cart/CartUseCase.kt b/core/src/main/java/com/mankart/eshop/core/domain/usecase/cart/CartUseCase.kt new file mode 100644 index 0000000..8644e06 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/domain/usecase/cart/CartUseCase.kt @@ -0,0 +1,12 @@ +package com.mankart.eshop.core.domain.usecase.cart + +import com.mankart.eshop.core.data.Resource +import com.mankart.eshop.core.domain.model.Cart +import kotlinx.coroutines.flow.Flow + +interface CartUseCase { + fun getCarts(): Flow> + fun addItemToCart(productId: String, quantity: Int): Flow> + fun updateItemInCart(itemId: String, quantity: Int): Flow> + fun deleteItemFromCart(itemId: String): Flow> +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/domain/usecase/favproduct/FavoriteProductInteractor.kt b/core/src/main/java/com/mankart/eshop/core/domain/usecase/favproduct/FavoriteProductInteractor.kt new file mode 100644 index 0000000..1d66d10 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/domain/usecase/favproduct/FavoriteProductInteractor.kt @@ -0,0 +1,18 @@ +package com.mankart.eshop.core.domain.usecase.favproduct + +import com.mankart.eshop.core.data.Resource +import com.mankart.eshop.core.domain.model.Product +import com.mankart.eshop.core.domain.repository.IFavoriteProductRepository +import kotlinx.coroutines.flow.Flow +import javax.inject.Inject + +class FavoriteProductInteractor @Inject constructor(private val repository: IFavoriteProductRepository): FavoriteProductUseCase { + override fun getFavoriteProducts(): Flow>> = + repository.getFavoriteProducts() + + override fun addFavoriteProduct(product: Product) = + repository.addFavoriteProduct(product) + + override fun deleteFavoriteProductById(productId: String) = + repository.deleteFavoriteProductById(productId) +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/domain/usecase/favproduct/FavoriteProductUseCase.kt b/core/src/main/java/com/mankart/eshop/core/domain/usecase/favproduct/FavoriteProductUseCase.kt new file mode 100644 index 0000000..0e1a4fa --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/domain/usecase/favproduct/FavoriteProductUseCase.kt @@ -0,0 +1,11 @@ +package com.mankart.eshop.core.domain.usecase.favproduct + +import com.mankart.eshop.core.data.Resource +import com.mankart.eshop.core.domain.model.Product +import kotlinx.coroutines.flow.Flow + +interface FavoriteProductUseCase { + fun getFavoriteProducts(): Flow>> + fun addFavoriteProduct(product: Product) + fun deleteFavoriteProductById(productId: String) +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/domain/usecase/products/ProductInteractor.kt b/core/src/main/java/com/mankart/eshop/core/domain/usecase/products/ProductInteractor.kt new file mode 100644 index 0000000..33842e6 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/domain/usecase/products/ProductInteractor.kt @@ -0,0 +1,16 @@ +package com.mankart.eshop.core.domain.usecase.products + +import androidx.paging.PagingData +import com.mankart.eshop.core.data.Resource +import com.mankart.eshop.core.domain.model.Product +import com.mankart.eshop.core.domain.repository.IProductRepository +import kotlinx.coroutines.flow.Flow +import javax.inject.Inject + +class ProductInteractor @Inject constructor(private val repository: IProductRepository) : ProductUseCase { + override fun getProducts(): Flow> = + repository.getProducts() + + override fun getProductById(id: String): Flow> = + repository.getProductById(id) +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/domain/usecase/products/ProductUseCase.kt b/core/src/main/java/com/mankart/eshop/core/domain/usecase/products/ProductUseCase.kt new file mode 100644 index 0000000..2b540a5 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/domain/usecase/products/ProductUseCase.kt @@ -0,0 +1,11 @@ +package com.mankart.eshop.core.domain.usecase.products + +import androidx.paging.PagingData +import com.mankart.eshop.core.data.Resource +import com.mankart.eshop.core.domain.model.Product +import kotlinx.coroutines.flow.Flow + +interface ProductUseCase { + fun getProducts(): Flow> + fun getProductById(id: String): Flow> +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/domain/usecase/profile/ProfileInteractor.kt b/core/src/main/java/com/mankart/eshop/core/domain/usecase/profile/ProfileInteractor.kt new file mode 100644 index 0000000..3bb36e2 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/domain/usecase/profile/ProfileInteractor.kt @@ -0,0 +1,21 @@ +package com.mankart.eshop.core.domain.usecase.profile + +import com.mankart.eshop.core.data.Resource +import com.mankart.eshop.core.domain.model.User +import com.mankart.eshop.core.domain.repository.IProfileRepository +import kotlinx.coroutines.flow.Flow +import javax.inject.Inject + +class ProfileInteractor @Inject constructor(private val repository: IProfileRepository) : ProfileUseCase { + override fun getUserProfile(): Flow> = repository.getProfile() + + override suspend fun logout() = repository.logout() + + override suspend fun getName(): Flow = repository.getName() + + override suspend fun getEmail(): Flow = repository.getEmail() + + override suspend fun saveName(name: String) = repository.saveName(name) + + override suspend fun saveEmail(email: String) = repository.saveEmail(email) +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/domain/usecase/profile/ProfileUseCase.kt b/core/src/main/java/com/mankart/eshop/core/domain/usecase/profile/ProfileUseCase.kt new file mode 100644 index 0000000..c1f5859 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/domain/usecase/profile/ProfileUseCase.kt @@ -0,0 +1,14 @@ +package com.mankart.eshop.core.domain.usecase.profile + +import com.mankart.eshop.core.data.Resource +import com.mankart.eshop.core.domain.model.User +import kotlinx.coroutines.flow.Flow + +interface ProfileUseCase { + fun getUserProfile(): Flow> + suspend fun logout() + suspend fun getName(): Flow + suspend fun getEmail(): Flow + suspend fun saveName(name: String) + suspend fun saveEmail(email: String) +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/domain/usecase/transaction/TransactionInteractor.kt b/core/src/main/java/com/mankart/eshop/core/domain/usecase/transaction/TransactionInteractor.kt new file mode 100644 index 0000000..d3f38d6 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/domain/usecase/transaction/TransactionInteractor.kt @@ -0,0 +1,18 @@ +package com.mankart.eshop.core.domain.usecase.transaction + +import com.mankart.eshop.core.data.Resource +import com.mankart.eshop.core.domain.model.Transaction +import com.mankart.eshop.core.domain.repository.ITransactionRepository +import kotlinx.coroutines.flow.Flow +import javax.inject.Inject + +class TransactionInteractor @Inject constructor(private val repository: ITransactionRepository): TransactionUseCase { + override fun getTransactions(): Flow>> = + repository.getTransactions() + + override fun getTransactionById(id: String): Flow> = + repository.getTransactionById(id) + + override fun checkout(): Flow> = + repository.checkout() +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/domain/usecase/transaction/TransactionUseCase.kt b/core/src/main/java/com/mankart/eshop/core/domain/usecase/transaction/TransactionUseCase.kt new file mode 100644 index 0000000..88b6767 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/domain/usecase/transaction/TransactionUseCase.kt @@ -0,0 +1,11 @@ +package com.mankart.eshop.core.domain.usecase.transaction + +import com.mankart.eshop.core.data.Resource +import com.mankart.eshop.core.domain.model.Transaction +import kotlinx.coroutines.flow.Flow + +interface TransactionUseCase { + fun getTransactions(): Flow>> + fun getTransactionById(id: String): Flow> + fun checkout(): Flow> +} \ No newline at end of file diff --git a/core/src/main/java/com/mankart/eshop/core/utils/AppExecutors.kt b/core/src/main/java/com/mankart/eshop/core/utils/AppExecutors.kt new file mode 100644 index 0000000..a8414a9 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/utils/AppExecutors.kt @@ -0,0 +1,41 @@ +package com.mankart.eshop.core.utils + +import android.os.Handler +import android.os.Looper +import androidx.annotation.VisibleForTesting + +import java.util.concurrent.Executor +import java.util.concurrent.Executors +import javax.inject.Inject + +class AppExecutors @VisibleForTesting constructor( + private val diskIO: Executor, + private val networkIO: Executor, + private val mainThread: Executor +) { + + companion object { + private const val THREAD_COUNT = 3 + } + + @Inject + constructor() : this( + Executors.newSingleThreadExecutor(), + Executors.newFixedThreadPool(THREAD_COUNT), + MainThreadExecutor() + ) + + fun diskIO(): Executor = diskIO + + fun networkIO(): Executor = networkIO + + fun mainThread(): Executor = mainThread + + private class MainThreadExecutor : Executor { + private val mainThreadHandler = Handler(Looper.getMainLooper()) + + override fun execute(command: Runnable) { + mainThreadHandler.post(command) + } + } +} diff --git a/core/src/main/java/com/mankart/eshop/core/utils/DataMapper.kt b/core/src/main/java/com/mankart/eshop/core/utils/DataMapper.kt new file mode 100644 index 0000000..e7a2915 --- /dev/null +++ b/core/src/main/java/com/mankart/eshop/core/utils/DataMapper.kt @@ -0,0 +1,111 @@ +package com.mankart.eshop.core.utils + +import com.mankart.eshop.core.data.source.local.entity.FavoriteProductEntity +import com.mankart.eshop.core.data.source.local.entity.ProductEntity +import com.mankart.eshop.core.data.source.remote.response.* +import com.mankart.eshop.core.domain.model.* + +object DataMapper { + fun mapLoginResponseToDomain(input: LoginResponse) : User = User( + id = input.id, + token = input.token, + ) + + fun mapProfileResponseToDomain(input: ProfileResponse) : User = User( + name = input.name, + email = input.email, + ) + + fun mapProductsResponseToEntity(input: ProductResponse) : ProductEntity = ProductEntity( + id = input.id, + title = input.title, + price = input.price, + description = input.description, + image = input.image, + category = input.category, + rating = input.rating?.rating ?: 0.0, + countRate = input.rating?.countRate ?: 0, + ) + + fun mapProductEntityToDomain(input: ProductEntity): Product = Product( + id = input.id, + title = input.title, + price = input.price, + description = input.description, + image = input.image, + category = input.category, + rating = input.rating, + countRate = input.countRate + ) + + fun mapProductResponseToDomain(input: ProductResponse): Product = Product( + id = input.id, + title = input.title, + price = input.price, + description = input.description, + image = input.image, + category = input.category, + rating = input.rating?.rating ?: 0.0, + countRate = input.rating?.countRate ?: 0, + reviews = input.reviews?.map { mapUserReviewResponseToDomain(it) } ?: emptyList() + ) + + private fun mapUserReviewResponseToDomain(input: ReviewResponse): UserReview = UserReview( + user = input.user, + rate = input.rate, + review = input.review, + ) + + fun mapCartResponseToDomain(input: CartResponse): Cart = Cart( + totalItem = input.totalItem, + subTotal = input.subTotal, + cart = input.cart.map { mapCartItemResponseToDomain(it) } + ) + + private fun mapCartItemResponseToDomain(input: CartItemResponse): CartItem = CartItem( + id = input.id, + productId = input.productId, + title = input.title, + price = input.price, + description = input.description, + image = input.image, + quantity = input.quantity, + ) + + fun mapTransactionResponseToDomain(input: TransactionResponse): Transaction = Transaction( + id = input.id, + dateCreated = input.dateCreated, + totalItem = input.totalItem, + totalPrice = input.totalPrice, + orders = input.orders?.map { mapOrderResponseToDomain(it) } ?: emptyList() + ) + + private fun mapOrderResponseToDomain(input: OrderResponse): Order = Order( + id = input.id, + title = input.title, + price = input.price, + image = input.image, + quantity = input.quantity, + yourRating = input.yourRating, + yourReview = input.yourReview, + ) + + fun mapFavouriteProductEntityToDomain(input: FavoriteProductEntity): Product { + val favouriteProductEntity = input.product + return mapProductEntityToDomain(favouriteProductEntity) + } + + fun mapFavouriteProductDomainToEntity(input: Product): FavoriteProductEntity { + val productEntity = ProductEntity( + id = input.id, + title = input.title, + price = input.price, + description = input.description, + image = input.image, + category = input.category, + rating = input.rating, + countRate = input.countRate + ) + return FavoriteProductEntity(product = productEntity) + } +} \ No newline at end of file diff --git a/core/src/test/java/com/mankart/eshop/core/ExampleUnitTest.kt b/core/src/test/java/com/mankart/eshop/core/ExampleUnitTest.kt new file mode 100644 index 0000000..4501696 --- /dev/null +++ b/core/src/test/java/com/mankart/eshop/core/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.mankart.eshop.core + +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) + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index b9e88e1..df6e5d5 100644 --- a/settings.gradle +++ b/settings.gradle @@ -14,3 +14,4 @@ dependencyResolutionManagement { } rootProject.name = "Eshop" include ':app' +include ':core' diff --git a/shared_dependencies.gradle b/shared_dependencies.gradle new file mode 100644 index 0000000..b4c3999 --- /dev/null +++ b/shared_dependencies.gradle @@ -0,0 +1,18 @@ +dependencies { + implementation 'androidx.core:core-ktx:1.7.0' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1' + implementation 'androidx.appcompat:appcompat:1.4.2' + implementation 'com.google.android.material:material:1.6.1' + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + + // dagger hilt + implementation "com.google.dagger:hilt-android:2.43.2" + kapt "com.google.dagger:hilt-android-compiler:2.43.2" + + // coroutine + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1" +} \ No newline at end of file