From 0c949919d870986639de67830029fdd7d254dbc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=BE=D0=BB=D0=BE=D0=B4=D1=8F?= Date: Wed, 20 Dec 2023 19:45:20 +0300 Subject: [PATCH] lab 5 work --- app/build.gradle.kts | 11 ++ app/src/main/AndroidManifest.xml | 6 + app/src/main/java/com/example/pizza/Auth.kt | 7 +- .../example/pizza/Model/Basket/BasketDao.kt | 4 + .../com/example/pizza/Model/Dto/BasketDto.kt | 22 +++ .../com/example/pizza/Model/Dto/OrderDto.kt | 22 +++ .../com/example/pizza/Model/Dto/PizzaDto.kt | 45 ++++++ .../com/example/pizza/Model/Dto/UserDto.kt | 31 ++++ .../com/example/pizza/Model/Order/OrderDao.kt | 4 + .../com/example/pizza/Model/Pizza/PizzaDao.kt | 8 +- .../com/example/pizza/Model/PizzaAdapter.kt | 2 +- .../Repository/OfflineBasketRepository.kt | 2 + .../Repository/OfflineOrderRepository.kt | 2 + .../Repository/OfflinePizzaRepository.kt | 3 + .../Model/Repository/OfflineUserRepository.kt | 2 +- .../pizza/Model/Repository/PizzaRepository.kt | 2 + .../Rest/Mediators/OrderRemoteMediator.kt | 150 ++++++++++++++++++ .../Rest/Mediators/PizzaRemoteMediator.kt | 140 ++++++++++++++++ .../Repository/Rest/RestBasketRepository.kt | 80 ++++++++++ .../Repository/Rest/RestOrderRepository.kt | 54 +++++++ .../Repository/Rest/RestPizzaRepository.kt | 62 ++++++++ .../Repository/Rest/RestUserRepository.kt | 41 +++++ .../pizza/Model/Repository/UserRepository.kt | 2 - .../pizza/Model/Rest/MyServerService.kt | 149 +++++++++++++++++ .../RemoteKeys/OfflineRemoteKeyRepository.kt | 15 ++ .../pizza/RemoteKeys/RemoteKeyRepository.kt | 10 ++ .../example/pizza/RemoteKeys/RemoteKeys.kt | 32 ++++ .../example/pizza/RemoteKeys/RemoteKeysDao.kt | 20 +++ .../pizza/ViewModels/AppViewModelProvider.kt | 7 +- .../pizza/ViewModels/OrderListViewModel.kt | 48 +++--- .../pizza/ViewModels/PizzaEditViewModel.kt | 4 +- .../pizza/ViewModels/PizzaListViewModel.kt | 29 ++-- .../example/pizza/ViewModels/UserViewModel.kt | 1 + .../java/com/example/pizza/create_pizza.kt | 7 +- .../example/pizza/database/AppContainer.kt | 40 ++++- .../com/example/pizza/database/AppDatabase.kt | 16 +- .../main/res/xml/network_security_config.xml | 4 + 37 files changed, 1023 insertions(+), 61 deletions(-) create mode 100644 app/src/main/java/com/example/pizza/Model/Dto/BasketDto.kt create mode 100644 app/src/main/java/com/example/pizza/Model/Dto/OrderDto.kt create mode 100644 app/src/main/java/com/example/pizza/Model/Dto/PizzaDto.kt create mode 100644 app/src/main/java/com/example/pizza/Model/Dto/UserDto.kt create mode 100644 app/src/main/java/com/example/pizza/Model/Repository/Rest/Mediators/OrderRemoteMediator.kt create mode 100644 app/src/main/java/com/example/pizza/Model/Repository/Rest/Mediators/PizzaRemoteMediator.kt create mode 100644 app/src/main/java/com/example/pizza/Model/Repository/Rest/RestBasketRepository.kt create mode 100644 app/src/main/java/com/example/pizza/Model/Repository/Rest/RestOrderRepository.kt create mode 100644 app/src/main/java/com/example/pizza/Model/Repository/Rest/RestPizzaRepository.kt create mode 100644 app/src/main/java/com/example/pizza/Model/Repository/Rest/RestUserRepository.kt create mode 100644 app/src/main/java/com/example/pizza/Model/Rest/MyServerService.kt create mode 100644 app/src/main/java/com/example/pizza/RemoteKeys/OfflineRemoteKeyRepository.kt create mode 100644 app/src/main/java/com/example/pizza/RemoteKeys/RemoteKeyRepository.kt create mode 100644 app/src/main/java/com/example/pizza/RemoteKeys/RemoteKeys.kt create mode 100644 app/src/main/java/com/example/pizza/RemoteKeys/RemoteKeysDao.kt create mode 100644 app/src/main/res/xml/network_security_config.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 6937e62..c5354db 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -4,6 +4,7 @@ plugins { id("org.jetbrains.kotlin.android") id("androidx.navigation.safeargs.kotlin") id("kotlin-kapt") + kotlin("plugin.serialization") version "1.4.21" } android { @@ -75,6 +76,16 @@ dependencies { // optional - RxJava3 support implementation("androidx.paging:paging-rxjava3:$paging_version") + //retrofit + val retrofitVersion = "2.9.0" + implementation("com.squareup.retrofit2:retrofit:$retrofitVersion") + implementation("com.squareup.retrofit2:adapter-rxjava3:$retrofitVersion") + implementation("com.squareup.retrofit2:converter-moshi:$retrofitVersion") + implementation("com.squareup.okhttp3:logging-interceptor:4.11.0") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1") + implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0") + implementation("com.squareup.retrofit2:converter-gson:2.9.0") + // Test testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.5") diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2c373bc..fd95bb6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,7 +1,11 @@ + + + + > @Insert fun insert(group: Basket): Completable + @Insert + fun insertAll(group: List): Completable @Update fun update(group: Basket): Completable @@ -26,6 +28,8 @@ interface BasketDao { fun delete(group: Basket): Completable @Query("delete from basket where basket.user_id = :uid") fun deleteByUser(uid : Int): Completable + @Query("delete from basket ") + fun deleteAll(): Completable @Query("delete from basket where basket.user_id = :uid and basket.pizza_id = :pid") fun deleteByUserAndPizza(uid : Int, pid:Int): Completable } \ No newline at end of file diff --git a/app/src/main/java/com/example/pizza/Model/Dto/BasketDto.kt b/app/src/main/java/com/example/pizza/Model/Dto/BasketDto.kt new file mode 100644 index 0000000..3e86a5f --- /dev/null +++ b/app/src/main/java/com/example/pizza/Model/Dto/BasketDto.kt @@ -0,0 +1,22 @@ +package com.example.pizza.Model.Dto + +import com.example.pizza.Model.Basket.Basket +import com.example.pizza.Model.Pizza.Pizza +import com.google.gson.annotations.SerializedName +import kotlinx.serialization.Serializable + +@Serializable +data class BasketDto( + @SerializedName("user")val user: Int, + @SerializedName("pizza")val pizza: Int, + @SerializedName("count")val countPizza: Int = 1 +){ +} + +fun BasketDto.toBasket(): Basket = Basket( + user, pizza, countPizza) + + +fun Basket.toBasketDto(): BasketDto = BasketDto( + user, pizza, countPizza +) diff --git a/app/src/main/java/com/example/pizza/Model/Dto/OrderDto.kt b/app/src/main/java/com/example/pizza/Model/Dto/OrderDto.kt new file mode 100644 index 0000000..7c030e0 --- /dev/null +++ b/app/src/main/java/com/example/pizza/Model/Dto/OrderDto.kt @@ -0,0 +1,22 @@ +package com.example.pizza.Model.Dto + + +import com.example.pizza.Model.Order.Order +import com.example.pizza.Model.Pizza.Pizza +import kotlinx.serialization.Serializable +import java.util.Date +@Serializable +data class OrderDto( + val uid : Int? = 0, + val date: Long = 0, + val pizza: Int? = 0, + val user: Int? = 0, + val price : Int = 0 +) +fun OrderDto.toOrder(): Order = Order( + uid, Date(date), pizza, user, price) + + +fun Order.toOrderDto(): OrderDto = OrderDto( + uid, date.time, pizza, user, price +) diff --git a/app/src/main/java/com/example/pizza/Model/Dto/PizzaDto.kt b/app/src/main/java/com/example/pizza/Model/Dto/PizzaDto.kt new file mode 100644 index 0000000..0b0024d --- /dev/null +++ b/app/src/main/java/com/example/pizza/Model/Dto/PizzaDto.kt @@ -0,0 +1,45 @@ +package com.example.pizza.Model.Dto + +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.os.Build +import androidx.annotation.RequiresApi +import com.example.pizza.Model.Pizza.Pizza +import com.example.pizza.Model.User.User +import kotlinx.serialization.Serializable +import java.io.ByteArrayOutputStream +import java.util.Base64 + +@Serializable +data class PizzaDto( + val id : Int?, + val title : String, + val price : Int, + val image : String, + val ingredients : String + +){ + constructor( + title : String, + price : Int, + image : String, + ingredients : String + + ) : this(null, title,price, image, ingredients) + +} +@RequiresApi(Build.VERSION_CODES.O) +fun PizzaDto.toPizza(): Pizza = Pizza( + id, + title, BitmapFactory.decodeByteArray(Base64.getDecoder().decode(image), 0, Base64.getDecoder().decode(image).size),ingredients,price) + + +@RequiresApi(Build.VERSION_CODES.O) +fun Pizza.toPizzaDto(): PizzaDto { + val outputStream = ByteArrayOutputStream() + image?.compress(Bitmap.CompressFormat.PNG, 100, outputStream) + val encoded = Base64.getEncoder().encodeToString(outputStream.toByteArray()) + return PizzaDto(uid, title,price, encoded, ingredients, ) +} + + diff --git a/app/src/main/java/com/example/pizza/Model/Dto/UserDto.kt b/app/src/main/java/com/example/pizza/Model/Dto/UserDto.kt new file mode 100644 index 0000000..d56e284 --- /dev/null +++ b/app/src/main/java/com/example/pizza/Model/Dto/UserDto.kt @@ -0,0 +1,31 @@ +package com.example.pizza.Model.Dto + +import com.example.pizza.Model.User.User +import com.google.gson.annotations.SerializedName +import kotlinx.serialization.Serializable + +@Serializable +data class UserDto( + @SerializedName("id")val id : Int?, + @SerializedName("login")val login : String?, + @SerializedName("email")val email : String?, + @SerializedName("password")val password : String?, + @SerializedName("role")val role : String? +){ + constructor( + login: String, + email : String, + password: String, + role : String + ) : this(null, login,email,password,role) + +} +fun UserDto.toUser(): User = User( + id, + login?:"", email?:"", password?:"", role?:"") + + +fun User.toUserDto(): UserDto = UserDto( + uid, + login, email, password, role +) \ No newline at end of file diff --git a/app/src/main/java/com/example/pizza/Model/Order/OrderDao.kt b/app/src/main/java/com/example/pizza/Model/Order/OrderDao.kt index 6de525a..f795922 100644 --- a/app/src/main/java/com/example/pizza/Model/Order/OrderDao.kt +++ b/app/src/main/java/com/example/pizza/Model/Order/OrderDao.kt @@ -18,6 +18,10 @@ interface OrderDao { @Insert fun insert(group: Order): Completable + @Delete + fun delete(group: Order): Completable + @Query("DELETE FROM pizzas") + fun deleteAll(): Completable @Insert fun insertMany(group: List): Completable diff --git a/app/src/main/java/com/example/pizza/Model/Pizza/PizzaDao.kt b/app/src/main/java/com/example/pizza/Model/Pizza/PizzaDao.kt index fa2ba39..f813ce3 100644 --- a/app/src/main/java/com/example/pizza/Model/Pizza/PizzaDao.kt +++ b/app/src/main/java/com/example/pizza/Model/Pizza/PizzaDao.kt @@ -16,14 +16,20 @@ import io.reactivex.rxjava3.core.Single interface PizzaDao { @Query("select * from pizzas") fun getAll(): RxPagingSource + @Query("select * from pizzas where pizzas.uid = :uid") fun getById(uid : Int): Single @Insert(onConflict = OnConflictStrategy.REPLACE) fun insert(group: Pizza): Completable - + @Insert + fun insertAll(group: List): Completable @Update fun update(group: Pizza): Completable @Delete fun delete(group: Pizza): Completable + + @Query("DELETE FROM pizzas") + fun deleteAll(): Completable + } \ No newline at end of file diff --git a/app/src/main/java/com/example/pizza/Model/PizzaAdapter.kt b/app/src/main/java/com/example/pizza/Model/PizzaAdapter.kt index 50622bf..736c3e0 100644 --- a/app/src/main/java/com/example/pizza/Model/PizzaAdapter.kt +++ b/app/src/main/java/com/example/pizza/Model/PizzaAdapter.kt @@ -63,7 +63,7 @@ class PizzaAdapter(var context: Context, holder.price.text = currentPizza.price.toString() holder.image.setImageBitmap(currentPizza.image) holder.btn.setOnClickListener { addToBasket(holder, position) } - holder.image.setOnClickListener{ onClick(position+1) } + holder.image.setOnClickListener{ onClick(pizza.uid!!) } } } fun addToBasket(holder: PizzaAdapter.MyViewHolder, position: Int){ diff --git a/app/src/main/java/com/example/pizza/Model/Repository/OfflineBasketRepository.kt b/app/src/main/java/com/example/pizza/Model/Repository/OfflineBasketRepository.kt index 771ea65..008c4c4 100644 --- a/app/src/main/java/com/example/pizza/Model/Repository/OfflineBasketRepository.kt +++ b/app/src/main/java/com/example/pizza/Model/Repository/OfflineBasketRepository.kt @@ -13,6 +13,7 @@ class OfflineBasketRepository(private val basketDao : BasketDao) : BasketReposit override fun getUserAllBasket(uid: Int): Flowable> = basketDao.getUserAllBasket(uid) override fun insert(group: Basket): Completable = basketDao.insert(group) + fun insertAll(group: List): Completable = basketDao.insertAll(group) override fun update(group: Basket): Completable = basketDao.update(group) @@ -20,5 +21,6 @@ class OfflineBasketRepository(private val basketDao : BasketDao) : BasketReposit override fun deleteByUser(uid: Int): Completable = basketDao.deleteByUser(uid) + fun deleteAll(): Completable = basketDao.deleteAll() override fun deleteByUserAndPizza(uid: Int, pid: Int): Completable = basketDao.deleteByUserAndPizza(uid,pid) } \ No newline at end of file diff --git a/app/src/main/java/com/example/pizza/Model/Repository/OfflineOrderRepository.kt b/app/src/main/java/com/example/pizza/Model/Repository/OfflineOrderRepository.kt index 6f223a4..b8ef04b 100644 --- a/app/src/main/java/com/example/pizza/Model/Repository/OfflineOrderRepository.kt +++ b/app/src/main/java/com/example/pizza/Model/Repository/OfflineOrderRepository.kt @@ -13,4 +13,6 @@ class OfflineOrderRepository(private val orderDao: OrderDao) : OrderRepository { override fun insert(group: Order): Completable = orderDao.insert(group) override fun insertMany(group: List): Completable = orderDao.insertMany(group) + fun delete(group: Order): Completable = orderDao.delete(group) + fun deleteAll(): Completable = orderDao.deleteAll() } \ No newline at end of file diff --git a/app/src/main/java/com/example/pizza/Model/Repository/OfflinePizzaRepository.kt b/app/src/main/java/com/example/pizza/Model/Repository/OfflinePizzaRepository.kt index 547dece..7eda387 100644 --- a/app/src/main/java/com/example/pizza/Model/Repository/OfflinePizzaRepository.kt +++ b/app/src/main/java/com/example/pizza/Model/Repository/OfflinePizzaRepository.kt @@ -15,4 +15,7 @@ class OfflinePizzaRepository(private val pizzaDao : PizzaDao) : PizzaRepository override fun update(group: Pizza): Completable = pizzaDao.update(group) override fun delete(group: Pizza): Completable = pizzaDao.delete(group) + override fun deleteAll(): Completable = pizzaDao.deleteAll() + + override fun insertAll(pizzas: List): Completable = pizzaDao.insertAll(pizzas) } \ No newline at end of file diff --git a/app/src/main/java/com/example/pizza/Model/Repository/OfflineUserRepository.kt b/app/src/main/java/com/example/pizza/Model/Repository/OfflineUserRepository.kt index 69165a2..3e2cfdd 100644 --- a/app/src/main/java/com/example/pizza/Model/Repository/OfflineUserRepository.kt +++ b/app/src/main/java/com/example/pizza/Model/Repository/OfflineUserRepository.kt @@ -11,7 +11,7 @@ import io.reactivex.rxjava3.core.Single class OfflineUserRepository(private val userDao : UserDao) : UserRepository { override fun getById(uid: Int): Single = userDao.getById(uid) - override fun getAll(): Flowable> = userDao.getAll() + fun getAll(): Flowable> = throw Exception() override fun getByLoginAndPassword(login: String, pass: String): Single = userDao.getByLoginAndPassword(login,pass) diff --git a/app/src/main/java/com/example/pizza/Model/Repository/PizzaRepository.kt b/app/src/main/java/com/example/pizza/Model/Repository/PizzaRepository.kt index 29a7615..c11f153 100644 --- a/app/src/main/java/com/example/pizza/Model/Repository/PizzaRepository.kt +++ b/app/src/main/java/com/example/pizza/Model/Repository/PizzaRepository.kt @@ -11,4 +11,6 @@ interface PizzaRepository { fun insert(group: Pizza): Completable fun update(group: Pizza): Completable fun delete(group: Pizza): Completable + fun deleteAll() : Completable + fun insertAll(pizzas: List) : Completable } \ No newline at end of file diff --git a/app/src/main/java/com/example/pizza/Model/Repository/Rest/Mediators/OrderRemoteMediator.kt b/app/src/main/java/com/example/pizza/Model/Repository/Rest/Mediators/OrderRemoteMediator.kt new file mode 100644 index 0000000..98aa94e --- /dev/null +++ b/app/src/main/java/com/example/pizza/Model/Repository/Rest/Mediators/OrderRemoteMediator.kt @@ -0,0 +1,150 @@ +package com.example.pizza.Model.Repository.Rest.Mediators + +import android.annotation.SuppressLint +import android.os.Build +import android.util.Log +import androidx.annotation.RequiresApi +import androidx.lifecycle.LiveData +import androidx.paging.ExperimentalPagingApi +import androidx.paging.LoadType +import androidx.paging.PagingState +import androidx.paging.RemoteMediator +import androidx.paging.rxjava3.RxRemoteMediator +import com.example.pizza.Model.Dto.toOrder +import com.example.pizza.Model.Dto.toPizza +import com.example.pizza.Model.Order.Order +import com.example.pizza.Model.Pizza.Pizza +import com.example.pizza.Model.Repository.OfflineOrderRepository +import com.example.pizza.Model.Repository.OfflinePizzaRepository +import com.example.pizza.Model.Rest.MyServerService +import com.example.pizza.RemoteKeys.OfflineRemoteKeyRepository +import com.example.pizza.RemoteKeys.RemoteKeyType +import com.example.pizza.RemoteKeys.RemoteKeys +import com.example.pizza.database.AppDatabase +import io.reactivex.rxjava3.core.Maybe +import io.reactivex.rxjava3.core.Observable +import io.reactivex.rxjava3.core.Single +import io.reactivex.rxjava3.schedulers.Schedulers +import java.io.InvalidObjectException +import java.util.concurrent.atomic.AtomicInteger + +@OptIn(ExperimentalPagingApi::class) +class OrderRemoteMediator( + private val service: MyServerService, + private val dbOrderRepository: OfflineOrderRepository, + private val dbPizzaRepository: OfflinePizzaRepository, + private val dbRemoteKeyRepository: OfflineRemoteKeyRepository, + private val database: AppDatabase, + private val id : Int +): RxRemoteMediator() { + @RequiresApi(Build.VERSION_CODES.O) + override fun loadSingle( + loadType: LoadType, + state: PagingState + ): Single { + Log.d(this::class.simpleName, state.toString()) + + return when (loadType){ + LoadType.REFRESH -> getRemoteKeyClosestToCurrentPosition(state).subscribeOn(Schedulers.io()).toSingle().map{ + Log.d("StudentRemoteMediator", "Refresh" + it.toString()) + val page = AtomicInteger(it?.nextKey?.minus(1)?:1) + getMediatorResout(loadType,page,state).blockingGet() + }.onErrorReturn { + data -> Log.d(this::class.simpleName, data.message?:"not message this") + getMediatorResout(loadType, AtomicInteger(1),state).blockingGet() } + LoadType.PREPEND -> + getRemoteKeyForFirstItem(state).subscribeOn(Schedulers.io()).toSingle().map { + Log.d("StudentRemoteMediator", "PREPEND" + it.toString()) + if(it?.prevKey != null) { + getMediatorResout(loadType, AtomicInteger(it.prevKey),state).blockingGet() + } + else { + MediatorResult.Success(endOfPaginationReached = it != null) + } + }.onErrorReturn{ + data ->Log.d("StudentRemoteMediator", data.message?:"not message this") + MediatorResult.Success(false)} + + LoadType.APPEND -> + getRemoteKeyForLastItem(state).subscribeOn(Schedulers.io()).toSingle().map{ + Log.d("StudentRemoteMediator", "APPEND" + it.toString()) + Log.d(this::class.simpleName, "trying to load more") + if(it?.nextKey != null) { + getMediatorResout(loadType,AtomicInteger(it.nextKey),state).blockingGet() + } + else { + MediatorResult.Success(endOfPaginationReached = it != null) + } + } + .onErrorReturn{ + data -> + Log.d(this::class.simpleName, data.message?:"not message this") + MediatorResult.Success(false)} + } + + } + + @SuppressLint("CheckResult") + @RequiresApi(Build.VERSION_CODES.O) + fun getMediatorResout(loadType: LoadType, page : AtomicInteger, state: PagingState) + : Single { + Log.d(PizzaRemoteMediator::class.simpleName,"Load") + service.getAllPizzas() + .subscribeOn(Schedulers.io()) + .map{ + database.runInTransaction { + dbPizzaRepository.deleteAll().blockingAwait() + } + dbPizzaRepository.insertAll(it.map { it.toPizza() }).blockingAwait() + } + return service.getUserOrders(id,state.config.pageSize,(page.get()-1)*state.config.pageSize) + .subscribeOn(Schedulers.io()) + .map{ + database.runInTransaction { + if(loadType == LoadType.REFRESH) { + dbOrderRepository.deleteAll().blockingAwait() + dbRemoteKeyRepository.deleteRemoteKey(RemoteKeyType.ORDER).blockingAwait() + } + dbOrderRepository.insertMany(it.map { it.toOrder() }).blockingAwait() + val prevKey = if (page.get() == 1) null else page.get() - 1 + val nextKey = if (it.isEmpty()) null else page.get() + 1 + val keys = it.map { + RemoteKeys( + entityId = it.uid!!, + type = RemoteKeyType.ORDER, + prevKey = prevKey, + nextKey = nextKey + ) + } + dbRemoteKeyRepository.createRemoteKeys(keys).blockingAwait() + } + MediatorResult.Success(it.isEmpty()) as MediatorResult + }.singleOrError() + .onErrorResumeNext { + e-> + Single.just(MediatorResult.Error(e) as MediatorResult) + } + } + private fun getRemoteKeyForLastItem(state: PagingState): Maybe { + return state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull() + ?.let { group -> + dbRemoteKeyRepository.getAllRemoteKeys(group.uid!!, RemoteKeyType.ORDER) + } ?: Maybe.empty() + } + + private fun getRemoteKeyForFirstItem(state: PagingState): Maybe { + return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull() + ?.let { group -> + dbRemoteKeyRepository.getAllRemoteKeys(group.uid!!, RemoteKeyType.ORDER) + }?: Maybe.empty() + } + + private fun getRemoteKeyClosestToCurrentPosition( state: PagingState): Maybe { + return state.anchorPosition?.let { position -> + state.closestItemToPosition(position)?.uid?.let { studentUid -> + dbRemoteKeyRepository.getAllRemoteKeys(studentUid, RemoteKeyType.ORDER) + } + }?: Maybe.empty() + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/pizza/Model/Repository/Rest/Mediators/PizzaRemoteMediator.kt b/app/src/main/java/com/example/pizza/Model/Repository/Rest/Mediators/PizzaRemoteMediator.kt new file mode 100644 index 0000000..1fbb0d2 --- /dev/null +++ b/app/src/main/java/com/example/pizza/Model/Repository/Rest/Mediators/PizzaRemoteMediator.kt @@ -0,0 +1,140 @@ +package com.example.pizza.Model.Repository.Rest.Mediators + +import android.annotation.SuppressLint +import android.os.Build +import android.util.Log +import androidx.annotation.RequiresApi +import androidx.paging.ExperimentalPagingApi +import androidx.paging.LoadType +import androidx.paging.PagingState +import androidx.paging.RemoteMediator +import androidx.paging.rxjava3.RxRemoteMediator +import com.example.pizza.Model.Dto.PizzaDto +import com.example.pizza.Model.Dto.toPizza +import com.example.pizza.Model.Pizza.Pizza +import com.example.pizza.Model.Repository.OfflinePizzaRepository +import com.example.pizza.Model.Repository.OfflineUserRepository +import com.example.pizza.Model.Rest.MyServerService +import com.example.pizza.Model.User.User +import com.example.pizza.RemoteKeys.OfflineRemoteKeyRepository +import com.example.pizza.RemoteKeys.RemoteKeyType +import com.example.pizza.RemoteKeys.RemoteKeys +import com.example.pizza.database.AppDatabase +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers +import io.reactivex.rxjava3.core.Maybe +import io.reactivex.rxjava3.core.Observable +import io.reactivex.rxjava3.core.Single +import io.reactivex.rxjava3.schedulers.Schedulers +import java.io.InvalidObjectException +import java.util.concurrent.atomic.AtomicInteger + +@OptIn(ExperimentalPagingApi::class) +class PizzaRemoteMediator( + private val service: MyServerService, + private val dbPizzaRepository: OfflinePizzaRepository, + private val dbRemoteKeyRepository: OfflineRemoteKeyRepository, + private val database: AppDatabase +) : RxRemoteMediator(){ + @RequiresApi(Build.VERSION_CODES.O) + override fun loadSingle( + loadType: LoadType, + state: PagingState + ): Single { + Log.d(this::class.simpleName, state.toString()) + + return when (loadType){ + LoadType.REFRESH -> getRemoteKeyClosestToCurrentPosition(state).subscribeOn(Schedulers.io()).toSingle().map{ + Log.d("StudentRemoteMediator", "Refresh" + it.toString()) + val page = AtomicInteger(it?.nextKey?.minus(1)?:1) + getMediatorResout(loadType,page,state).blockingGet() + }.onErrorReturn { + data -> Log.d(this::class.simpleName, data.message?:"not message this") + getMediatorResout(loadType, AtomicInteger(1),state).blockingGet() } + LoadType.PREPEND -> + getRemoteKeyForFirstItem(state).subscribeOn(Schedulers.io()).toSingle().map { + Log.d("StudentRemoteMediator", "PREPEND" + it.toString()) + if(it?.prevKey != null) { + getMediatorResout(loadType, AtomicInteger(it.prevKey),state).blockingGet() + } + else { + MediatorResult.Success(endOfPaginationReached = it != null) + } + }.onErrorReturn{ + data ->Log.d("StudentRemoteMediator", data.message?:"not message this") + MediatorResult.Success(false)} + + LoadType.APPEND -> + getRemoteKeyForLastItem(state).subscribeOn(Schedulers.io()).toSingle().map{ + Log.d("StudentRemoteMediator", "APPEND" + it.toString()) + Log.d(this::class.simpleName, "trying to load more") + if(it?.nextKey != null) { + getMediatorResout(loadType,AtomicInteger(it.nextKey),state).blockingGet() + } + else { + MediatorResult.Success(endOfPaginationReached = it != null) + } + } + .onErrorReturn{ + data -> + Log.d(this::class.simpleName, data.message?:"not message this") + MediatorResult.Success(false)} + } + + } + + @SuppressLint("CheckResult") + @RequiresApi(Build.VERSION_CODES.O) + fun getMediatorResout(loadType: LoadType, page : AtomicInteger, state: PagingState) + : Single { + Log.d(PizzaRemoteMediator::class.simpleName,"Load") + return service.getPizzas(state.config.pageSize,(page.get()-1)*state.config.pageSize) + .subscribeOn(Schedulers.io()) + .map{ + database.runInTransaction { + if(loadType == LoadType.REFRESH) { + dbPizzaRepository.deleteAll().blockingAwait() + dbRemoteKeyRepository.deleteRemoteKey(RemoteKeyType.PIZZA).blockingAwait() + } + dbPizzaRepository.insertAll(it.map { it.toPizza() }).blockingAwait() + val prevKey = if (page.get() == 1) null else page.get() - 1 + val nextKey = if (it.isEmpty()) null else page.get() + 1 + val keys = it.map { + RemoteKeys( + entityId = it.id!!, + type = RemoteKeyType.PIZZA, + prevKey = prevKey, + nextKey = nextKey + ) + } + dbRemoteKeyRepository.createRemoteKeys(keys).blockingAwait() + } + MediatorResult.Success(it.isEmpty()) as MediatorResult + }.singleOrError() + .onErrorResumeNext { + e-> + Single.just(MediatorResult.Error(e) as MediatorResult) + } + } + private fun getRemoteKeyForLastItem(state: PagingState): Maybe { + return state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull() + ?.let { group -> + dbRemoteKeyRepository.getAllRemoteKeys(group.uid!!, RemoteKeyType.PIZZA) + } ?: Maybe.empty() + } + + private fun getRemoteKeyForFirstItem(state: PagingState): Maybe { + return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull() + ?.let { group -> + dbRemoteKeyRepository.getAllRemoteKeys(group.uid!!, RemoteKeyType.PIZZA) + }?: Maybe.empty() + } + + private fun getRemoteKeyClosestToCurrentPosition( state: PagingState): Maybe { + return state.anchorPosition?.let { position -> + state.closestItemToPosition(position)?.uid?.let { studentUid -> + dbRemoteKeyRepository.getAllRemoteKeys(studentUid, RemoteKeyType.PIZZA) + } + }?: Maybe.empty() + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/pizza/Model/Repository/Rest/RestBasketRepository.kt b/app/src/main/java/com/example/pizza/Model/Repository/Rest/RestBasketRepository.kt new file mode 100644 index 0000000..5f4f4ce --- /dev/null +++ b/app/src/main/java/com/example/pizza/Model/Repository/Rest/RestBasketRepository.kt @@ -0,0 +1,80 @@ +package com.example.pizza.Model.Repository.Rest + +import android.annotation.SuppressLint +import android.os.Build +import android.util.Log +import androidx.annotation.RequiresApi +import androidx.lifecycle.MutableLiveData +import androidx.paging.rxjava3.RxPagingSource +import com.example.pizza.Model.Basket.Basket +import com.example.pizza.Model.Basket.PizzaBasket +import com.example.pizza.Model.Dto.BasketDto +import com.example.pizza.Model.Dto.toBasket +import com.example.pizza.Model.Dto.toBasketDto +import com.example.pizza.Model.Dto.toPizza +import com.example.pizza.Model.Dto.toUserDto +import com.example.pizza.Model.Pizza.Pizza +import com.example.pizza.Model.Repository.BasketRepository +import com.example.pizza.Model.Repository.OfflineBasketRepository +import com.example.pizza.Model.Repository.OfflinePizzaRepository +import com.example.pizza.Model.Repository.OfflineUserRepository +import com.example.pizza.Model.Rest.MyServerService +import com.example.pizza.RemoteKeys.OfflineRemoteKeyRepository +import com.example.pizza.database.AppDatabase +import io.reactivex.rxjava3.core.Completable +import io.reactivex.rxjava3.core.Flowable +import io.reactivex.rxjava3.core.Observable +import io.reactivex.rxjava3.schedulers.Schedulers +import okhttp3.internal.wait + +class RestBasketRepository(private val service: MyServerService, + private val dbBasketRepository: OfflineBasketRepository, + private val dbPizzaRepository: OfflinePizzaRepository, + private val dbRemoteKeyRepository: OfflineRemoteKeyRepository, + private val database: AppDatabase +) : BasketRepository { + var AllBaskets : MutableLiveData?> = MutableLiveData() + @RequiresApi(Build.VERSION_CODES.O) + @SuppressLint("CheckResult") + override fun getUserBasket(uid: Int): RxPagingSource { + insertAll(uid) + return AllBaskets.value!! + } + @RequiresApi(Build.VERSION_CODES.O) + @SuppressLint("CheckResult") + fun insertAll(uid: Int){ + Log.d("RestBasketRepository", "Insert All") + Observable.zip(service.getUserBaskets(uid),service.getAllPizzas()){ + baskets,pizzas -> + Log.d("RestBasketRepository", "Insert All") + val bks = baskets.map { it.toBasket() } + val ps = pizzas.map { it.toPizza() } + database.runInTransaction{ + dbBasketRepository.deleteAll().blockingAwait() + dbPizzaRepository.deleteAll().blockingAwait() + dbBasketRepository.insertAll(bks).blockingAwait() + dbPizzaRepository.insertAll(ps).blockingAwait() + } + }.map { dbBasketRepository.getUserBasket(uid) }.subscribe { data -> AllBaskets.postValue(data)} + } + + @SuppressLint("CheckResult") + override fun insert(group: Basket): Completable = Completable.fromObservable(service.createBasket(group.toBasketDto())) + + + override fun getUserAllBasket(uid: Int): Flowable> { + return dbBasketRepository.getUserAllBasket(uid) + } + + override fun update(group: Basket): Completable = throw Exception() + + override fun delete(group: Basket): Completable { + throw Exception() + } + + override fun deleteByUser(uid: Int): Completable = Completable.fromObservable(service.deleteUserBaskets(uid)) + + override fun deleteByUserAndPizza(uid: Int, pid: Int): Completable = Completable.fromObservable(service.deleteBasket( + BasketDto(uid,pid,1) + )) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/pizza/Model/Repository/Rest/RestOrderRepository.kt b/app/src/main/java/com/example/pizza/Model/Repository/Rest/RestOrderRepository.kt new file mode 100644 index 0000000..b8a6626 --- /dev/null +++ b/app/src/main/java/com/example/pizza/Model/Repository/Rest/RestOrderRepository.kt @@ -0,0 +1,54 @@ +package com.example.pizza.Model.Repository.Rest + +import android.database.Observable +import androidx.paging.rxjava3.RxPagingSource +import androidx.room.Index +import com.example.pizza.Model.Dto.OrderDto +import com.example.pizza.Model.Dto.toOrderDto +import com.example.pizza.Model.Order.Order +import com.example.pizza.Model.Order.PizzaOrder +import com.example.pizza.Model.Repository.OfflineOrderRepository +import com.example.pizza.Model.Repository.OfflinePizzaRepository +import com.example.pizza.Model.Repository.OrderRepository +import com.example.pizza.Model.Repository.Rest.Mediators.OrderRemoteMediator +import com.example.pizza.Model.Repository.Rest.Mediators.PizzaRemoteMediator +import com.example.pizza.Model.Rest.MyServerService +import com.example.pizza.RemoteKeys.OfflineRemoteKeyRepository +import com.example.pizza.database.AppDatabase +import io.reactivex.rxjava3.core.Completable + +class RestOrderRepository(private val service: MyServerService, + private val dbOrderRepository: OfflineOrderRepository, + private val dbPizzaRepository: OfflinePizzaRepository, + private val dbRemoteKeyRepository: OfflineRemoteKeyRepository, + private val database: AppDatabase +) : OrderRepository { + var mediator: OrderRemoteMediator? = null + override fun getUserHistory(uid: Int): RxPagingSource { + if (mediator==null){ + mediator = OrderRemoteMediator( + service, + dbOrderRepository, + dbPizzaRepository, + dbRemoteKeyRepository, + database, + uid + ) + } + + return dbOrderRepository.getUserHistory(uid) + } + + override fun insert(group: Order): Completable = Completable.fromObservable(service.createOrder(group.toOrderDto())) + + override fun insertMany(orders: List): Completable + { + var list : MutableList = mutableListOf() + for (order in orders){ + + list.add(order.toOrderDto()) + } + return Completable.fromObservable(service.createOrders(list)) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/pizza/Model/Repository/Rest/RestPizzaRepository.kt b/app/src/main/java/com/example/pizza/Model/Repository/Rest/RestPizzaRepository.kt new file mode 100644 index 0000000..975faf7 --- /dev/null +++ b/app/src/main/java/com/example/pizza/Model/Repository/Rest/RestPizzaRepository.kt @@ -0,0 +1,62 @@ +package com.example.pizza.Model.Repository.Rest + +import android.annotation.SuppressLint +import android.os.Build +import androidx.annotation.RequiresApi +import androidx.paging.rxjava3.RxPagingSource +import com.example.pizza.Model.Dto.toPizza +import com.example.pizza.Model.Dto.toPizzaDto +import com.example.pizza.Model.Pizza.Pizza +import com.example.pizza.Model.Repository.OfflinePizzaRepository +import com.example.pizza.Model.Repository.PizzaRepository +import com.example.pizza.Model.Repository.Rest.Mediators.PizzaRemoteMediator +import com.example.pizza.Model.Rest.MyServerService +import com.example.pizza.RemoteKeys.OfflineRemoteKeyRepository +import com.example.pizza.database.AppDatabase +import io.reactivex.rxjava3.core.Completable +import io.reactivex.rxjava3.core.Single +import io.reactivex.rxjava3.schedulers.Schedulers +import java.net.ConnectException +import java.net.SocketTimeoutException + +class RestPizzaRepository( + private val service: MyServerService, + private val dbPizzaRepository: OfflinePizzaRepository, + private val dbRemoteKeyRepository: OfflineRemoteKeyRepository, + private val database: AppDatabase +) : PizzaRepository{ + var mediator: PizzaRemoteMediator? = null + + override fun getAll(): RxPagingSource { + if (mediator==null){ + mediator = PizzaRemoteMediator( + service, + dbPizzaRepository, + dbRemoteKeyRepository, + database, + ) + } + return dbPizzaRepository.getAll() + } + + @RequiresApi(Build.VERSION_CODES.O) + @SuppressLint("CheckResult") + override fun getById(uid: Int): Single { + return service.getPizza(uid).subscribeOn(Schedulers.io()).map { it.toPizza() } + } + + @RequiresApi(Build.VERSION_CODES.O) + override fun insert(group: Pizza): Completable = Completable.fromObservable( + service.createPizza(group.toPizzaDto())) + + + @RequiresApi(Build.VERSION_CODES.O) + override fun update(group: Pizza): Completable = Completable.fromObservable( + service.updatePizza(group.uid!!,group.toPizzaDto())) + + override fun delete(group: Pizza): Completable = throw Exception() + + override fun deleteAll(): Completable = throw Exception() + + override fun insertAll(pizzas: List): Completable = throw Exception() +} \ No newline at end of file diff --git a/app/src/main/java/com/example/pizza/Model/Repository/Rest/RestUserRepository.kt b/app/src/main/java/com/example/pizza/Model/Repository/Rest/RestUserRepository.kt new file mode 100644 index 0000000..5ea07e9 --- /dev/null +++ b/app/src/main/java/com/example/pizza/Model/Repository/Rest/RestUserRepository.kt @@ -0,0 +1,41 @@ +package com.example.pizza.Model.Repository.Rest + +import android.annotation.SuppressLint +import android.util.Log +import androidx.paging.ExperimentalPagingApi +import androidx.paging.Pager +import androidx.paging.PagingConfig +import androidx.paging.PagingData +import com.example.pizza.Model.Dto.toUser +import com.example.pizza.Model.Dto.toUserDto +import com.example.pizza.Model.Repository.OfflineUserRepository +import com.example.pizza.Model.Repository.UserRepository +import com.example.pizza.Model.Rest.MyServerService +import com.example.pizza.Model.User.User +import com.example.pizza.RemoteKeys.OfflineRemoteKeyRepository +import com.example.pizza.database.AppDatabase +import io.reactivex.rxjava3.core.Completable +import io.reactivex.rxjava3.core.Flowable +import io.reactivex.rxjava3.core.Single + +class RestUserRepository ( + private val service: MyServerService, + private val dbUserRepository: OfflineUserRepository, + private val dbRemoteKeyRepository: OfflineRemoteKeyRepository, + private val database: AppDatabase +) : UserRepository { + @SuppressLint("CheckResult") + override fun getById(uid: Int): Single = service.getUser(uid).map { it.toUser() } + + + override fun getByLoginAndPassword(login: String, pass: String): Single { + return service.getUserLP(login, pass).map { + Log.d(RestUserRepository::class.simpleName ,it.toString()) + it.toUser() } + } + override fun insert(group: User): Completable = Completable.fromObservable(service.createUser(group.toUserDto())) + + override fun update(group: User): Completable = Completable.fromObservable(service.updateUser(group.uid!!,group.toUserDto())) + + override fun delete(group: User): Completable = Completable.fromObservable(service.updateUser(group.uid!!,group.toUserDto())) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/pizza/Model/Repository/UserRepository.kt b/app/src/main/java/com/example/pizza/Model/Repository/UserRepository.kt index 3ca3db7..fd48f01 100644 --- a/app/src/main/java/com/example/pizza/Model/Repository/UserRepository.kt +++ b/app/src/main/java/com/example/pizza/Model/Repository/UserRepository.kt @@ -12,8 +12,6 @@ import io.reactivex.rxjava3.core.Single interface UserRepository { fun getById(uid : Int): Single - fun getAll(): Flowable> - fun getByLoginAndPassword(login : String, pass : String): Single fun insert(group: User): Completable diff --git a/app/src/main/java/com/example/pizza/Model/Rest/MyServerService.kt b/app/src/main/java/com/example/pizza/Model/Rest/MyServerService.kt new file mode 100644 index 0000000..c8738b8 --- /dev/null +++ b/app/src/main/java/com/example/pizza/Model/Rest/MyServerService.kt @@ -0,0 +1,149 @@ +package com.example.pizza.Model.Rest + +import com.example.pizza.Model.Dto.BasketDto +import com.example.pizza.Model.Dto.OrderDto +import com.example.pizza.Model.Dto.PizzaDto +import com.example.pizza.Model.Dto.UserDto +import com.google.gson.GsonBuilder +import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory +import io.reactivex.rxjava3.core.Flowable +import io.reactivex.rxjava3.core.Observable +import io.reactivex.rxjava3.core.Single +import kotlinx.serialization.json.Json +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory +import retrofit2.converter.gson.GsonConverterFactory +import retrofit2.http.Body +import retrofit2.http.DELETE +import retrofit2.http.Field +import retrofit2.http.GET +import retrofit2.http.POST +import retrofit2.http.PUT +import retrofit2.http.Path +import retrofit2.http.Query + +interface MyServerService { + + @GET("pizzas") + fun getPizzas( + @Query("limit") limit: Int, + @Query("offset") offset: Int, + ): Flowable> + @GET("allpizzas") + fun getAllPizzas( + ): Observable> + + @GET("baskets/{id}") + fun getUserBaskets( + @Path("id") id: Int, + ): Observable> + + @GET("pizza/{id}") + fun getPizza( + @Path("id") id: Int, + ): Single + @GET("baskets") + fun getAllBaskets( + ): Observable> + @GET("orders/{id}") + fun getUserOrders( + @Path("id") id: Int, + @Query("page") page: Int, + @Query("size") limit: Int, + ): Observable> + @GET("orders") + fun getAllOrders( + ): Observable> + @GET("users") + fun getAllUsers( + ): Observable + + @GET("user/{id}") + fun getUser( + @Path("id") id: Int, + ): Single + + @GET("user") + fun getUserLP( + @Query("login") login : String , + @Query("password") password : String + ): Single + + @POST("user") + fun createUser( + @Body user: UserDto, + ): Observable + + @POST("pizza") + fun createPizza( + @Body pizza: PizzaDto, + ): Observable + + @POST("basket") + fun createBasket( + @Body basket: BasketDto, + ): Observable + + @POST("order") + fun createOrder( + @Body order: OrderDto, + ): Observable + @POST("orders") + fun createOrders( + @Body order: List, + ): Observable + @PUT("user/{id}") + fun updateUser( + @Path("id") id: Int, + @Body user: UserDto, + ): Observable + + @PUT("pizza/{id}") + fun updatePizza( + @Path("id") id: Int, + @Body user: PizzaDto, + ): Observable + + @DELETE("baskets/{id}") + fun deleteUserBaskets( + @Path("id") id: Int, + ): Observable + + @DELETE("basket") + fun deleteBasket( + @Body user: BasketDto, + ): Observable + + companion object { + private const val BASE_URL = "http://192.168.42.168:8080/api/" + + @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() + val gsonBuilder = GsonBuilder() + gsonBuilder.setLenient() + val gson = gsonBuilder.create() + return Retrofit.Builder() + .baseUrl(BASE_URL) + .client(client) + //.addConverterFactory(Json.asConverterFactory("application/json".toMediaType())) + .addCallAdapterFactory(RxJava3CallAdapterFactory.create()) + .addConverterFactory(GsonConverterFactory.create(gson)) + //.addCallAdapterFactory(RxJavaCallAdapterFactory.create()) + .build() + .create(MyServerService::class.java) + .also { INSTANCE = it } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/pizza/RemoteKeys/OfflineRemoteKeyRepository.kt b/app/src/main/java/com/example/pizza/RemoteKeys/OfflineRemoteKeyRepository.kt new file mode 100644 index 0000000..0701806 --- /dev/null +++ b/app/src/main/java/com/example/pizza/RemoteKeys/OfflineRemoteKeyRepository.kt @@ -0,0 +1,15 @@ +package com.example.pizza.RemoteKeys + +import io.reactivex.rxjava3.core.Completable +import io.reactivex.rxjava3.core.Maybe + +class OfflineRemoteKeyRepository(private val remoteKeysDao: RemoteKeysDao) : RemoteKeyRepository { + override fun getAllRemoteKeys(id: Int, type: RemoteKeyType) = + remoteKeysDao.getRemoteKeys(id, type) + + override fun createRemoteKeys(remoteKeys: List) = + remoteKeysDao.insertAll(remoteKeys) + + override fun deleteRemoteKey(type: RemoteKeyType) = + remoteKeysDao.clearRemoteKeys(type) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/pizza/RemoteKeys/RemoteKeyRepository.kt b/app/src/main/java/com/example/pizza/RemoteKeys/RemoteKeyRepository.kt new file mode 100644 index 0000000..66b396d --- /dev/null +++ b/app/src/main/java/com/example/pizza/RemoteKeys/RemoteKeyRepository.kt @@ -0,0 +1,10 @@ +package com.example.pizza.RemoteKeys + +import io.reactivex.rxjava3.core.Completable +import io.reactivex.rxjava3.core.Maybe + +interface RemoteKeyRepository { + fun getAllRemoteKeys(id: Int, type: RemoteKeyType): Maybe + fun createRemoteKeys(remoteKeys: List) : Completable + fun deleteRemoteKey(type: RemoteKeyType) : Completable +} \ No newline at end of file diff --git a/app/src/main/java/com/example/pizza/RemoteKeys/RemoteKeys.kt b/app/src/main/java/com/example/pizza/RemoteKeys/RemoteKeys.kt new file mode 100644 index 0000000..af0f1a0 --- /dev/null +++ b/app/src/main/java/com/example/pizza/RemoteKeys/RemoteKeys.kt @@ -0,0 +1,32 @@ +package com.example.pizza.RemoteKeys + +import androidx.room.Entity +import androidx.room.PrimaryKey +import androidx.room.TypeConverter +import androidx.room.TypeConverters +import com.example.pizza.Model.User.User +import com.example.pizza.Model.Pizza.Pizza +import com.example.pizza.Model.Basket.Basket +import com.example.pizza.Model.Order.Order + + +@Entity(tableName = "remote_keys") +data class RemoteKeys( + @PrimaryKey val entityId: Int, + @TypeConverters(RemoteKeyType::class) + val type: RemoteKeyType, + val prevKey: Int?, + val nextKey: Int? +) + +enum class RemoteKeyType(private val type: String) { + USER(User::class.simpleName ?: "User"), + PIZZA(Pizza::class.simpleName ?: "Pizza"), + ORDER(Order::class.simpleName ?: "Order"), + BASKET(Basket::class.simpleName ?: "Basket"); + @TypeConverter + fun toRemoteKeyType(value: String) = RemoteKeyType.values().first { it.type == value } + + @TypeConverter + fun fromRemoteKeyType(value: RemoteKeyType) = value.type +} \ No newline at end of file diff --git a/app/src/main/java/com/example/pizza/RemoteKeys/RemoteKeysDao.kt b/app/src/main/java/com/example/pizza/RemoteKeys/RemoteKeysDao.kt new file mode 100644 index 0000000..c1aeeae --- /dev/null +++ b/app/src/main/java/com/example/pizza/RemoteKeys/RemoteKeysDao.kt @@ -0,0 +1,20 @@ +package com.example.pizza.RemoteKeys + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import io.reactivex.rxjava3.core.Completable +import io.reactivex.rxjava3.core.Maybe + +@Dao +interface RemoteKeysDao { + @Query("SELECT * FROM remote_keys WHERE entityId = :entityId AND type = :type") + fun getRemoteKeys(entityId: Int, type: RemoteKeyType): Maybe + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insertAll(remoteKey: List) : Completable + + @Query("DELETE FROM remote_keys WHERE type = :type") + fun clearRemoteKeys(type: RemoteKeyType) : Completable +} \ No newline at end of file diff --git a/app/src/main/java/com/example/pizza/ViewModels/AppViewModelProvider.kt b/app/src/main/java/com/example/pizza/ViewModels/AppViewModelProvider.kt index 1f877dd..04d6fde 100644 --- a/app/src/main/java/com/example/pizza/ViewModels/AppViewModelProvider.kt +++ b/app/src/main/java/com/example/pizza/ViewModels/AppViewModelProvider.kt @@ -14,9 +14,7 @@ object AppViewModelProvider { initializer { PizzaEditViewModel(studentApplication().container.pizzaRepository) } - initializer { - UserViewModel(studentApplication().container.userRepository) - } + initializer { BasketEditViewModel(studentApplication().container.basketRepository) } @@ -29,6 +27,9 @@ object AppViewModelProvider { initializer { OrderListViewModel(studentApplication().container.orderRepository) } + initializer { + UserViewModel(studentApplication().container.userRepository) + } } } diff --git a/app/src/main/java/com/example/pizza/ViewModels/OrderListViewModel.kt b/app/src/main/java/com/example/pizza/ViewModels/OrderListViewModel.kt index 2c37854..9cb9c61 100644 --- a/app/src/main/java/com/example/pizza/ViewModels/OrderListViewModel.kt +++ b/app/src/main/java/com/example/pizza/ViewModels/OrderListViewModel.kt @@ -4,15 +4,18 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import androidx.paging.ExperimentalPagingApi import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.PagingData +import androidx.paging.PagingSource import androidx.paging.rxjava3.cachedIn import androidx.paging.rxjava3.flowable import com.example.pizza.Model.Basket.PizzaBasket import com.example.pizza.Model.Order.PizzaOrder import com.example.pizza.Model.Repository.BasketRepository import com.example.pizza.Model.Repository.OrderRepository +import com.example.pizza.Model.Repository.Rest.RestOrderRepository import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.schedulers.Schedulers @@ -24,38 +27,33 @@ class OrderListViewModel(private val orderRepository: OrderRepository): ViewMode private val pizzas: MutableLiveData?> = MutableLiveData() fun getPizzas(uid: Int): LiveData?> { - if (pizzas.value == null) { loadPizzas(uid) - } return pizzas } - private fun createPagingConfig(): PagingConfig { + + + @OptIn(ExperimentalCoroutinesApi::class, ExperimentalPagingApi::class) + private fun loadPizzas(uid: Int) { val pageSize = 3 val placeholders = true - return PagingConfig( - pageSize, - pageSize, - placeholders, - pageSize * 2, - pageSize + pageSize * 2, Int.MIN_VALUE + var config = PagingConfig( + pageSize = pageSize, + enablePlaceholders =placeholders ) - } - - - private fun loadPizzas(uid: Int) { - val disposable = Pager( - config = createPagingConfig(), - pagingSourceFactory = {orderRepository.getUserHistory(uid)} - ).flowable - .cachedIn(viewModelScope) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .doOnError({pizzas.value = null}) - .subscribe { data -> - pizzas.postValue(data) - } - mDisposable.add(disposable) +// val disposable = Pager( +// config = config, +// remoteMediator = (orderRepository as RestOrderRepository).mediator, +// pagingSourceFactory = { orderRepository.getUserHistory(uid) } +// ).flowable +// .cachedIn(viewModelScope) +// .subscribeOn(Schedulers.io()) +// .observeOn(AndroidSchedulers.mainThread()) +// .doOnError({pizzas.value = null}) +// .subscribe { data -> +// pizzas.postValue(data) +// } +// mDisposable.add(disposable) } override fun onCleared() { diff --git a/app/src/main/java/com/example/pizza/ViewModels/PizzaEditViewModel.kt b/app/src/main/java/com/example/pizza/ViewModels/PizzaEditViewModel.kt index 2d4f6ee..4b419f6 100644 --- a/app/src/main/java/com/example/pizza/ViewModels/PizzaEditViewModel.kt +++ b/app/src/main/java/com/example/pizza/ViewModels/PizzaEditViewModel.kt @@ -36,13 +36,13 @@ class PizzaEditViewModel( mDisposable.add(disposable) } - fun savePizza(pizza: Pizza, onSave: () -> Unit) { + fun savePizza(pizza: Pizza, onSave: () -> Unit,onError: () -> Unit) { val completable: Completable = pizzaRepository.insert(pizza) val disposable = completable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ onSave() }) + .subscribe({ onSave() },{onError()}) mDisposable.add(disposable) } fun updatePizza(pizza: Pizza, onSave: () -> Unit, onError: () -> Unit) { diff --git a/app/src/main/java/com/example/pizza/ViewModels/PizzaListViewModel.kt b/app/src/main/java/com/example/pizza/ViewModels/PizzaListViewModel.kt index adf9624..7626b4e 100644 --- a/app/src/main/java/com/example/pizza/ViewModels/PizzaListViewModel.kt +++ b/app/src/main/java/com/example/pizza/ViewModels/PizzaListViewModel.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import androidx.paging.ExperimentalPagingApi import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.PagingData @@ -11,6 +12,7 @@ import androidx.paging.rxjava3.cachedIn import androidx.paging.rxjava3.flowable import com.example.pizza.Model.Pizza.Pizza import com.example.pizza.Model.Repository.PizzaRepository +import com.example.pizza.Model.Repository.Rest.RestPizzaRepository import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.schedulers.Schedulers @@ -22,29 +24,26 @@ class PizzaListViewModel(private val pizzaRepository: PizzaRepository): ViewMode private val pizzas: MutableLiveData> = MutableLiveData() fun getPizzas(): LiveData> { - if (pizzas.value == null) { loadPizzas() - } return pizzas } - private fun createPagingConfig(): PagingConfig { + + + @OptIn(ExperimentalCoroutinesApi::class, ExperimentalPagingApi::class) + private fun loadPizzas() { + var meditator = (pizzaRepository as RestPizzaRepository).mediator + var pagingSourceFactory = {pizzaRepository.getAll()} val pageSize = 3 val placeholders = true - return PagingConfig( - pageSize, - pageSize, - placeholders, - pageSize * 2, - pageSize + pageSize * 2, Int.MIN_VALUE + var config = PagingConfig( + pageSize = pageSize, + enablePlaceholders =placeholders ) - } - - @OptIn(ExperimentalCoroutinesApi::class) - private fun loadPizzas() { val disposable = Pager( - config = createPagingConfig(), - pagingSourceFactory = pizzaRepository::getAll + config = config, + pagingSourceFactory = pagingSourceFactory, + remoteMediator = meditator, ).flowable .cachedIn(viewModelScope) .subscribeOn(Schedulers.io()) diff --git a/app/src/main/java/com/example/pizza/ViewModels/UserViewModel.kt b/app/src/main/java/com/example/pizza/ViewModels/UserViewModel.kt index f6d7d6a..25810ae 100644 --- a/app/src/main/java/com/example/pizza/ViewModels/UserViewModel.kt +++ b/app/src/main/java/com/example/pizza/ViewModels/UserViewModel.kt @@ -87,6 +87,7 @@ class UserViewModel( } + override fun onCleared() { super.onCleared() mDisposable.clear() diff --git a/app/src/main/java/com/example/pizza/create_pizza.kt b/app/src/main/java/com/example/pizza/create_pizza.kt index 34bee97..c11e788 100644 --- a/app/src/main/java/com/example/pizza/create_pizza.kt +++ b/app/src/main/java/com/example/pizza/create_pizza.kt @@ -83,12 +83,15 @@ class create_pizza : Fragment() { return } if(!editing) { - pizzaViewModel!!.savePizza(pizza1) { + pizzaViewModel!!.savePizza(pizza1, + onSave = { Toast.makeText(view.context, "Пицца успешно добавлена", Toast.LENGTH_SHORT).show() val action = create_pizzaDirections .actionNavigationCreatePizzaToListNavigation() Navigation.findNavController(view).navigate(action) - } + }, + onError = {Toast.makeText(view.context, "Ошибка!!!", Toast.LENGTH_SHORT).show()} + ) } else { diff --git a/app/src/main/java/com/example/pizza/database/AppContainer.kt b/app/src/main/java/com/example/pizza/database/AppContainer.kt index 992f97a..a179370 100644 --- a/app/src/main/java/com/example/pizza/database/AppContainer.kt +++ b/app/src/main/java/com/example/pizza/database/AppContainer.kt @@ -8,7 +8,14 @@ import com.example.pizza.Model.Repository.OfflinePizzaRepository import com.example.pizza.Model.Repository.OfflineUserRepository import com.example.pizza.Model.Repository.OrderRepository import com.example.pizza.Model.Repository.PizzaRepository +import com.example.pizza.Model.Repository.Rest.RestBasketRepository +import com.example.pizza.Model.Repository.Rest.RestOrderRepository +import com.example.pizza.Model.Repository.Rest.RestPizzaRepository +import com.example.pizza.Model.Repository.Rest.RestUserRepository import com.example.pizza.Model.Repository.UserRepository +import com.example.pizza.Model.Rest.MyServerService +import com.example.pizza.RemoteKeys.OfflineRemoteKeyRepository +import com.example.pizza.RemoteKeys.RemoteKeyRepository interface AppContainer { @@ -16,20 +23,45 @@ interface AppContainer { val userRepository: UserRepository val basketRepository: BasketRepository val orderRepository : OrderRepository + val remoteKeyRepository: RemoteKeyRepository } class AppDataContainer(private val context: Context) : AppContainer { override val pizzaRepository: PizzaRepository by lazy { - OfflinePizzaRepository(AppDatabase.getInstance(context).pizzaDao()) + RestPizzaRepository( + MyServerService.getInstance(), + OfflinePizzaRepository(AppDatabase.getInstance(context).pizzaDao()), + OfflineRemoteKeyRepository(AppDatabase.getInstance(context).remoteKeysDao()), + AppDatabase.getInstance(context)) } override val userRepository: UserRepository by lazy { - OfflineUserRepository(AppDatabase.getInstance(context).userDao()) + RestUserRepository( + MyServerService.getInstance(), + OfflineUserRepository(AppDatabase.getInstance(context).userDao()), + OfflineRemoteKeyRepository(AppDatabase.getInstance(context).remoteKeysDao()), + AppDatabase.getInstance(context) + ) } override val basketRepository: BasketRepository by lazy { - OfflineBasketRepository(AppDatabase.getInstance(context).basketDao()) + RestBasketRepository( + MyServerService.getInstance(), + OfflineBasketRepository(AppDatabase.getInstance(context).basketDao()), + OfflinePizzaRepository(AppDatabase.getInstance(context).pizzaDao()), + OfflineRemoteKeyRepository(AppDatabase.getInstance(context).remoteKeysDao()), + AppDatabase.getInstance(context)) } override val orderRepository: OrderRepository by lazy { - OfflineOrderRepository(AppDatabase.getInstance(context).orderDao()) + RestOrderRepository( + MyServerService.getInstance(), + OfflineOrderRepository(AppDatabase.getInstance(context).orderDao()), + OfflinePizzaRepository(AppDatabase.getInstance(context).pizzaDao()), + OfflineRemoteKeyRepository(AppDatabase.getInstance(context).remoteKeysDao()), + AppDatabase.getInstance(context)) + + + } + override val remoteKeyRepository: RemoteKeyRepository by lazy { + OfflineRemoteKeyRepository(AppDatabase.getInstance(context).remoteKeysDao()) } diff --git a/app/src/main/java/com/example/pizza/database/AppDatabase.kt b/app/src/main/java/com/example/pizza/database/AppDatabase.kt index 11bad88..eff8c65 100644 --- a/app/src/main/java/com/example/pizza/database/AppDatabase.kt +++ b/app/src/main/java/com/example/pizza/database/AppDatabase.kt @@ -26,22 +26,30 @@ import com.example.pizza.Model.Pizza.PizzaDao import com.example.pizza.Model.Pizza.Singleton import com.example.pizza.Model.User.User import com.example.pizza.Model.User.UserDao +import com.example.pizza.RemoteKeys.RemoteKeys +import com.example.pizza.RemoteKeys.RemoteKeysDao import java.io.ByteArrayOutputStream import java.text.SimpleDateFormat import java.util.Date import java.util.concurrent.Executors -@Database(entities = [User::class, Pizza::class,Basket::class,Order::class], version = 1, exportSchema = false) +@Database(entities = [ + User::class, + Pizza::class, + Basket::class, + Order::class, + RemoteKeys::class, + ], version = 1, exportSchema = false) @TypeConverters(ImageConverter::class, DateConverter::class) abstract class AppDatabase : RoomDatabase() { abstract fun userDao(): UserDao abstract fun pizzaDao(): PizzaDao abstract fun basketDao(): BasketDao abstract fun orderDao(): OrderDao - + abstract fun remoteKeysDao(): RemoteKeysDao companion object { - private const val DB_NAME: String = "dp9" + private const val DB_NAME: String = "dp11" @Volatile private var INSTANCE: AppDatabase? = null @@ -101,7 +109,7 @@ abstract class AppDatabase : RoomDatabase() { .addCallback(object : Callback() { override fun onCreate(db: SupportSQLiteDatabase) { super.onCreate(db) - Executors.newSingleThreadExecutor().execute { populateDatabase() } + //Executors.newSingleThreadExecutor().execute { populateDatabase() } } }) .build() diff --git a/app/src/main/res/xml/network_security_config.xml b/app/src/main/res/xml/network_security_config.xml new file mode 100644 index 0000000..6c7cfec --- /dev/null +++ b/app/src/main/res/xml/network_security_config.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file