diff --git a/app/build.gradle.kts b/app/build.gradle.kts index e1f9abb..4e34e07 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -76,7 +76,7 @@ dependencies { implementation("androidx.compose.ui:ui-tooling-preview:1.6.0-beta02") implementation("androidx.compose.material3:material3:1.1.2") implementation("androidx.paging:paging-compose:3.2.1") - implementation("eu.bambooapps:compose-material3-pullrefresh:1.0.0") + implementation("eu.bambooapps:compose-material3-pullrefresh:1.0.1") // Room val roomVersion = "2.6.1" diff --git a/app/src/main/java/com/zyzf/coffeepreorder/api/MyServerService.kt b/app/src/main/java/com/zyzf/coffeepreorder/api/MyServerService.kt new file mode 100644 index 0000000..9c0d678 --- /dev/null +++ b/app/src/main/java/com/zyzf/coffeepreorder/api/MyServerService.kt @@ -0,0 +1,66 @@ +package com.zyzf.coffeepreorder.api + +import com.zyzf.coffeepreorder.api.model.CoffeeRemote +import retrofit2.http.Body +import retrofit2.http.DELETE +import retrofit2.http.GET +import retrofit2.http.POST +import retrofit2.http.PUT +import retrofit2.http.Path +import retrofit2.http.Query + +interface MyServerService { + @GET("groups") + suspend fun getGroups(): List + + @GET("coffee") + suspend fun getCoffees( + @Query("_page") page: Int, + @Query("_limit") limit: Int, + ): List + + @GET("coffee/{id}") + suspend fun getCoffee( + @Path("id") id: Int, + ): CoffeeRemote + + @POST("coffee") + suspend fun createCoffee( + @Body student: CoffeeRemote, + ): CoffeeRemote + + @PUT("coffee/{id}") + suspend fun updateCoffee( + @Path("id") id: Int, + @Body student: CoffeeRemote, + ): CoffeeRemote + + @DELETE("coffee/{id}") + suspend fun deleteCoffee( + @Path("id") id: Int, + ): CoffeeRemote + + companion object { + private const val BASE_URL = "http://10.120.175.105:8079/" + + @Volatile + private var INSTANCE: MyServerService? = null + + fun getInstance(): MyServerService { + return INSTANCE ?: synchronized(this) { + val logger = HttpLoggingInterceptor() + logger.level = HttpLoggingInterceptor.Level.BASIC + val client = OkHttpClient.Builder() + .addInterceptor(logger) + .build() + return Retrofit.Builder() + .baseUrl(BASE_URL) + .client(client) + .addConverterFactory(Json.asConverterFactory("application/json".toMediaType())) + .build() + .create(MyServerService::class.java) + .also { INSTANCE = it } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/zyzf/coffeepreorder/api/coffee/CoffeeRemoteMediator.kt b/app/src/main/java/com/zyzf/coffeepreorder/api/coffee/CoffeeRemoteMediator.kt new file mode 100644 index 0000000..859b98e --- /dev/null +++ b/app/src/main/java/com/zyzf/coffeepreorder/api/coffee/CoffeeRemoteMediator.kt @@ -0,0 +1,108 @@ +package com.zyzf.coffeepreorder.api.coffee + +import androidx.paging.ExperimentalPagingApi +import androidx.paging.LoadType +import androidx.paging.PagingState +import androidx.paging.RemoteMediator +import androidx.room.withTransaction +import com.zyzf.coffeepreorder.api.MyServerService +import com.zyzf.coffeepreorder.api.model.toCoffee +import com.zyzf.coffeepreorder.database.AppDatabase +import com.zyzf.coffeepreorder.database.model.Coffee +import com.zyzf.coffeepreorder.database.model.RemoteKeyType +import com.zyzf.coffeepreorder.database.model.RemoteKeys +import com.zyzf.coffeepreorder.database.repository.OfflineCoffeeRepository +import com.zyzf.coffeepreorder.database.repository.OfflineRemoteKeyRepository +import retrofit2.HttpException +import java.io.IOException + +@OptIn(ExperimentalPagingApi::class) +class CoffeeRemoteMediator( + private val service: MyServerService, + private val dbCoffeeRepository: OfflineCoffeeRepository, + private val dbRemoteKeyRepository: OfflineRemoteKeyRepository, + private val database: AppDatabase +) : RemoteMediator() { + + override suspend fun initialize(): InitializeAction { + return InitializeAction.LAUNCH_INITIAL_REFRESH + } + + override suspend fun load( + loadType: LoadType, + state: PagingState + ): MediatorResult { + val page = when (loadType) { + LoadType.REFRESH -> { + val remoteKeys = getRemoteKeyClosestToCurrentPosition(state) + remoteKeys?.nextKey?.minus(1) ?: 1 + } + + LoadType.PREPEND -> { + val remoteKeys = getRemoteKeyForFirstItem(state) + remoteKeys?.prevKey + ?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null) + } + + LoadType.APPEND -> { + val remoteKeys = getRemoteKeyForLastItem(state) + remoteKeys?.nextKey + ?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null) + } + } + + try { + val coffees = service.getCoffees(page, state.config.pageSize).map { it.toCoffee() } + val endOfPaginationReached = coffees.isEmpty() + database.withTransaction { + if (loadType == LoadType.REFRESH) { + dbRemoteKeyRepository.deleteRemoteKey(RemoteKeyType.Coffee) + dbCoffeeRepository.clearCoffees() + } + val prevKey = if (page == 1) null else page - 1 + val nextKey = if (endOfPaginationReached) null else page + 1 + val keys = coffees.map { + RemoteKeys( + entityId = it.uid, + type = RemoteKeyType.Coffee, + prevKey = prevKey, + nextKey = nextKey + ) + } + dbRemoteKeyRepository.createRemoteKeys(keys) + for (coffee in coffees) { + dbCoffeeRepository.insert(coffee.name, coffee.cost, coffee.ingredients) + } + } + return MediatorResult.Success(endOfPaginationReached = endOfPaginationReached) + } catch (exception: IOException) { + return MediatorResult.Error(exception) + } catch (exception: HttpException) { + return MediatorResult.Error(exception) + } + } + + private suspend fun getRemoteKeyForLastItem(state: PagingState): RemoteKeys? { + return state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull() + ?.let { coffee -> + dbRemoteKeyRepository.getAllRemoteKeys(coffee.uid, RemoteKeyType.Coffee) + } + } + + private suspend fun getRemoteKeyForFirstItem(state: PagingState): RemoteKeys? { + return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull() + ?.let { coffee -> + dbRemoteKeyRepository.getAllRemoteKeys(coffee.uid, RemoteKeyType.Coffee) + } + } + + private suspend fun getRemoteKeyClosestToCurrentPosition( + state: PagingState + ): RemoteKeys? { + return state.anchorPosition?.let { position -> + state.closestItemToPosition(position)?.uid?.let { coffeeUid -> + dbRemoteKeyRepository.getAllRemoteKeys(coffeeUid, RemoteKeyType.Coffee) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/zyzf/coffeepreorder/api/coffee/RestCoffeeRepository.kt b/app/src/main/java/com/zyzf/coffeepreorder/api/coffee/RestCoffeeRepository.kt new file mode 100644 index 0000000..fa2c8f0 --- /dev/null +++ b/app/src/main/java/com/zyzf/coffeepreorder/api/coffee/RestCoffeeRepository.kt @@ -0,0 +1,60 @@ +package com.zyzf.coffeepreorder.api.coffee + +import android.util.Log +import androidx.paging.ExperimentalPagingApi +import androidx.paging.Pager +import androidx.paging.PagingConfig +import androidx.paging.PagingData +import com.zyzf.coffeepreorder.api.MyServerService +import com.zyzf.coffeepreorder.api.model.toCoffee +import com.zyzf.coffeepreorder.api.model.toCoffeeRemote +import com.zyzf.coffeepreorder.database.AppContainer +import com.zyzf.coffeepreorder.database.AppDatabase +import com.zyzf.coffeepreorder.database.model.Coffee +import com.zyzf.coffeepreorder.database.repository.CoffeeRepository +import com.zyzf.coffeepreorder.database.repository.OfflineCoffeeRepository +import com.zyzf.coffeepreorder.database.repository.OfflineRemoteKeyRepository +import kotlinx.coroutines.flow.Flow + +class RestCoffeeRepository( + private val service: MyServerService, + private val dbCoffeeRepository: OfflineCoffeeRepository, + private val dbRemoteKeyRepository: OfflineRemoteKeyRepository, + private val database: AppDatabase +) : CoffeeRepository { + override fun getAll(): Flow> { + Log.d(RestCoffeeRepository::class.simpleName, "Get coffees") + + val pagingSourceFactory = { dbCoffeeRepository.getAllCoffeesPagingSource() } + + @OptIn(ExperimentalPagingApi::class) + return Pager( + config = PagingConfig( + pageSize = AppContainer.LIMIT, + enablePlaceholders = false + ), + remoteMediator = CoffeeRemoteMediator( + service, + dbCoffeeRepository, + dbRemoteKeyRepository, + database, + ), + pagingSourceFactory = pagingSourceFactory + ).flow + } + + override suspend fun getByUid(uid: Int): Coffee? = + service.getCoffee(uid).toCoffee() + + override suspend fun insert(name: String, cost: Double, ingredients: String): Int { + return service.createCoffee(Coffee(name, cost, ingredients, null, 0).toCoffeeRemote()).toCoffee().uid + } + + override suspend fun update(uid: Int, name: String, cost: Double, ingredients: String, cartId: Int?, count: Int): Int? { + return service.updateCoffee(uid, Coffee(uid, name, cost, ingredients, cartId, count).toCoffeeRemote()).toCoffee().uid + } + + override suspend fun delete(coffee: Coffee) { + service.deleteCoffee(coffee.uid).toCoffee() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/zyzf/coffeepreorder/api/model/CoffeeRemote.kt b/app/src/main/java/com/zyzf/coffeepreorder/api/model/CoffeeRemote.kt new file mode 100644 index 0000000..1fce4f3 --- /dev/null +++ b/app/src/main/java/com/zyzf/coffeepreorder/api/model/CoffeeRemote.kt @@ -0,0 +1,32 @@ +package com.zyzf.coffeepreorder.api.model + +import com.zyzf.coffeepreorder.database.model.Coffee +import kotlinx.serialization.Serializable + +@Serializable +data class CoffeeRemote( + val id: Int = 0, + val name: String = "", + val cost: Double = 0.0, + val ingredients: String = "", + val cartId: Int? = 0, + val count: Int = 0 +) + +fun CoffeeRemote.toCoffee(): Coffee = Coffee( + id, + name, + cost, + ingredients, + cartId, + count +) + +fun Coffee.toCoffeeRemote(): CoffeeRemote = CoffeeRemote( + uid, + name, + cost, + ingredients, + cartId, + count +) \ No newline at end of file diff --git a/app/src/main/java/com/zyzf/coffeepreorder/api/model/UserRemote.kt b/app/src/main/java/com/zyzf/coffeepreorder/api/model/UserRemote.kt new file mode 100644 index 0000000..95ffa45 --- /dev/null +++ b/app/src/main/java/com/zyzf/coffeepreorder/api/model/UserRemote.kt @@ -0,0 +1,32 @@ +package com.zyzf.coffeepreorder.api.model + +import com.zyzf.coffeepreorder.database.model.User +import kotlinx.serialization.Serializable + +@Serializable +data class UserRemote( + val id: Int = 0, + val login: String = "", + val fio: String = "", + val phone: String = "", + val password: String = "", + val role: String = "user" +) + +fun UserRemote.toUser(): User = User( + id, + login, + fio, + phone, + password, + role +) + +fun User.toUserRemote(): UserRemote = UserRemote( + uid, + login, + fio, + phone, + password, + role +) \ No newline at end of file diff --git a/app/src/main/java/com/zyzf/coffeepreorder/api/user/RestUserRepository.kt b/app/src/main/java/com/zyzf/coffeepreorder/api/user/RestUserRepository.kt new file mode 100644 index 0000000..0d85157 --- /dev/null +++ b/app/src/main/java/com/zyzf/coffeepreorder/api/user/RestUserRepository.kt @@ -0,0 +1,64 @@ +package com.zyzf.coffeepreorder.api.user + +import android.util.Log +import androidx.paging.ExperimentalPagingApi +import androidx.paging.Pager +import androidx.paging.PagingConfig +import androidx.paging.PagingData +import com.zyzf.coffeepreorder.api.MyServerService +import com.zyzf.coffeepreorder.api.coffee.CoffeeRemoteMediator +import com.zyzf.coffeepreorder.api.model.toCoffee +import com.zyzf.coffeepreorder.api.model.toCoffeeRemote +import com.zyzf.coffeepreorder.database.AppContainer +import com.zyzf.coffeepreorder.database.AppDatabase +import com.zyzf.coffeepreorder.database.model.Coffee +import com.zyzf.coffeepreorder.database.model.User +import com.zyzf.coffeepreorder.database.repository.CoffeeRepository +import com.zyzf.coffeepreorder.database.repository.OfflineCoffeeRepository +import com.zyzf.coffeepreorder.database.repository.OfflineRemoteKeyRepository +import com.zyzf.coffeepreorder.database.repository.OfflineUserRepository +import com.zyzf.coffeepreorder.database.repository.UserRepository +import kotlinx.coroutines.flow.Flow + +class RestUserRepository( + private val service: MyServerService, + private val dbUserRepository: OfflineUserRepository, + private val dbRemoteKeyRepository: OfflineRemoteKeyRepository, + private val database: AppDatabase +) : UserRepository { + override fun getAll(): Flow> { + Log.d(RestUserRepository::class.simpleName, "Get users") + + val pagingSourceFactory = { dbUserRepository.getAllUserPagingSource() } + + @OptIn(ExperimentalPagingApi::class) + return Pager( + config = PagingConfig( + pageSize = AppContainer.LIMIT, + enablePlaceholders = false + ), + remoteMediator = UserRemoteMediator( + service, + dbUserRepository, + dbRemoteKeyRepository, + database, + ), + pagingSourceFactory = pagingSourceFactory + ).flow + } + + override suspend fun getByUid(uid: Int): User? = + service.getUser(uid).toUser() + + override suspend fun insert(name: String, cost: Double, ingredients: String): Int { + return service.createCoffee(Coffee(name, cost, ingredients, null, 0).toCoffeeRemote()).toCoffee().uid + } + + override suspend fun update(uid: Int, name: String, cost: Double, ingredients: String, cartId: Int?, count: Int): Int? { + return service.updateCoffee(uid, Coffee(uid, name, cost, ingredients, cartId, count).toCoffeeRemote()).toCoffee().uid + } + + override suspend fun delete(coffee: Coffee) { + service.deleteCoffee(coffee.uid).toCoffee() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/zyzf/coffeepreorder/api/user/UserRemoteMediator.kt b/app/src/main/java/com/zyzf/coffeepreorder/api/user/UserRemoteMediator.kt new file mode 100644 index 0000000..7191d36 --- /dev/null +++ b/app/src/main/java/com/zyzf/coffeepreorder/api/user/UserRemoteMediator.kt @@ -0,0 +1,110 @@ +package com.zyzf.coffeepreorder.api.user + +import androidx.paging.ExperimentalPagingApi +import androidx.paging.LoadType +import androidx.paging.PagingState +import androidx.paging.RemoteMediator +import androidx.room.withTransaction +import com.zyzf.coffeepreorder.api.MyServerService +import com.zyzf.coffeepreorder.api.model.toCoffee +import com.zyzf.coffeepreorder.database.AppDatabase +import com.zyzf.coffeepreorder.database.model.Coffee +import com.zyzf.coffeepreorder.database.model.RemoteKeyType +import com.zyzf.coffeepreorder.database.model.RemoteKeys +import com.zyzf.coffeepreorder.database.model.User +import com.zyzf.coffeepreorder.database.repository.OfflineCoffeeRepository +import com.zyzf.coffeepreorder.database.repository.OfflineRemoteKeyRepository +import com.zyzf.coffeepreorder.database.repository.OfflineUserRepository +import retrofit2.HttpException +import java.io.IOException + +@OptIn(ExperimentalPagingApi::class) +class UserRemoteMediator( + private val service: MyServerService, + private val dbUserRepository: OfflineUserRepository, + private val dbRemoteKeyRepository: OfflineRemoteKeyRepository, + private val database: AppDatabase +) : RemoteMediator() { + + override suspend fun initialize(): InitializeAction { + return InitializeAction.LAUNCH_INITIAL_REFRESH + } + + override suspend fun load( + loadType: LoadType, + state: PagingState + ): MediatorResult { + val page = when (loadType) { + LoadType.REFRESH -> { + val remoteKeys = getRemoteKeyClosestToCurrentPosition(state) + remoteKeys?.nextKey?.minus(1) ?: 1 + } + + LoadType.PREPEND -> { + val remoteKeys = getRemoteKeyForFirstItem(state) + remoteKeys?.prevKey + ?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null) + } + + LoadType.APPEND -> { + val remoteKeys = getRemoteKeyForLastItem(state) + remoteKeys?.nextKey + ?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null) + } + } + + try { + val coffees = service.getUsers(page, state.config.pageSize).map { it.toCoffee() } + val endOfPaginationReached = coffees.isEmpty() + database.withTransaction { + if (loadType == LoadType.REFRESH) { + dbRemoteKeyRepository.deleteRemoteKey(RemoteKeyType.User) + dbUserRepository.clearCoffees() + } + val prevKey = if (page == 1) null else page - 1 + val nextKey = if (endOfPaginationReached) null else page + 1 + val keys = coffees.map { + RemoteKeys( + entityId = it.uid, + type = RemoteKeyType.User, + prevKey = prevKey, + nextKey = nextKey + ) + } + dbRemoteKeyRepository.createRemoteKeys(keys) + for (user in coffees) { + dbUserRepository.insert() + } + } + return MediatorResult.Success(endOfPaginationReached = endOfPaginationReached) + } catch (exception: IOException) { + return MediatorResult.Error(exception) + } catch (exception: HttpException) { + return MediatorResult.Error(exception) + } + } + + private suspend fun getRemoteKeyForLastItem(state: PagingState): RemoteKeys? { + return state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull() + ?.let { user -> + dbRemoteKeyRepository.getAllRemoteKeys(user.uid, RemoteKeyType.User) + } + } + + private suspend fun getRemoteKeyForFirstItem(state: PagingState): RemoteKeys? { + return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull() + ?.let { user -> + dbRemoteKeyRepository.getAllRemoteKeys(user.uid, RemoteKeyType.User) + } + } + + private suspend fun getRemoteKeyClosestToCurrentPosition( + state: PagingState + ): RemoteKeys? { + return state.anchorPosition?.let { position -> + state.closestItemToPosition(position)?.uid?.let { userUid -> + dbRemoteKeyRepository.getAllRemoteKeys(userUid, RemoteKeyType.User) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/zyzf/coffeepreorder/database/AppContainer.kt b/app/src/main/java/com/zyzf/coffeepreorder/database/AppContainer.kt index af07db1..80a0f31 100644 --- a/app/src/main/java/com/zyzf/coffeepreorder/database/AppContainer.kt +++ b/app/src/main/java/com/zyzf/coffeepreorder/database/AppContainer.kt @@ -12,6 +12,11 @@ interface AppContainer { val coffeeRepository: CoffeeRepository val userRepository: UserRepository val cartRepository: CartRepository + + companion object { + const val TIMEOUT = 5000L + const val LIMIT = 10 + } } class AppDataContainer(private val context: Context) : AppContainer { @@ -24,9 +29,4 @@ class AppDataContainer(private val context: Context) : AppContainer { override val cartRepository: CartRepository by lazy { OfflineCartRepository(AppDatabase.getInstance(context).cartDao()) } - - companion object { - const val TIMEOUT = 5000L - const val LIMIT = 10 - } } \ No newline at end of file diff --git a/app/src/main/java/com/zyzf/coffeepreorder/database/dao/CartDao.kt b/app/src/main/java/com/zyzf/coffeepreorder/database/dao/CartDao.kt index 57e1738..3917816 100644 --- a/app/src/main/java/com/zyzf/coffeepreorder/database/dao/CartDao.kt +++ b/app/src/main/java/com/zyzf/coffeepreorder/database/dao/CartDao.kt @@ -1,16 +1,24 @@ package com.zyzf.coffeepreorder.database.dao +import androidx.paging.PagingSource import androidx.room.Dao import androidx.room.Insert import androidx.room.Query import androidx.room.Update import com.zyzf.coffeepreorder.database.model.Cart +import com.zyzf.coffeepreorder.database.model.Coffee @Dao interface CartDao { @Query("select * from cart limit 1") suspend fun get(): Cart + @Query("select * from coffee where cart_id is not null and count > 0 order by name collate nocase asc") + fun getAllInCart(): PagingSource + + @Query("select sum(cost) from coffee where cart_id is not null and count > 0") + fun getSumInCart(): Double + @Insert suspend fun insert(cart: Cart) diff --git a/app/src/main/java/com/zyzf/coffeepreorder/database/dao/CoffeeDao.kt b/app/src/main/java/com/zyzf/coffeepreorder/database/dao/CoffeeDao.kt index 14974be..f164e7e 100644 --- a/app/src/main/java/com/zyzf/coffeepreorder/database/dao/CoffeeDao.kt +++ b/app/src/main/java/com/zyzf/coffeepreorder/database/dao/CoffeeDao.kt @@ -5,28 +5,24 @@ import androidx.room.Dao import androidx.room.Delete import androidx.room.Query import com.zyzf.coffeepreorder.database.model.Coffee -import com.zyzf.coffeepreorder.database.model.CoffeeWithCart @Dao interface CoffeeDao { @Query("select * from coffee order by name collate nocase asc") fun getAll(): PagingSource - @Query("select * from coffee where cart_id is not null and count > 0 order by name collate nocase asc") - fun getAllInCart(): PagingSource - - @Query("select sum(cost) from coffee where cart_id is not null and count > 0") - fun getSumInCart(): Double - - @Query("select coffee.uid, name, cost, ingredients, cart_id, count, cart.uid as cart_uid from coffee left join cart on coffee.cart_id = cart.uid where coffee.uid = :uid") - suspend fun getByUid(uid: Int): CoffeeWithCart? + @Query("select coffee.uid, name, cost, ingredients, cart_id, count from coffee where coffee.uid = :uid") + suspend fun getByUid(uid: Int): Coffee? @Query("insert into coffee (name, cost, ingredients, count) values (:name, :cost, :ingredients, 0)") - suspend fun insert(name: String, cost: Double, ingredients: String): Long + suspend fun insert(name: String, cost: Double, ingredients: String): Int @Query("update coffee set name = :name, cost = :cost, ingredients = :ingredients, cart_id = :cartId, count = :count where uid = :uid") suspend fun update(uid: Int, name: String, cost: Double, ingredients: String, cartId: Int?, count: Int): Int? @Delete suspend fun delete(coffee: Coffee) + + @Query("delete from coffee") + suspend fun deleteAll() } \ No newline at end of file diff --git a/app/src/main/java/com/zyzf/coffeepreorder/database/dao/RemoteKeysDao.kt b/app/src/main/java/com/zyzf/coffeepreorder/database/dao/RemoteKeysDao.kt new file mode 100644 index 0000000..3d29351 --- /dev/null +++ b/app/src/main/java/com/zyzf/coffeepreorder/database/dao/RemoteKeysDao.kt @@ -0,0 +1,20 @@ +package com.zyzf.coffeepreorder.database.dao + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import com.zyzf.coffeepreorder.database.model.RemoteKeyType +import com.zyzf.coffeepreorder.database.model.RemoteKeys + +@Dao +interface RemoteKeysDao { + @Query("SELECT * FROM remote_keys WHERE entityId = :entityId AND type = :type") + suspend fun getRemoteKeys(entityId: Int, type: RemoteKeyType): RemoteKeys? + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertAll(remoteKey: List) + + @Query("DELETE FROM remote_keys WHERE type = :type") + suspend fun clearRemoteKeys(type: RemoteKeyType) +} \ No newline at end of file diff --git a/app/src/main/java/com/zyzf/coffeepreorder/database/dao/UserDao.kt b/app/src/main/java/com/zyzf/coffeepreorder/database/dao/UserDao.kt index 2661321..c9d2116 100644 --- a/app/src/main/java/com/zyzf/coffeepreorder/database/dao/UserDao.kt +++ b/app/src/main/java/com/zyzf/coffeepreorder/database/dao/UserDao.kt @@ -1,15 +1,17 @@ package com.zyzf.coffeepreorder.database.dao +import androidx.paging.PagingSource import androidx.room.Dao import androidx.room.Insert import androidx.room.Query +import com.zyzf.coffeepreorder.database.model.Coffee import com.zyzf.coffeepreorder.database.model.User import kotlinx.coroutines.flow.Flow @Dao interface UserDao { @Query("select * from user") - fun getAll(): Flow> + fun getAll(): PagingSource @Query("select * from user where login = :login and password = :password") suspend fun tryLogin(login: String, password: String): User? diff --git a/app/src/main/java/com/zyzf/coffeepreorder/database/model/CoffeeWithCart.kt b/app/src/main/java/com/zyzf/coffeepreorder/database/model/CoffeeWithCart.kt deleted file mode 100644 index 00171f2..0000000 --- a/app/src/main/java/com/zyzf/coffeepreorder/database/model/CoffeeWithCart.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.zyzf.coffeepreorder.database.model - -import androidx.room.ColumnInfo -import androidx.room.Embedded - -data class CoffeeWithCart( - @Embedded - val coffee: Coffee, - @ColumnInfo(name = "cart_uid") - val cartUid: Int -) diff --git a/app/src/main/java/com/zyzf/coffeepreorder/database/model/RemoteKeys.kt b/app/src/main/java/com/zyzf/coffeepreorder/database/model/RemoteKeys.kt new file mode 100644 index 0000000..b4f5865 --- /dev/null +++ b/app/src/main/java/com/zyzf/coffeepreorder/database/model/RemoteKeys.kt @@ -0,0 +1,26 @@ +package com.zyzf.coffeepreorder.database.model + +import androidx.room.Entity +import androidx.room.PrimaryKey +import androidx.room.TypeConverter +import androidx.room.TypeConverters + +enum class RemoteKeyType(private val type: String) { + Coffee(Coffee::class.simpleName ?: "Coffee"), + User(User::class.simpleName ?: "User"); + + @TypeConverter + fun toRemoteKeyType(value: String) = RemoteKeyType.values().first { it.type == value } + + @TypeConverter + fun fromRemoteKeyType(value: RemoteKeyType) = value.type +} + +@Entity(tableName = "remote_keys") +data class RemoteKeys( + @PrimaryKey val entityId: Int, + @TypeConverters(RemoteKeyType::class) + val type: RemoteKeyType, + val prevKey: Int?, + val nextKey: Int? +) diff --git a/app/src/main/java/com/zyzf/coffeepreorder/database/model/User.kt b/app/src/main/java/com/zyzf/coffeepreorder/database/model/User.kt index 5903ce0..ab7268c 100644 --- a/app/src/main/java/com/zyzf/coffeepreorder/database/model/User.kt +++ b/app/src/main/java/com/zyzf/coffeepreorder/database/model/User.kt @@ -8,7 +8,7 @@ import androidx.room.PrimaryKey @Entity(tableName = "user") data class User( @PrimaryKey(autoGenerate = true) - val uid: Int?, + val uid: Int = 0, @ColumnInfo(name = "login") var login: String, @ColumnInfo(name = "fio") @@ -27,7 +27,20 @@ data class User( phone: String, password: String, role: String - ) : this(null, login, fio, phone, password, role) + ) : this(0, login, fio, phone, password, role) + + companion object { + fun getUser(index: Int = 0): User { + return User( + index, + "", + "", + "", + "", + "user" + ) + } + } override fun equals(other: Any?): Boolean { if (this === other) return true diff --git a/app/src/main/java/com/zyzf/coffeepreorder/database/repository/CartRepository.kt b/app/src/main/java/com/zyzf/coffeepreorder/database/repository/CartRepository.kt index 91e8a1c..1575ed6 100644 --- a/app/src/main/java/com/zyzf/coffeepreorder/database/repository/CartRepository.kt +++ b/app/src/main/java/com/zyzf/coffeepreorder/database/repository/CartRepository.kt @@ -1,10 +1,15 @@ package com.zyzf.coffeepreorder.database.repository +import androidx.paging.PagingData import com.zyzf.coffeepreorder.database.model.Cart +import com.zyzf.coffeepreorder.database.model.Coffee +import kotlinx.coroutines.flow.Flow interface CartRepository { suspend fun get(): Cart suspend fun insert(cart: Cart) + fun getAllInCart(): Flow> + fun getSumInCart(): Double suspend fun insertCoffee(cartId: Int, coffeeId: Int, count: Int) suspend fun deleteCoffee(coffeeId: Int, count: Int) suspend fun update(cart: Cart) diff --git a/app/src/main/java/com/zyzf/coffeepreorder/database/repository/CoffeeRepository.kt b/app/src/main/java/com/zyzf/coffeepreorder/database/repository/CoffeeRepository.kt index 82bc299..692aeea 100644 --- a/app/src/main/java/com/zyzf/coffeepreorder/database/repository/CoffeeRepository.kt +++ b/app/src/main/java/com/zyzf/coffeepreorder/database/repository/CoffeeRepository.kt @@ -2,15 +2,12 @@ package com.zyzf.coffeepreorder.database.repository import androidx.paging.PagingData import com.zyzf.coffeepreorder.database.model.Coffee -import com.zyzf.coffeepreorder.database.model.CoffeeWithCart import kotlinx.coroutines.flow.Flow interface CoffeeRepository { fun getAll(): Flow> - fun getAllInCart(): Flow> - fun getSumInCart(): Double - suspend fun getByUid(uid: Int): CoffeeWithCart? - suspend fun insert(name: String, cost: Double, ingredients: String): Long + suspend fun getByUid(uid: Int): Coffee? + suspend fun insert(name: String, cost: Double, ingredients: String): Int suspend fun update(uid: Int, name: String, cost: Double, ingredients: String, cartId: Int?, count: Int): Int? suspend fun delete(coffee: Coffee) } \ No newline at end of file diff --git a/app/src/main/java/com/zyzf/coffeepreorder/database/repository/OfflineCartRepository.kt b/app/src/main/java/com/zyzf/coffeepreorder/database/repository/OfflineCartRepository.kt index 394d81d..67a3185 100644 --- a/app/src/main/java/com/zyzf/coffeepreorder/database/repository/OfflineCartRepository.kt +++ b/app/src/main/java/com/zyzf/coffeepreorder/database/repository/OfflineCartRepository.kt @@ -1,9 +1,23 @@ package com.zyzf.coffeepreorder.database.repository +import androidx.paging.Pager +import androidx.paging.PagingConfig +import androidx.paging.PagingData +import com.zyzf.coffeepreorder.database.AppContainer import com.zyzf.coffeepreorder.database.dao.CartDao import com.zyzf.coffeepreorder.database.model.Cart +import com.zyzf.coffeepreorder.database.model.Coffee +import kotlinx.coroutines.flow.Flow class OfflineCartRepository(private val cartDao: CartDao) : CartRepository { + override fun getAllInCart(): Flow> = Pager( + config = PagingConfig( + pageSize = AppContainer.LIMIT, + enablePlaceholders = false + ), + pagingSourceFactory = cartDao::getAllInCart + ).flow + override fun getSumInCart(): Double = cartDao.getSumInCart() override suspend fun get(): Cart = cartDao.get() override suspend fun insert(cart: Cart) = cartDao.insert(cart) override suspend fun insertCoffee(cartId: Int, coffeeId: Int, count: Int) = cartDao.insertCoffee(cartId, coffeeId, count) diff --git a/app/src/main/java/com/zyzf/coffeepreorder/database/repository/OfflineCoffeeRepository.kt b/app/src/main/java/com/zyzf/coffeepreorder/database/repository/OfflineCoffeeRepository.kt index 93d8dd0..1ff31ce 100644 --- a/app/src/main/java/com/zyzf/coffeepreorder/database/repository/OfflineCoffeeRepository.kt +++ b/app/src/main/java/com/zyzf/coffeepreorder/database/repository/OfflineCoffeeRepository.kt @@ -3,30 +3,25 @@ package com.zyzf.coffeepreorder.database.repository import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.PagingData -import com.zyzf.coffeepreorder.database.AppDataContainer +import androidx.paging.PagingSource +import com.zyzf.coffeepreorder.database.AppContainer import com.zyzf.coffeepreorder.database.dao.CoffeeDao import com.zyzf.coffeepreorder.database.model.Coffee -import com.zyzf.coffeepreorder.database.model.CoffeeWithCart import kotlinx.coroutines.flow.Flow class OfflineCoffeeRepository(private val coffeeDao: CoffeeDao) : CoffeeRepository { override fun getAll(): Flow> = Pager( config = PagingConfig( - pageSize = AppDataContainer.LIMIT, + pageSize = AppContainer.LIMIT, enablePlaceholders = false ), pagingSourceFactory = coffeeDao::getAll ).flow - override fun getAllInCart(): Flow> = Pager( - config = PagingConfig( - pageSize = AppDataContainer.LIMIT, - enablePlaceholders = false - ), - pagingSourceFactory = coffeeDao::getAllInCart - ).flow - override fun getSumInCart(): Double = coffeeDao.getSumInCart() - override suspend fun getByUid(uid: Int): CoffeeWithCart? = coffeeDao.getByUid(uid) - override suspend fun insert(name: String, cost: Double, ingredients: String): Long = coffeeDao.insert(name, cost, ingredients) + + fun getAllCoffeesPagingSource(): PagingSource = coffeeDao.getAll() + suspend fun clearCoffees() = coffeeDao.deleteAll() + override suspend fun getByUid(uid: Int): Coffee? = coffeeDao.getByUid(uid) + override suspend fun insert(name: String, cost: Double, ingredients: String): Int = coffeeDao.insert(name, cost, ingredients) override suspend fun update(uid: Int, name: String, cost: Double, ingredients: String, cartId: Int?, count: Int): Int? = coffeeDao.update(uid, name, cost, ingredients, cartId, count) override suspend fun delete(coffee: Coffee) = coffeeDao.delete(coffee) } \ No newline at end of file diff --git a/app/src/main/java/com/zyzf/coffeepreorder/database/repository/OfflineRemoteKeyRepository.kt b/app/src/main/java/com/zyzf/coffeepreorder/database/repository/OfflineRemoteKeyRepository.kt new file mode 100644 index 0000000..71bcc59 --- /dev/null +++ b/app/src/main/java/com/zyzf/coffeepreorder/database/repository/OfflineRemoteKeyRepository.kt @@ -0,0 +1,16 @@ +package com.zyzf.coffeepreorder.database.repository + +import com.zyzf.coffeepreorder.database.dao.RemoteKeysDao +import com.zyzf.coffeepreorder.database.model.RemoteKeyType +import com.zyzf.coffeepreorder.database.model.RemoteKeys + +class OfflineRemoteKeyRepository(private val remoteKeysDao: RemoteKeysDao) : RemoteKeyRepository { + override suspend fun getAllRemoteKeys(id: Int, type: RemoteKeyType) = + remoteKeysDao.getRemoteKeys(id, type) + + override suspend fun createRemoteKeys(remoteKeys: List) = + remoteKeysDao.insertAll(remoteKeys) + + override suspend fun deleteRemoteKey(type: RemoteKeyType) = + remoteKeysDao.clearRemoteKeys(type) +} \ No newline at end of file diff --git a/app/src/main/java/com/zyzf/coffeepreorder/database/repository/OfflineUserRepository.kt b/app/src/main/java/com/zyzf/coffeepreorder/database/repository/OfflineUserRepository.kt index 9031369..3b53f68 100644 --- a/app/src/main/java/com/zyzf/coffeepreorder/database/repository/OfflineUserRepository.kt +++ b/app/src/main/java/com/zyzf/coffeepreorder/database/repository/OfflineUserRepository.kt @@ -1,11 +1,24 @@ package com.zyzf.coffeepreorder.database.repository +import androidx.paging.Pager +import androidx.paging.PagingConfig +import androidx.paging.PagingData +import androidx.paging.PagingSource +import com.zyzf.coffeepreorder.database.AppContainer import com.zyzf.coffeepreorder.database.dao.UserDao +import com.zyzf.coffeepreorder.database.model.Coffee import com.zyzf.coffeepreorder.database.model.User import kotlinx.coroutines.flow.Flow class OfflineUserRepository(private val userDao: UserDao) : UserRepository { - override fun getAll(): Flow> = userDao.getAll() + override fun getAll(): Flow> = Pager( + config = PagingConfig( + pageSize = AppContainer.LIMIT, + enablePlaceholders = false + ), + pagingSourceFactory = userDao::getAll + ).flow + fun getAllUserPagingSource(): PagingSource = userDao.getAll() override suspend fun tryLogin(login: String, password: String): User? = userDao.tryLogin(login, password) override suspend fun getByUid(uid: Int): User? = userDao.getByUid(uid) override suspend fun getLogined(): User? = userDao.getLogined() diff --git a/app/src/main/java/com/zyzf/coffeepreorder/database/repository/RemoteKeyRepository.kt b/app/src/main/java/com/zyzf/coffeepreorder/database/repository/RemoteKeyRepository.kt new file mode 100644 index 0000000..3acbb59 --- /dev/null +++ b/app/src/main/java/com/zyzf/coffeepreorder/database/repository/RemoteKeyRepository.kt @@ -0,0 +1,10 @@ +package com.zyzf.coffeepreorder.database.repository + +import com.zyzf.coffeepreorder.database.model.RemoteKeyType +import com.zyzf.coffeepreorder.database.model.RemoteKeys + +interface RemoteKeyRepository { + suspend fun getAllRemoteKeys(id: Int, type: RemoteKeyType): RemoteKeys? + suspend fun createRemoteKeys(remoteKeys: List) + suspend fun deleteRemoteKey(type: RemoteKeyType) +} \ No newline at end of file diff --git a/app/src/main/java/com/zyzf/coffeepreorder/database/repository/UserRepository.kt b/app/src/main/java/com/zyzf/coffeepreorder/database/repository/UserRepository.kt index cfe8eec..edef88f 100644 --- a/app/src/main/java/com/zyzf/coffeepreorder/database/repository/UserRepository.kt +++ b/app/src/main/java/com/zyzf/coffeepreorder/database/repository/UserRepository.kt @@ -1,10 +1,11 @@ package com.zyzf.coffeepreorder.database.repository +import androidx.paging.PagingData import com.zyzf.coffeepreorder.database.model.User import kotlinx.coroutines.flow.Flow interface UserRepository { - fun getAll(): Flow> + fun getAll(): Flow> suspend fun tryLogin(login: String, password: String): User? suspend fun getByUid(uid: Int): User? suspend fun getLogined(): User? diff --git a/app/src/main/java/com/zyzf/coffeepreorder/ui/cart/CartViewModel.kt b/app/src/main/java/com/zyzf/coffeepreorder/ui/cart/CartViewModel.kt index c591988..88b6b4c 100644 --- a/app/src/main/java/com/zyzf/coffeepreorder/ui/cart/CartViewModel.kt +++ b/app/src/main/java/com/zyzf/coffeepreorder/ui/cart/CartViewModel.kt @@ -8,10 +8,9 @@ import com.zyzf.coffeepreorder.database.repository.CoffeeRepository import kotlinx.coroutines.flow.Flow class CartViewModel( - private val coffeeRepository: CoffeeRepository, private val cartRepository: CartRepository ) : ViewModel() { - val coffeeListUiState: Flow> = coffeeRepository.getAllInCart() + val coffeeListUiState: Flow> = cartRepository.getAllInCart() suspend fun deleteCoffeeFromCart(coffee: Coffee) { cartRepository.deleteCoffee(coffee.uid, 1) diff --git a/app/src/main/java/com/zyzf/coffeepreorder/ui/coffee/CoffeeListViewModel.kt b/app/src/main/java/com/zyzf/coffeepreorder/ui/coffee/CoffeeListViewModel.kt index b23dc3e..70986b7 100644 --- a/app/src/main/java/com/zyzf/coffeepreorder/ui/coffee/CoffeeListViewModel.kt +++ b/app/src/main/java/com/zyzf/coffeepreorder/ui/coffee/CoffeeListViewModel.kt @@ -36,7 +36,7 @@ class CoffeeListViewModel( cart.uid?.let { cartRepository.insertCoffee(it, coffeeUid, 1) } } suspend fun createCoffee(coffee: Coffee, imageUri: Uri, context: Context) { - val newCoffee: Long = coffeeRepository.insert(coffee.name, coffee.cost, coffee.ingredients) + val newCoffee: Int = coffeeRepository.insert(coffee.name, coffee.cost, coffee.ingredients) val inputStream = context.contentResolver.openInputStream(imageUri) val bitmap = BitmapFactory.decodeStream(inputStream)