This commit is contained in:
VictoriaPresnyakova 2023-12-17 21:47:25 +04:00
parent 2aa2e5bc60
commit 7af968a96a
35 changed files with 681 additions and 91 deletions

View File

@ -2,6 +2,7 @@ plugins {
id("com.android.application") id("com.android.application")
id("org.jetbrains.kotlin.android") id("org.jetbrains.kotlin.android")
id ("kotlin-kapt") id ("kotlin-kapt")
kotlin("plugin.serialization") version "1.4.21"
} }
android { android {
@ -87,4 +88,12 @@ dependencies {
//Paging //Paging
implementation ("androidx.paging:paging-compose:3.2.1") implementation ("androidx.paging:paging-compose:3.2.1")
implementation ("androidx.paging:paging-runtime:3.2.1") implementation ("androidx.paging:paging-runtime:3.2.1")
// retrofit
val retrofitVersion = "2.9.0"
implementation("com.squareup.retrofit2:retrofit:$retrofitVersion")
implementation("com.squareup.okhttp3:logging-interceptor:4.11.0")
implementation("androidx.paging:paging-compose:3.2.1")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0")
} }

View File

@ -8,4 +8,8 @@ interface AppContainer {
val hotelRepo: HotelRepository val hotelRepo: HotelRepository
val userRepo: UserRepository val userRepo: UserRepository
val orderRepo: OrderRepository val orderRepo: OrderRepository
companion object {
const val TIMEOUT = 5000L
const val LIMIT = 10
}
} }

View File

@ -1,22 +1,38 @@
package com.example.androidlabs package com.example.androidlabs
import RestUserRepository
import android.content.Context import android.content.Context
import com.example.androidlabs.DB.AppDatabase import com.example.androidlabs.DB.AppDatabase
import com.example.androidlabs.DB.repository.HotelRepImpl import com.example.androidlabs.DB.repository.HotelRepImpl
import com.example.androidlabs.DB.repository.HotelRepository import com.example.androidlabs.DB.repository.HotelRepository
import com.example.androidlabs.DB.repository.OrderRepImpl
import com.example.androidlabs.DB.repository.OrderRepository import com.example.androidlabs.DB.repository.OrderRepository
import com.example.androidlabs.DB.repository.UserRepImpl import com.example.androidlabs.DB.repository.RemoteKeysRepositoryImpl
import com.example.androidlabs.DB.repository.UserRepository import com.example.androidlabs.DB.repository.UserRepository
import com.example.androidlabs.api.BackendService
import com.example.androidlabs.api.repository.RestHotelRepository
import com.example.androidlabs.api.repository.RestOrderRepository
class AppDataContainer(private val context: Context) : AppContainer { class AppDataContainer(private val context: Context) : AppContainer {
override val hotelRepo: HotelRepository by lazy { override val hotelRepo: HotelRepository by lazy {
HotelRepImpl(AppDatabase.getInstance(context).hotelDao()) RestHotelRepository(
BackendService.getInstance(),
hotelRepository,
AppDatabase.getInstance(context),
remoteKeyRepository
)
} }
override val userRepo: UserRepository by lazy { override val userRepo: UserRepository by lazy {
UserRepImpl(AppDatabase.getInstance(context).userDao()) RestUserRepository(BackendService.getInstance())
} }
override val orderRepo: OrderRepository by lazy { override val orderRepo: OrderRepository by lazy {
OrderRepImpl(AppDatabase.getInstance(context).orderDao()) RestOrderRepository(BackendService.getInstance())
}
private val hotelRepository: HotelRepImpl by lazy {
HotelRepImpl(AppDatabase.getInstance(context).hotelDao())
}
private val remoteKeyRepository: RemoteKeysRepositoryImpl by lazy {
RemoteKeysRepositoryImpl(AppDatabase.getInstance(context).remoteKeysDao())
} }
} }

View File

@ -7,21 +7,25 @@ import androidx.room.RoomDatabase
import androidx.sqlite.db.SupportSQLiteDatabase import androidx.sqlite.db.SupportSQLiteDatabase
import com.example.androidlabs.DB.dao.HotelDao import com.example.androidlabs.DB.dao.HotelDao
import com.example.androidlabs.DB.dao.OrderDao import com.example.androidlabs.DB.dao.OrderDao
import com.example.androidlabs.DB.dao.RemoteKeysDao
import com.example.androidlabs.DB.dao.UserDao import com.example.androidlabs.DB.dao.UserDao
import com.example.androidlabs.DB.models.Hotel import com.example.androidlabs.DB.models.Hotel
import com.example.androidlabs.DB.models.Order import com.example.androidlabs.DB.models.Order
import com.example.androidlabs.DB.models.RemoteKeys
import com.example.androidlabs.DB.models.RoleEnum import com.example.androidlabs.DB.models.RoleEnum
import com.example.androidlabs.DB.models.User import com.example.androidlabs.DB.models.User
import com.example.androidlabs.R import com.example.androidlabs.R
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
// TODO
@Database(entities = [Hotel::class, User::class, Order::class], version = 5) @Database(entities = [Hotel::class, User::class, Order::class, RemoteKeys::class], version = 5)
abstract class AppDatabase : RoomDatabase() { abstract class AppDatabase : RoomDatabase() {
abstract fun hotelDao(): HotelDao abstract fun hotelDao(): HotelDao
abstract fun userDao(): UserDao abstract fun userDao(): UserDao
abstract fun orderDao(): OrderDao abstract fun orderDao(): OrderDao
abstract fun remoteKeysDao(): RemoteKeysDao
companion object { companion object {
private const val DB_NAME: String = "my-db" private const val DB_NAME: String = "my-db"
@ -33,9 +37,9 @@ abstract class AppDatabase : RoomDatabase() {
INSTANCE?.let { database -> INSTANCE?.let { database ->
// User // User
val userDao = database.userDao() val userDao = database.userDao()
val user1 = User(null, "Artem", "Emelyanov", "artem@mail.ru", "123", RoleEnum.User) val user1 = User(null, "Artem", "Emelyanov", "artem@mail.ru", "123", "USER")
val user2 = User(null, "Danil", "Markov", "danil@mail.ru", "123", RoleEnum.User) val user2 = User(null, "Danil", "Markov", "danil@mail.ru", "123", "ADMIN")
val user3 = User(null, "Viktoria", "Presnyakova", "vika@mail.ru", "123", RoleEnum.Admin) val user3 = User(null, "Viktoria", "Presnyakova", "vika@mail.ru", "123", "USER")
userDao.createUser(user1) userDao.createUser(user1)
userDao.createUser(user2) userDao.createUser(user2)
userDao.createUser(user3) userDao.createUser(user3)

View File

@ -11,7 +11,7 @@ import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface HotelDao { interface HotelDao {
@Insert @Insert
suspend fun insert(hotel: Hotel) suspend fun insert(vararg hotel: Hotel)
@Update @Update
suspend fun update(hotel: Hotel) suspend fun update(hotel: Hotel)
@ -24,4 +24,10 @@ interface HotelDao {
@Query("SELECT * FROM Hotel WHERE hotelId = :id") @Query("SELECT * FROM Hotel WHERE hotelId = :id")
suspend fun getHotelById(id: Int): Hotel suspend fun getHotelById(id: Int): Hotel
@Query("select * from Hotel")
fun getAll(): PagingSource<Int, Hotel>
@Query("DELETE FROM Hotel")
suspend fun deleteAll()
} }

View File

@ -0,0 +1,19 @@
package com.example.androidlabs.DB.dao
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.example.androidlabs.DB.models.RemoteKeyType
import com.example.androidlabs.DB.models.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<RemoteKeys>)
@Query("DELETE FROM remote_keys WHERE type = :type")
suspend fun clearRemoteKeys(type: RemoteKeyType)
}

View File

@ -21,6 +21,6 @@ data class Order(
val creatorUserId: Int, val creatorUserId: Int,
@ColumnInfo(name = "BookedHotelId") @ColumnInfo(name = "BookedHotelId")
val bookedHotelId: Int, val bookedHotelId: Int,
@Embedded // @Embedded
val hotel: Hotel // val hotel: Hotel
) )

View File

@ -0,0 +1,22 @@
package com.example.androidlabs.DB.models
import androidx.room.Entity
import androidx.room.PrimaryKey
import androidx.room.TypeConverter
import androidx.room.TypeConverters
enum class RemoteKeyType(private val type: String) {
SNEAKER(Hotel::class.simpleName ?: "Hotel");
@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?
)

View File

@ -17,7 +17,7 @@ data class User(
@ColumnInfo(name = "Password") @ColumnInfo(name = "Password")
val password: String, val password: String,
@ColumnInfo(name = "Role") @ColumnInfo(name = "Role")
val role: RoleEnum, val role: String,
@ColumnInfo(name = "Photo") @ColumnInfo(name = "Photo")
val photo: Int? = null, val photo: Int? = null,
) )

View File

@ -6,6 +6,7 @@ import androidx.paging.PagingData
import com.example.androidlabs.DB.dao.HotelDao import com.example.androidlabs.DB.dao.HotelDao
import com.example.androidlabs.DB.models.Hotel import com.example.androidlabs.DB.models.Hotel
import androidx.paging.PagingSource import androidx.paging.PagingSource
import com.example.androidlabs.AppContainer
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@ -18,12 +19,17 @@ class HotelRepImpl (private val hotelDao: HotelDao) : HotelRepository {
override suspend fun deleteHotel(hotel: Hotel) = hotelDao.delete(hotel) override suspend fun deleteHotel(hotel: Hotel) = hotelDao.delete(hotel)
override suspend fun getHotelById(id: Int): Hotel = hotelDao.getHotelById(id) override suspend fun getHotelById(id: Int): Hotel = hotelDao.getHotelById(id)
override fun getAllHotelsPaged(): PagingSource<Int, Hotel> = hotelDao.getAllHotelsPaged() override fun getAllHotels(): Flow<PagingData<Hotel>> = Pager(
override fun call(): Flow<PagingData<Hotel>> { config = PagingConfig(
return Pager( pageSize = AppContainer.LIMIT,
PagingConfig(pageSize = 5) enablePlaceholders = false
) { ),
hotelDao.getAllHotelsPaged() pagingSourceFactory = hotelDao::getAll
}.flow ).flow
}
suspend fun clearHotels() = hotelDao.deleteAll()
suspend fun insertHotels(hotels: List<Hotel>) =
hotelDao.insert(*hotels.toTypedArray())
fun getAllHotelsPagingSource(): PagingSource<Int, Hotel> = hotelDao.getAll()
} }

View File

@ -9,6 +9,6 @@ interface HotelRepository {
suspend fun updateHotel(hotel: Hotel) suspend fun updateHotel(hotel: Hotel)
suspend fun deleteHotel(hotel: Hotel) suspend fun deleteHotel(hotel: Hotel)
suspend fun getHotelById(id: Int): Hotel suspend fun getHotelById(id: Int): Hotel
fun getAllHotelsPaged(): PagingSource<Int, Hotel> fun getAllHotels(): Flow<PagingData<Hotel>>
fun call(): Flow<PagingData<Hotel>>
} }

View File

@ -1,17 +1,17 @@
package com.example.androidlabs.DB.repository //package com.example.androidlabs.DB.repository
//
import com.example.androidlabs.DB.dao.OrderDao //import com.example.androidlabs.DB.dao.OrderDao
import com.example.androidlabs.DB.models.Order //import com.example.androidlabs.DB.models.Order
import com.example.androidlabs.DB.models.UserWithOrder //import com.example.androidlabs.DB.models.UserWithOrder
import kotlinx.coroutines.flow.Flow //import kotlinx.coroutines.flow.Flow
//
class OrderRepImpl (private val orderDao: OrderDao) : OrderRepository { //class OrderRepImpl (private val orderDao: OrderDao) : OrderRepository {
//
override suspend fun createOrder(order: Order): Long = orderDao.createOrder(order) // override suspend fun createOrder(order: Order): Long = orderDao.createOrder(order)
//
override suspend fun delete(order: Order) = orderDao.delete(order) // override suspend fun delete(order: Order) = orderDao.delete(order)
//
override fun getAllOrder(): Flow<List<Order>> = orderDao.getAllOrder() // override fun getAllOrder(): Flow<List<Order>> = orderDao.getAllOrder()
//
override fun getUserOrders(id: Int): Flow<UserWithOrder> = orderDao.getUserOrders(id) // override fun getUserOrders(id: Int): Flow<UserWithOrder> = orderDao.getUserOrders(id)
} //}

View File

@ -1,12 +1,15 @@
package com.example.androidlabs.DB.repository package com.example.androidlabs.DB.repository
import com.example.androidlabs.DB.models.Hotel
import com.example.androidlabs.DB.models.Order import com.example.androidlabs.DB.models.Order
import com.example.androidlabs.DB.models.UserWithOrder import com.example.androidlabs.DB.models.UserWithOrder
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
interface OrderRepository { interface OrderRepository {
suspend fun createOrder(order: Order): Long suspend fun createOrder(order: Order): Long
suspend fun delete(order: Order) suspend fun delete(orderId: Int)
fun getAllOrder(): Flow<List<Order>> //fun getAllOrder(): Flow<List<Order>>
fun getUserOrders(id: Int) : Flow<UserWithOrder> suspend fun getHotelFromOrder(id: Int): Hotel
suspend fun getUserOrders(id: Int) : Flow<List<Order>>
} }

View File

@ -0,0 +1,10 @@
package com.example.androidlabs.DB.repository
import com.example.androidlabs.DB.models.RemoteKeyType
import com.example.androidlabs.DB.models.RemoteKeys
interface RemoteKeyRepository {
suspend fun getAllRemoteKeys(id: Int, type: RemoteKeyType): RemoteKeys?
suspend fun createRemoteKeys(remoteKeys: List<RemoteKeys>)
suspend fun deleteRemoteKey(type: RemoteKeyType)
}

View File

@ -0,0 +1,16 @@
package com.example.androidlabs.DB.repository
import com.example.androidlabs.DB.dao.RemoteKeysDao
import com.example.androidlabs.DB.models.RemoteKeyType
import com.example.androidlabs.DB.models.RemoteKeys
class RemoteKeysRepositoryImpl(private val remoteKeysDao: RemoteKeysDao) : RemoteKeyRepository {
override suspend fun getAllRemoteKeys(id: Int, type: RemoteKeyType) =
remoteKeysDao.getRemoteKeys(id, type)
override suspend fun createRemoteKeys(remoteKeys: List<RemoteKeys>) =
remoteKeysDao.insertAll(remoteKeys)
override suspend fun deleteRemoteKey(type: RemoteKeyType) =
remoteKeysDao.clearRemoteKeys(type)
}

View File

@ -3,15 +3,15 @@ package com.example.androidlabs.DB.repository
import com.example.androidlabs.DB.dao.UserDao import com.example.androidlabs.DB.dao.UserDao
import com.example.androidlabs.DB.models.User import com.example.androidlabs.DB.models.User
class UserRepImpl(private val userDao: UserDao) : UserRepository { //class UserRepImpl(private val userDao: UserDao) : UserRepository {
//
override suspend fun createUser(user: User) = userDao.createUser(user) // override suspend fun createUser(user: User) = userDao.createUser(user)
//
override suspend fun updateUser(user: User) = userDao.updateUser(user) // override suspend fun updateUser(user: User) = userDao.updateUser(user)
//
override suspend fun deleteUser(user: User) = userDao.deleteUser(user) // override suspend fun deleteUser(user: User) = userDao.deleteUser(user)
//
override suspend fun getUserById(id: Int): User = userDao.getUserById(id) // override suspend fun getUserById(id: Int): User = userDao.getUserById(id)
//
override suspend fun getUserByEmail(email: String): User = userDao.getUserByEmail(email) // override suspend fun getUserByEmail(email: String): User = userDao.getUserByEmail(email)
} //}

View File

@ -1,11 +1,11 @@
package com.example.androidlabs.DB.repository package com.example.androidlabs.DB.repository
import com.example.androidlabs.DB.models.User import com.example.androidlabs.DB.models.User
import com.example.androidlabs.api.model.UserRemoteSignIn
interface UserRepository { interface UserRepository {
suspend fun createUser(user: User) suspend fun createUser(user: User)
suspend fun updateUser(user: User) suspend fun updateUser(user: User)
suspend fun deleteUser(user: User) suspend fun deleteUser(user: User)
suspend fun getUserById(id: Int): User suspend fun authUser(user: UserRemoteSignIn): User
suspend fun getUserByEmail(email: String): User
} }

View File

@ -22,7 +22,7 @@ class HotelViewModel(private val hotelRepository: HotelRepository): ViewModel()
val stars = mutableStateOf("") val stars = mutableStateOf("")
val info = mutableStateOf("") val info = mutableStateOf("")
val img = mutableStateOf(R.drawable.img) val img = mutableStateOf(R.drawable.img)
val HotelList = hotelRepository.call().cachedIn(viewModelScope) val HotelList = hotelRepository.getAllHotels()
var hotel: Hotel? = null var hotel: Hotel? = null
fun insertHotel() = viewModelScope.launch { fun insertHotel() = viewModelScope.launch {

View File

@ -23,14 +23,17 @@ class OrderViewModel(private val orderRepository: OrderRepository) : ViewModel()
var dateFrom = mutableStateOf("") var dateFrom = mutableStateOf("")
var dateTo = mutableStateOf("") var dateTo = mutableStateOf("")
fun deleteOrder(order: Order) = viewModelScope.launch { fun deleteOrder(orderId: Int) = viewModelScope.launch {
orderRepository.delete(order) orderRepository.delete(orderId)
} }
fun getOrderList(id: Int) : Flow<UserWithOrder> { suspend fun getOrderList(id: Int) : Flow<List<Order>> {
return orderRepository.getUserOrders(id) return orderRepository.getUserOrders(id)
} }
suspend fun getHotelFromOrder(id: Int) : Hotel {
return orderRepository.getHotelFromOrder(id)
}
fun createOrder() = viewModelScope.launch { fun createOrder() = viewModelScope.launch {
Log.d("MyLog", GlobalUser.getInstance().getUser()?.userId.toString()) Log.d("MyLog", GlobalUser.getInstance().getUser()?.userId.toString())
@ -42,7 +45,7 @@ class OrderViewModel(private val orderRepository: OrderRepository) : ViewModel()
total = getSubTotal(), total = getSubTotal(),
bookedHotelId = selectedItem?.hotelId!!, bookedHotelId = selectedItem?.hotelId!!,
creatorUserId = GlobalUser.getInstance().getUser()?.userId!!, creatorUserId = GlobalUser.getInstance().getUser()?.userId!!,
hotel = selectedItem!! //hotel = selectedItem!!
) )
val orderId = orderRepository.createOrder(order) val orderId = orderRepository.createOrder(order)

View File

@ -12,6 +12,8 @@ import com.example.androidlabs.DB.models.RoleEnum
import com.example.androidlabs.DB.models.User import com.example.androidlabs.DB.models.User
import com.example.androidlabs.DB.repository.UserRepository import com.example.androidlabs.DB.repository.UserRepository
import com.example.androidlabs.GlobalUser import com.example.androidlabs.GlobalUser
import com.example.androidlabs.R
import com.example.androidlabs.api.model.UserRemoteSignIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class UserViewModel(private val userRepository: UserRepository): ViewModel() { class UserViewModel(private val userRepository: UserRepository): ViewModel() {
@ -26,22 +28,17 @@ class UserViewModel(private val userRepository: UserRepository): ViewModel() {
surname = surname.value, surname = surname.value,
email = email.value, email = email.value,
password = password.value, password = password.value,
role = RoleEnum.User role = "USER",
photo = R.drawable.img_2
) )
userRepository.createUser(user) userRepository.createUser(user)
} }
fun authUser() = viewModelScope.launch { fun authUser() = viewModelScope.launch {
val user = userRepository.getUserByEmail(email.value) val user = userRepository.authUser(UserRemoteSignIn(email.value, password.value))
if (password.value != "" && user.password == password.value) { GlobalUser.getInstance().setUser(user)
val globalUser = GlobalUser.getInstance() }
globalUser.setUser(user)
Log.d("MyLog", GlobalUser.getInstance().getUser()?.userId.toString())
println()
}
}
fun isValidEmail(email: String): Boolean { fun isValidEmail(email: String): Boolean {
return android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches() return android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches()
} }
} }

View File

@ -10,6 +10,7 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
@ -19,13 +20,24 @@ import com.example.androidlabs.DB.viewModels.OrderViewModel
import com.example.androidlabs.GlobalUser import com.example.androidlabs.GlobalUser
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.androidlabs.DB.viewModels.AppViewModelProvider
import kotlinx.coroutines.flow.first
@Composable @Composable
fun MyOrderScreen(orderViewModel: OrderViewModel) { fun MyOrderScreen(orderViewModel: OrderViewModel= viewModel(factory = AppViewModelProvider.Factory)) {
// val userWithOrder by orderViewModel.database.userDao().getUserOrders(GlobalUser.getInstance().getUser()?.userId!!).collectAsState(null) // val userWithOrder by orderViewModel.database.userDao().getUserOrders(GlobalUser.getInstance().getUser()?.userId!!).collectAsState(null)
val userId = GlobalUser.getInstance().getUser()?.userId val userId = GlobalUser.getInstance().getUser()?.userId
val userWithOrder = orderViewModel.getOrderList(userId!!).collectAsState(null).value?.orders var usersOrder by remember { mutableStateOf<List<Order>>(emptyList()) }
println() LaunchedEffect(userId) {
usersOrder = orderViewModel.getOrderList(userId!!).first()
}
Column( Column(
modifier = Modifier modifier = Modifier
.padding(bottom = 50.dp) .padding(bottom = 50.dp)
@ -46,9 +58,9 @@ fun MyOrderScreen(orderViewModel: OrderViewModel) {
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
) { ) {
if (userWithOrder != null) { if (usersOrder != null) {
for (item in userWithOrder) { for (item in usersOrder) {
OrderCard(item, orderViewModel) OrderCard(item)
} }
} }
} }

View File

@ -14,6 +14,10 @@ import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
@ -22,9 +26,11 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.androidlabs.DB.AppDatabase import com.example.androidlabs.DB.AppDatabase
import com.example.androidlabs.DB.models.Hotel import com.example.androidlabs.DB.models.Hotel
import com.example.androidlabs.DB.models.Order import com.example.androidlabs.DB.models.Order
import com.example.androidlabs.DB.viewModels.AppViewModelProvider
import com.example.androidlabs.DB.viewModels.OrderViewModel import com.example.androidlabs.DB.viewModels.OrderViewModel
import com.example.androidlabs.R import com.example.androidlabs.R
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -32,7 +38,11 @@ import kotlinx.coroutines.withContext
import java.util.Date import java.util.Date
@Composable @Composable
fun OrderCard(order: Order, orderViewModel: OrderViewModel){ fun OrderCard(order: Order, orderViewModel: OrderViewModel = viewModel(factory = AppViewModelProvider.Factory)){
var hotels by remember { mutableStateOf<List<Hotel>>(emptyList()) }
LaunchedEffect(order.orderId) {
hotels = listOf(orderViewModel.getHotelFromOrder(order.orderId!!))
}
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@ -53,17 +63,18 @@ fun OrderCard(order: Order, orderViewModel: OrderViewModel){
Row(){ Row(){
if (hotels != null) {
for(hotel in hotels) {
Image( Image(
contentScale = ContentScale.FillBounds, contentScale = ContentScale.FillBounds,
painter = painterResource(id =order.hotel.img), painter = painterResource(id = hotel.img), // TODO
contentDescription = null, contentDescription = null,
modifier = Modifier modifier = Modifier
.size(70.dp) .size(70.dp)
.padding(0.dp, 10.dp, 10.dp, 10.dp) .padding(0.dp, 10.dp, 10.dp, 10.dp)
) )
}
}
} }
Button( Button(
colors = ButtonDefaults.buttonColors( colors = ButtonDefaults.buttonColors(
@ -71,7 +82,7 @@ fun OrderCard(order: Order, orderViewModel: OrderViewModel){
contentColor = Color.White contentColor = Color.White
), ),
onClick = { onClick = {
orderViewModel.deleteOrder(order) orderViewModel.deleteOrder(order.orderId!!)
}, },
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()

View File

@ -24,7 +24,6 @@ import com.google.gson.Gson
@Composable @Composable
fun NavController(navController: NavHostController) { fun NavController(navController: NavHostController) {
var orderViewModel: OrderViewModel = viewModel(factory = AppViewModelProvider.Factory)
NavHost( NavHost(
navController = navController, navController = navController,
startDestination = NavItem.Home.route startDestination = NavItem.Home.route
@ -41,7 +40,7 @@ fun NavController(navController: NavHostController) {
HomeScreen(navController) HomeScreen(navController)
} }
composable(NavItem.MyOrder.route){ composable(NavItem.MyOrder.route){
MyOrderScreen(orderViewModel) MyOrderScreen()
} }
composable(NavItem.Profile.route) { composable(NavItem.Profile.route) {
ProfileScreen(navController) ProfileScreen(navController)
@ -56,7 +55,7 @@ fun NavController(navController: NavHostController) {
backStackEntry -> backStackEntry ->
val hotelItemString = backStackEntry.arguments?.getString("hotelItem") val hotelItemString = backStackEntry.arguments?.getString("hotelItem")
val hotelItem = Gson().fromJson(hotelItemString, Hotel::class.java) val hotelItem = Gson().fromJson(hotelItemString, Hotel::class.java)
hotelItem?.let { BookingScreen(orderViewModel, it, navController) hotelItem?.let { BookingScreen(it, navController)
} }
} }
composable(NavItem.ChangeHotel.route) { backStackEntry -> composable(NavItem.ChangeHotel.route) { backStackEntry ->

View File

@ -26,7 +26,7 @@ import com.example.androidlabs.profileScreen.signIn.LoginScreen
fun AdminPanel(navHostController: NavHostController) { fun AdminPanel(navHostController: NavHostController) {
var isAddPanelVisible by remember { mutableStateOf(false) } var isAddPanelVisible by remember { mutableStateOf(false) }
var isChangePanelVisible by remember { mutableStateOf(true) } var isChangePanelVisible by remember { mutableStateOf(true) }
var showDialog by remember { mutableStateOf(GlobalUser.getInstance().getUser()?.role == RoleEnum.User || GlobalUser.getInstance().getUser()?.role == null) } var showDialog by remember { mutableStateOf(GlobalUser.getInstance().getUser()?.role == "USER" || GlobalUser.getInstance().getUser()?.role == null) }
if (showDialog) { if (showDialog) {
AlertDialog( AlertDialog(
onDismissRequest = { showDialog = false }, onDismissRequest = { showDialog = false },

View File

@ -0,0 +1,105 @@
package com.example.androidlabs.api
import com.example.androidlabs.api.model.HotelRemote
import com.example.androidlabs.api.model.OrderRemote
import com.example.androidlabs.api.model.UserRemote
import com.example.androidlabs.api.model.UserRemoteSignIn
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
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 BackendService {
//SNEAKER
@GET("hotel/get/{id}")
suspend fun getHotel(
@Path("id") id: Int,
): HotelRemote
@GET("hotel/getAll")
suspend fun getHotels(
@Query("page") page: Int,
@Query("size") size: Int,
): List<HotelRemote>
@POST("hotel/create")
suspend fun createHotel(
@Body hotel: HotelRemote,
): HotelRemote
@PUT("hotel/update/{id}")
suspend fun updateHotel(
@Path("id") id: Int,
@Body hotel: HotelRemote
): HotelRemote
@DELETE("sneaker/delete/{id}")
suspend fun deleteHotel(
@Path("id") id: Int
)
//USER
@POST("user/signup")
suspend fun SignUp(
@Body user: UserRemote,
): UserRemote
@POST("user/signin")
suspend fun SignIn(
@Body user: UserRemoteSignIn
): UserRemote
@POST("order/create")
suspend fun createOrder(
@Body order: OrderRemote
): Long
@GET("order/getUserOrders/{userId}")
suspend fun getUserOrders(
@Path("userId") userId: Int
) : List<OrderRemote>
@GET("order/getHotelFromOrder/{orderId}")
suspend fun getHotelFromOrder(
@Path("orderId") orderId: Int
) : HotelRemote
@GET("order/deleteOrder/{orderId}")
suspend fun deleteOrder(
@Path("orderId") orderId: Int
)
companion object {
private const val BASE_URL = "https://59k4pfj3-8080.euw.devtunnels.ms/api/"
@Volatile
private var INSTANCE: BackendService? = null
fun getInstance(): BackendService {
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(BackendService::class.java)
.also { INSTANCE = it }
}
}
}
}

View File

@ -0,0 +1,104 @@
package com.example.androidlabs.api
import androidx.paging.ExperimentalPagingApi
import androidx.paging.LoadType
import androidx.paging.PagingState
import androidx.paging.RemoteMediator
import androidx.room.withTransaction
import com.example.androidlabs.DB.AppDatabase
import com.example.androidlabs.DB.models.Hotel
import com.example.androidlabs.DB.models.RemoteKeyType
import com.example.androidlabs.DB.models.RemoteKeys
import com.example.androidlabs.DB.repository.HotelRepImpl
import com.example.androidlabs.DB.repository.RemoteKeysRepositoryImpl
import com.example.androidlabs.api.model.toHotel
import retrofit2.HttpException
import java.io.IOException
@OptIn(ExperimentalPagingApi::class)
class HotelRemoteMediator(
private val service: BackendService,
private val hotelRepository: HotelRepImpl,
private val database: AppDatabase,
private val dbRemoteKeyRepository: RemoteKeysRepositoryImpl
) : RemoteMediator<Int, Hotel>() {
override suspend fun initialize(): InitializeAction {
return InitializeAction.LAUNCH_INITIAL_REFRESH
}
override suspend fun load(
loadType: LoadType,
state: PagingState<Int, Hotel>
): 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 sneakers = service.getHotels(page, state.config.pageSize).map { it.toHotel() }
val endOfPaginationReached = sneakers.isEmpty()
database.withTransaction {
if (loadType == LoadType.REFRESH) {
dbRemoteKeyRepository.deleteRemoteKey(RemoteKeyType.SNEAKER)
hotelRepository.clearHotels()
}
val prevKey = if (page == 1) null else page - 1
val nextKey = if (endOfPaginationReached) null else page + 1
val keys = sneakers.map {
RemoteKeys(
entityId = it.hotelId!!,
type = RemoteKeyType.SNEAKER,
prevKey = prevKey,
nextKey = nextKey
)
}
dbRemoteKeyRepository.createRemoteKeys(keys)
hotelRepository.insertHotels(sneakers)
}
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<Int, Hotel>): RemoteKeys? {
return state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull()
?.let { hotel ->
hotel.hotelId?.let { dbRemoteKeyRepository.getAllRemoteKeys(it, RemoteKeyType.SNEAKER) }
}
}
private suspend fun getRemoteKeyForFirstItem(state: PagingState<Int, Hotel>): RemoteKeys? {
return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
?.let { hotel ->
hotel.hotelId?.let { dbRemoteKeyRepository.getAllRemoteKeys(it, RemoteKeyType.SNEAKER) }
}
}
private suspend fun getRemoteKeyClosestToCurrentPosition(
state: PagingState<Int, Hotel>
): RemoteKeys? {
return state.anchorPosition?.let { position ->
state.closestItemToPosition(position)?.hotelId?.let { hotelUid ->
dbRemoteKeyRepository.getAllRemoteKeys(hotelUid, RemoteKeyType.SNEAKER)
}
}
}
}

View File

@ -0,0 +1,36 @@
package com.example.androidlabs.api.model
import com.example.androidlabs.DB.models.Hotel
import kotlinx.serialization.Serializable
@Serializable
data class HotelRemote (
val id: Int? = 0,
val name: String = "",
val price: Double = 0.0,
val img: Int = 0,
val stars: Int = 0,
val location: String = "",
val info: String = "",
)
fun HotelRemote.toHotel(): Hotel = Hotel(
id,
name,
price,
img,
stars,
location,
info
)
fun Hotel.toHotelRemote():HotelRemote = HotelRemote(
hotelId,
name,
price,
img,
stars,
location,
info
)

View File

@ -0,0 +1,39 @@
package com.example.androidlabs.api.model
import androidx.room.ColumnInfo
import androidx.room.PrimaryKey
import com.example.androidlabs.DB.models.Hotel
import com.example.androidlabs.DB.models.Order
import kotlinx.serialization.Serializable
@Serializable
data class OrderRemote(
val id: Int? = 0,
val dateFrom: String = "",
val dateTo: String = "",
val rooms: Int = 0,
val total: Double = 0.0,
val userId: Int = 0,
val bookedHotelId: Int = 0
)
fun OrderRemote.toOrder(): Order = Order(
id,
dateFrom,
dateTo,
rooms,
total,
userId,
bookedHotelId,
)
fun Order.toOrderRemote():OrderRemote = OrderRemote(
orderId,
dateFrom,
dateTo,
rooms,
total,
creatorUserId,
bookedHotelId,
)

View File

@ -0,0 +1,41 @@
package com.example.androidlabs.api.model
import androidx.room.ColumnInfo
import androidx.room.PrimaryKey
import com.example.androidlabs.DB.models.User
import com.example.androidlabs.DB.models.RoleEnum
import kotlinx.serialization.Serializable
@Serializable
data class UserRemote (
val id: Int? = 0,
val name: String = "",
val surname: String = "",
val email: String = "",
val password: String = "",
val role: String = "",
val photo: Int? = 0,
)
fun UserRemote.toUser(): User = User(
id,
name,
surname,
email,
password,
role,
photo
)
fun User.toUserRemote():UserRemote = UserRemote(
userId,
name,
surname,
email,
password,
role,
photo
)

View File

@ -0,0 +1,9 @@
package com.example.androidlabs.api.model
import kotlinx.serialization.Serializable
@Serializable
data class UserRemoteSignIn(
val email: String = "",
val password: String = "",
)

View File

@ -0,0 +1,60 @@
package com.example.androidlabs.api.repository
import androidx.paging.ExperimentalPagingApi
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.example.androidlabs.AppContainer
import com.example.androidlabs.DB.AppDatabase
import com.example.androidlabs.DB.models.Hotel
import com.example.androidlabs.DB.repository.HotelRepImpl
import com.example.androidlabs.DB.repository.HotelRepository
import com.example.androidlabs.DB.repository.RemoteKeysRepositoryImpl
import com.example.androidlabs.api.BackendService
import com.example.androidlabs.api.HotelRemoteMediator
import com.example.androidlabs.api.model.toHotel
import com.example.androidlabs.api.model.toHotelRemote
import kotlinx.coroutines.flow.Flow
class RestHotelRepository(
private val service: BackendService,
private val dbHotelRepository: HotelRepImpl,
private val database: AppDatabase,
private val dbRemoteKeyRepository: RemoteKeysRepositoryImpl
) : HotelRepository {
override fun getAllHotels(): Flow<PagingData<Hotel>> {
val pagingSourceFactory = { dbHotelRepository.getAllHotelsPagingSource() }
@OptIn(ExperimentalPagingApi::class)
return Pager(
config = PagingConfig(
pageSize = AppContainer.LIMIT,
enablePlaceholders = false
),
remoteMediator = HotelRemoteMediator(
service,
dbHotelRepository,
database,
dbRemoteKeyRepository,
),
pagingSourceFactory = pagingSourceFactory
).flow
}
override suspend fun getHotelById(id: Int): Hotel = service.getHotel(id).toHotel()
override suspend fun insertHotel(hotel: Hotel) {
service.createHotel(hotel.toHotelRemote())
}
override suspend fun updateHotel(hotel: Hotel) {
hotel.hotelId?.let { service.updateHotel(it, hotel.toHotelRemote()) }
}
override suspend fun deleteHotel(hotel: Hotel) {
hotel.hotelId?.let { service.deleteHotel(it) }
}
}

View File

@ -0,0 +1,32 @@
package com.example.androidlabs.api.repository
import com.example.androidlabs.DB.models.Hotel
import com.example.androidlabs.DB.models.Order
import com.example.androidlabs.DB.repository.OrderRepository
import com.example.androidlabs.api.BackendService
import com.example.androidlabs.api.model.toHotel
import com.example.androidlabs.api.model.toOrder
import com.example.androidlabs.api.model.toOrderRemote
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
class RestOrderRepository(private val service: BackendService): OrderRepository {
override suspend fun createOrder(order: Order): Long {
return service.createOrder(order.toOrderRemote())
}
override suspend fun delete(orderId: Int) {
service.deleteOrder(orderId)
}
override suspend fun getHotelFromOrder(id: Int): Hotel {
return service.getHotelFromOrder(id).toHotel()
}
override suspend fun getUserOrders(id: Int): Flow<List<Order>> {
val ordersRemoteList = service.getUserOrders(id)
val ordersList = ordersRemoteList.map { it.toOrder() }
return flowOf(ordersList.toList())
}
}

View File

@ -0,0 +1,25 @@
import com.example.androidlabs.DB.models.User
import com.example.androidlabs.DB.repository.UserRepository
import com.example.androidlabs.api.BackendService
import com.example.androidlabs.api.model.UserRemoteSignIn
import com.example.androidlabs.api.model.toUser
import com.example.androidlabs.api.model.toUserRemote
class RestUserRepository(
private var service: BackendService
): UserRepository {
override suspend fun createUser(user: User) {
service.SignUp(user.toUserRemote())
}
override suspend fun updateUser(user: User) {
println()
}
override suspend fun deleteUser(user: User) {
println()
}
override suspend fun authUser(user: UserRemoteSignIn): User {
return service.SignIn(user).toUser()
}
}

View File

@ -40,11 +40,13 @@ import com.example.androidlabs.R
import android.app.DatePickerDialog import android.app.DatePickerDialog
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.androidlabs.DB.viewModels.AppViewModelProvider
import java.util.Calendar import java.util.Calendar
import java.util.Date import java.util.Date
@Composable @Composable
fun BookingScreen(orderViewModel: OrderViewModel, hotel: Hotel, navHostController: NavHostController) { fun BookingScreen(hotel: Hotel, navHostController: NavHostController, orderViewModel: OrderViewModel = viewModel(factory = AppViewModelProvider.Factory)) {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()