diff --git a/compose/.gradle/8.0/executionHistory/executionHistory.bin b/compose/.gradle/8.0/executionHistory/executionHistory.bin index 91fe15c..67eba2a 100644 Binary files a/compose/.gradle/8.0/executionHistory/executionHistory.bin and b/compose/.gradle/8.0/executionHistory/executionHistory.bin differ diff --git a/compose/.gradle/8.0/executionHistory/executionHistory.lock b/compose/.gradle/8.0/executionHistory/executionHistory.lock index e0b3315..50757f9 100644 Binary files a/compose/.gradle/8.0/executionHistory/executionHistory.lock and b/compose/.gradle/8.0/executionHistory/executionHistory.lock differ diff --git a/compose/.gradle/8.0/fileHashes/fileHashes.bin b/compose/.gradle/8.0/fileHashes/fileHashes.bin index 206b3cc..f3e92c1 100644 Binary files a/compose/.gradle/8.0/fileHashes/fileHashes.bin and b/compose/.gradle/8.0/fileHashes/fileHashes.bin differ diff --git a/compose/.gradle/8.0/fileHashes/fileHashes.lock b/compose/.gradle/8.0/fileHashes/fileHashes.lock index 01a6d6a..b33bc8d 100644 Binary files a/compose/.gradle/8.0/fileHashes/fileHashes.lock and b/compose/.gradle/8.0/fileHashes/fileHashes.lock differ diff --git a/compose/.gradle/8.0/fileHashes/resourceHashesCache.bin b/compose/.gradle/8.0/fileHashes/resourceHashesCache.bin index 5c6347e..09e3077 100644 Binary files a/compose/.gradle/8.0/fileHashes/resourceHashesCache.bin and b/compose/.gradle/8.0/fileHashes/resourceHashesCache.bin differ diff --git a/compose/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/compose/.gradle/buildOutputCleanup/buildOutputCleanup.lock index cc1cf6f..9d44cfa 100644 Binary files a/compose/.gradle/buildOutputCleanup/buildOutputCleanup.lock and b/compose/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/compose/.gradle/buildOutputCleanup/outputFiles.bin b/compose/.gradle/buildOutputCleanup/outputFiles.bin index 967e65c..2eadbbb 100644 Binary files a/compose/.gradle/buildOutputCleanup/outputFiles.bin and b/compose/.gradle/buildOutputCleanup/outputFiles.bin differ diff --git a/compose/.gradle/file-system.probe b/compose/.gradle/file-system.probe index 14f5211..8323b79 100644 Binary files a/compose/.gradle/file-system.probe and b/compose/.gradle/file-system.probe differ diff --git a/compose/.idea/modules/app/pmu-demo.app.iml b/compose/.idea/modules/app/pmu-demo.app.iml deleted file mode 100644 index 349119f..0000000 --- a/compose/.idea/modules/app/pmu-demo.app.iml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/ApiRoutes.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/ApiRoutes.kt new file mode 100644 index 0000000..7594d0b --- /dev/null +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/ApiRoutes.kt @@ -0,0 +1,11 @@ +package ru.ulstu.`is`.pmu.tank.api + +object ApiRoutes { + const val BASE = "http://10.0.2.2:8079/" + const val USER = "users" + const val LEVEL = "level" + const val NATION = "nation" + const val TANK = "TANK" + const val USER_TANK = "userTanks" + const val NOT_USER_TANK = "notUserTanks" +} \ No newline at end of file diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/ServerService.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/ServerService.kt new file mode 100644 index 0000000..6fa34cd --- /dev/null +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/ServerService.kt @@ -0,0 +1,184 @@ +package ru.ulstu.`is`.pmu.tank.api + +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 +import ru.ulstu.`is`.pmu.tank.api.model.LevelRemote +import ru.ulstu.`is`.pmu.tank.api.model.NationRemote +import ru.ulstu.`is`.pmu.tank.api.model.TankRemote +import ru.ulstu.`is`.pmu.tank.api.model.TankWithNationAndLevelRemote +import ru.ulstu.`is`.pmu.tank.api.model.UserRemote +import ru.ulstu.`is`.pmu.tank.api.model.UserTankCrossRefRemote + +interface ServerService { + // :[USER] + + @GET(ApiRoutes.USER) + suspend fun getUserByEmail( + @Query("email") email: String + ): UserRemote + + @POST(ApiRoutes.USER) + suspend fun insertUser( + @Body user: UserRemote + ): UserRemote + + @PUT("${ApiRoutes.USER}/{id}") + suspend fun updateUser( + @Path("id") id: Long, + @Body user: UserRemote + ): UserRemote + + + // ![USER] + + // :[LEVEL] + + @GET(ApiRoutes.LEVEL) + suspend fun getLevels(): List + + @GET("${ApiRoutes.LEVEL}/{id}") + suspend fun getLevel( + @Path("id") id: Long + ): LevelRemote + + @POST(ApiRoutes.LEVEL) + suspend fun insertLevel( + @Body level: LevelRemote + ): LevelRemote + + @PUT("${ApiRoutes.LEVEL}/{id}") + suspend fun updateLevel( + @Path("id") id: Long, + @Body level: LevelRemote + ): LevelRemote + + @DELETE("${ApiRoutes.LEVEL}/{id}") + suspend fun deleteLevel( + @Path("id") id: Long + ): LevelRemote + + // ![LEVEL] + + // :[NATION] + + @GET(ApiRoutes.NATION) + suspend fun getNations( + @Query("_page") page: Int, + @Query("_limit") limit: Int + ): List + + @GET("${ApiRoutes.NATION}/{id}") + suspend fun getNation( + @Path("id") id: Long + ): NationRemote + + @POST(ApiRoutes.NATION) + suspend fun insertNation( + @Body product: NationRemote + ): NationRemote + + @PUT("${ApiRoutes.NATION}/{id}") + suspend fun updateNation( + @Path("id") id: Long, + @Body product: NationRemote + ): NationRemote + + @DELETE("${ApiRoutes.NATION}/{id}") + suspend fun deleteNation( + @Path("id") id: Long + ): NationRemote + + // ![NATION] + + // :[TANK] + + @GET(ApiRoutes.TANK) + suspend fun getTanks(): List + + @GET("${ApiRoutes.TANK}/{id}") + suspend fun getTank( + @Path("id") id: Long + ): TankRemote + + @GET("${ApiRoutes.TANK}/forPurchase") + suspend fun getTanksForPurchase( + @Path("id") userId: Long + ): List + + @GET("${ApiRoutes.TANK}/myHangar") + suspend fun getTanksFromHangar( + @Path("id") userId: Long + ): List + + @POST(ApiRoutes.TANK) + suspend fun insertTank( + @Body tank: TankRemote + ): TankRemote + + @PUT("${ApiRoutes.TANK}/{id}") + suspend fun updateTank( + @Path("id") id: Long, + @Body tank: TankRemote + ): TankRemote + + @DELETE("${ApiRoutes.TANK}/{id}") + suspend fun deleteTank( + @Path("id") id: Long + ): TankRemote + + // ![TANK] + + // :[USER_TANK_CROSS_REF] + + @GET("${ApiRoutes.USER_TANK}?_hangar={userId}") + suspend fun getUserTankCrossRef( + @Path("userId") userId: Long, + ): List + + @POST(ApiRoutes.USER_TANK) + suspend fun insertUserTankCrossRef( + @Body tank: UserTankCrossRefRemote + ) : UserTankCrossRefRemote + + @DELETE("${ApiRoutes.USER_TANK}/deleteMyTank") + suspend fun deleteUserTankCrossRef( + @Body tank: UserTankCrossRefRemote + ): UserTankCrossRefRemote + + // ![USER_TANK_CROSS_REF] + + companion object { + private const val BASE_URL = ApiRoutes.BASE + + @Volatile + private var INSTANCE: ServerService? = null + + fun getInstance(): ServerService { + 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(ServerService::class.java) + .also { INSTANCE = it } + } + } + } +} \ No newline at end of file diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/mediator/NationRemoteMediator.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/mediator/NationRemoteMediator.kt new file mode 100644 index 0000000..2191b48 --- /dev/null +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/mediator/NationRemoteMediator.kt @@ -0,0 +1,111 @@ +package ru.ulstu.`is`.pmu.tank.api.mediator + +import androidx.paging.ExperimentalPagingApi +import androidx.paging.LoadType +import androidx.paging.PagingState +import androidx.paging.RemoteMediator +import androidx.room.withTransaction +import retrofit2.HttpException +import ru.ulstu.`is`.pmu.tank.api.ServerService +import ru.ulstu.`is`.pmu.tank.api.model.toLevel +import ru.ulstu.`is`.pmu.tank.api.model.toNation +import ru.ulstu.`is`.pmu.tank.database.AppDatabase +import ru.ulstu.`is`.pmu.tank.model.Nation +import ru.ulstu.`is`.pmu.tank.model.RemoteKeyType +import ru.ulstu.`is`.pmu.tank.model.RemoteKeys +import ru.ulstu.`is`.pmu.tank.repository.OfflineNationRepository +import ru.ulstu.`is`.pmu.tank.repository.OfflineRemoteKeyRepository +import java.io.IOException + +@OptIn(ExperimentalPagingApi::class) +class NationRemoteMediator( + private val service: ServerService, + private val dbNationRepository: OfflineNationRepository, + private val dbRemoteKeyRepository: OfflineRemoteKeyRepository, + private val database: AppDatabase +) : RemoteMediator() { + + override suspend fun initialize(): InitializeAction { + return InitializeAction.LAUNCH_INITIAL_REFRESH + } + + override suspend fun load( + loadType: LoadType, + state: PagingState + ): MediatorResult { + val page = when (loadType) { + LoadType.REFRESH -> { + val remoteKeys = getRemoteKeyClosestToCurrentPosition(state) + remoteKeys?.nextKey?.minus(1) ?: 1 + } + + LoadType.PREPEND -> { + val remoteKeys = getRemoteKeyForFirstItem(state) + remoteKeys?.prevKey + ?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null) + } + + LoadType.APPEND -> { + val remoteKeys = getRemoteKeyForLastItem(state) + remoteKeys?.nextKey + ?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null) + } + } + + try { + val nations = service.getNations(page, state.config.pageSize).map { it.toNation() } + val endOfPaginationReached = nations.isEmpty() + + database.withTransaction { + if (loadType == LoadType.REFRESH) { + dbRemoteKeyRepository.deleteRemoteKey(RemoteKeyType.NATION) + dbNationRepository.deleteNations() + } + + val prevKey = if (page == 1) null else page - 1 + val nextKey = if (endOfPaginationReached) null else page + 1 + val keys = nations.map { + RemoteKeys( + entityId = it.uid!!.toInt(), + type = RemoteKeyType.NATION, + prevKey = prevKey, + nextKey = nextKey + ) + } + + dbRemoteKeyRepository.createRemoteKeys(keys) + dbNationRepository.insertNations(nations) + } + return MediatorResult.Success(endOfPaginationReached = endOfPaginationReached) + } catch (exception: IOException) { + return MediatorResult.Error(exception) + } catch (exception: HttpException) { + return MediatorResult.Error(exception) + } + } + + private suspend fun getRemoteKeyForLastItem(state: PagingState): RemoteKeys? { + return state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull() + ?.let { nation -> + dbRemoteKeyRepository.getAllRemoteKeys(nation.uid!!.toInt(), RemoteKeyType.NATION) + } + } + + private suspend fun getRemoteKeyForFirstItem(state: PagingState): RemoteKeys? { + return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull() + ?.let { nation -> + dbRemoteKeyRepository.getAllRemoteKeys(nation.uid!!.toInt(), RemoteKeyType.NATION) + } + } + + private suspend fun getRemoteKeyClosestToCurrentPosition( + state: PagingState + ): RemoteKeys? { + return state.anchorPosition?.let { position -> + state.closestItemToPosition(position)?.uid?.let { nationUid -> + dbRemoteKeyRepository.getAllRemoteKeys(nationUid.toInt(), RemoteKeyType.NATION) + } + } + } + +} \ No newline at end of file diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/model/LevelRemote.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/model/LevelRemote.kt new file mode 100644 index 0000000..c5cd2c3 --- /dev/null +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/model/LevelRemote.kt @@ -0,0 +1,22 @@ +package ru.ulstu.`is`.pmu.tank.api.model + +import androidx.room.ColumnInfo +import kotlinx.serialization.Serializable +import ru.ulstu.`is`.pmu.tank.model.Level +import ru.ulstu.`is`.pmu.tank.model.Nation + +@Serializable +data class LevelRemote ( + val uid: Long = 0, + val level: Int = 0 +) + +fun LevelRemote.toLevel(): Level = Level( + uid, + level +) + +fun Level.toRemote(): LevelRemote = LevelRemote( + uid!!, + level +) \ No newline at end of file diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/model/NationRemote.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/model/NationRemote.kt new file mode 100644 index 0000000..7bc7271 --- /dev/null +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/model/NationRemote.kt @@ -0,0 +1,20 @@ +package ru.ulstu.`is`.pmu.tank.api.model + +import kotlinx.serialization.Serializable +import ru.ulstu.`is`.pmu.tank.model.Nation + +@Serializable +data class NationRemote( + val uid: Long = 0, + val nationName: String = "" +) + +fun NationRemote.toNation(): Nation = Nation( + uid, + nationName +) + +fun Nation.toRemote(): NationRemote = NationRemote( + uid!!, + nationName +) diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/model/TankRemote.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/model/TankRemote.kt new file mode 100644 index 0000000..992c151 --- /dev/null +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/model/TankRemote.kt @@ -0,0 +1,38 @@ +package ru.ulstu.`is`.pmu.tank.api.model + +import android.graphics.Bitmap +import com.application.ui.toBase64 +import com.application.ui.toBitmap +import kotlinx.serialization.Serializable +import ru.ulstu.`is`.pmu.tank.model.Tank + +@Serializable +data class TankRemote( + val tankId: Long = 0, + val name: String = "", + val price: Int = 0, + val miniature: String = "", + val imageId: Long = 0, + val levelId: Long = 0, + val nationId: Long = 0, +) + +fun TankRemote.toTank(): Tank = Tank( + tankId = tankId, + name = name, + price = price, + miniature = miniature.toBitmap(), + imageId = imageId, + levelId = levelId, + nationId = nationId +) + +fun Tank.toRemote(): TankRemote = TankRemote( + tankId = tankId!!, + name = name, + price = price, + miniature = miniature.toBase64(), + imageId = imageId, + levelId = levelId!!, + nationId = nationId!! +) diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/model/TankWithNationAndLevelRemote.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/model/TankWithNationAndLevelRemote.kt new file mode 100644 index 0000000..5f6fd73 --- /dev/null +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/model/TankWithNationAndLevelRemote.kt @@ -0,0 +1,34 @@ +package ru.ulstu.`is`.pmu.tank.api.model + +import com.application.ui.toBase64 +import com.application.ui.toBitmap +import kotlinx.serialization.Serializable +import ru.ulstu.`is`.pmu.tank.model.TankWithNationAndLevel + +@Serializable +data class TankWithNationAndLevelRemote( + val tankId: Long, + val name: String, + val price: Int, + val image: String, + val level: Int, + val nationName: String +) + +fun TankWithNationAndLevelRemote.toTankWithNationAndLevel(): TankWithNationAndLevel = TankWithNationAndLevel( + tankId = tankId, + name = name, + price = price, + image = image.toBitmap(), + level = level, + nationName = nationName +) + +fun TankWithNationAndLevel.toRemote(): TankWithNationAndLevelRemote = TankWithNationAndLevelRemote( + tankId = tankId!!, + name = name, + price = price, + image = image.toBase64(), + level = level, + nationName = nationName +) diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/model/UserRemote.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/model/UserRemote.kt new file mode 100644 index 0000000..9e8feb6 --- /dev/null +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/model/UserRemote.kt @@ -0,0 +1,33 @@ +package ru.ulstu.`is`.pmu.tank.api.model + +import kotlinx.serialization.Serializable +import ru.ulstu.`is`.pmu.tank.model.User +import ru.ulstu.`is`.pmu.tank.model.UserRole + +@Serializable +data class UserRemote ( + val id: Long = 0, + val nickname: String = "", + val email: String = " ", + val role: Int = -1, + val password: String = "", + val balance: Int = 0 +) + +fun UserRemote.toUser(): User = User( + userId = id, + nickname = nickname, + email = email, + password = password, + role = enumValues()[role], + balance = balance +) + +fun User.toRemote(): UserRemote = UserRemote( + id = userId, + nickname = nickname, + email = email, + password = password, + role = role.ordinal, + balance = balance +) \ No newline at end of file diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/model/UserTankCrossRefRemote.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/model/UserTankCrossRefRemote.kt new file mode 100644 index 0000000..10ee79a --- /dev/null +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/model/UserTankCrossRefRemote.kt @@ -0,0 +1,9 @@ +package ru.ulstu.`is`.pmu.tank.api.model + +import kotlinx.serialization.Serializable + +@Serializable +data class UserTankCrossRefRemote ( + val userId: Long = 0, + val tankId: Long = 0 +) \ No newline at end of file diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/repository/RestLevelRepository.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/repository/RestLevelRepository.kt new file mode 100644 index 0000000..af69586 --- /dev/null +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/repository/RestLevelRepository.kt @@ -0,0 +1,51 @@ +package ru.ulstu.`is`.pmu.tank.api.repository + +import kotlinx.coroutines.flow.Flow +import ru.ulstu.`is`.pmu.tank.api.ServerService +import ru.ulstu.`is`.pmu.tank.api.model.toLevel +import ru.ulstu.`is`.pmu.tank.api.model.toRemote +import ru.ulstu.`is`.pmu.tank.model.Level +import ru.ulstu.`is`.pmu.tank.model.LevelWithTanks +import ru.ulstu.`is`.pmu.tank.repository.LevelRepository +import ru.ulstu.`is`.pmu.tank.repository.OfflineLevelRepository + +class RestLevelRepository( + private val service: ServerService, + private val dbLevelRepository: OfflineLevelRepository, +) : LevelRepository { + override suspend fun getAllLevels(): List { + dbLevelRepository.deleteAll() + val levels = service.getLevels().map { it.toLevel() } + dbLevelRepository.insertMany(levels) + return levels + } + + override suspend fun getSimpleLevel(uid: Long): Level { + TODO("Not yet implemented") + } + + override fun getFullLevel(uid: Long): Flow { + TODO("Not yet implemented") + } + + override suspend fun insertLevel(level: Level) { + service.insertLevel(level.toRemote()) + } + + override suspend fun insertMany(levels: List) { + TODO("Not yet implemented") + } + + override suspend fun updateLevel(level: Level) { + service.updateLevel(level.uid!!, level.toRemote()) + } + + override suspend fun deleteLevel(level: Level) { + service.deleteLevel(level.uid!!) + dbLevelRepository.deleteLevel(level) + } + + override suspend fun deleteAll() { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/repository/RestNationRepository.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/repository/RestNationRepository.kt new file mode 100644 index 0000000..5b0f4ee --- /dev/null +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/repository/RestNationRepository.kt @@ -0,0 +1,84 @@ +package ru.ulstu.`is`.pmu.tank.api.repository + +import android.util.Log +import androidx.paging.ExperimentalPagingApi +import androidx.paging.Pager +import androidx.paging.PagingConfig +import androidx.paging.PagingData +import androidx.paging.PagingSource +import androidx.paging.map +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import ru.ulstu.`is`.pmu.tank.api.ServerService +import ru.ulstu.`is`.pmu.tank.api.mediator.NationRemoteMediator +import ru.ulstu.`is`.pmu.tank.api.model.NationRemote +import ru.ulstu.`is`.pmu.tank.api.model.toNation +import ru.ulstu.`is`.pmu.tank.api.model.toRemote +import ru.ulstu.`is`.pmu.tank.database.AppContainer +import ru.ulstu.`is`.pmu.tank.database.AppDatabase +import ru.ulstu.`is`.pmu.tank.model.Nation +import ru.ulstu.`is`.pmu.tank.model.NationWithTanks +import ru.ulstu.`is`.pmu.tank.repository.NationRepository +import ru.ulstu.`is`.pmu.tank.repository.OfflineNationRepository +import ru.ulstu.`is`.pmu.tank.repository.OfflineRemoteKeyRepository + +class RestNationRepository( + private val service: ServerService, + private val dbNationRepository: OfflineNationRepository, + private val dbRemoteKeyRepository: OfflineRemoteKeyRepository, + private val database: AppDatabase +) : NationRepository { + override suspend fun getAllNations(): List { + TODO("Not yet implemented") + } + + override suspend fun getAll(): Flow> { + @OptIn(ExperimentalPagingApi::class) + return Pager( + config = PagingConfig( + pageSize = 10, + enablePlaceholders = false + ), + remoteMediator = NationRemoteMediator( + service = service, + dbNationRepository = dbNationRepository, + dbRemoteKeyRepository = dbRemoteKeyRepository, + database = database, + ), + ) { + dbNationRepository.pagingSource() + }.flow + } + + override fun getSimpleNation(uid: Long): Flow { + TODO("Not yet implemented") + } + + override fun getFullNation(uid: Long): Flow { + TODO("Not yet implemented") + } + + override fun pagingSource(): PagingSource { + TODO("Not yet implemented") + } + + override suspend fun insertNation(nation: Nation) { + service.insertNation(nation.toRemote()).toNation() + } + + override suspend fun insertNations(nations: List) { + TODO("Not yet implemented") + } + + override suspend fun updateNation(nation: Nation) { + service.updateNation(nation.uid!!, nation.toRemote()) + } + + override suspend fun deleteNation(nation: Nation) { + service.deleteNation(nation.uid!!) + } + + override suspend fun deleteNations() { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/repository/RestTankReppository.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/repository/RestTankReppository.kt new file mode 100644 index 0000000..3f7b5ec --- /dev/null +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/repository/RestTankReppository.kt @@ -0,0 +1,66 @@ +package ru.ulstu.`is`.pmu.tank.api.repository + +import android.graphics.Bitmap +import kotlinx.coroutines.flow.Flow +import ru.ulstu.`is`.pmu.tank.api.ServerService +import ru.ulstu.`is`.pmu.tank.api.model.toLevel +import ru.ulstu.`is`.pmu.tank.api.model.toRemote +import ru.ulstu.`is`.pmu.tank.api.model.toTank +import ru.ulstu.`is`.pmu.tank.api.model.toTankWithNationAndLevel +import ru.ulstu.`is`.pmu.tank.api.model.toUser +import ru.ulstu.`is`.pmu.tank.model.Tank +import ru.ulstu.`is`.pmu.tank.model.TankWithNationAndLevel +import ru.ulstu.`is`.pmu.tank.model.User +import ru.ulstu.`is`.pmu.tank.repository.OfflineTankRepository +import ru.ulstu.`is`.pmu.tank.repository.OfflineUserRepository +import ru.ulstu.`is`.pmu.tank.repository.TankRepository +import ru.ulstu.`is`.pmu.tank.repository.UserRepository + +class RestTankReppository ( + private val service: ServerService, + private val dbTankRepository: OfflineTankRepository, +) : TankRepository { + override suspend fun getAll(): List { + dbTankRepository.deleteAll() + val tanks = service.getTanks().map { it.toTank() } + dbTankRepository.insertMany(tanks) + return tanks + } + + override suspend fun getForUserAll(userId: Long): List { + return service.getTanksForPurchase(userId).map { it.toTank() } + } + + override suspend fun getTank(uid: Long): Tank { + return service.getTank(uid).toTank() + } + + override suspend fun getUserTanks(uid: Long): List { + return service.getTanksFromHangar(uid).map { it.toTankWithNationAndLevel() } + } + + override suspend fun insertTank(tank: Tank, image: Bitmap) { + service.insertTank(tank.toRemote()) + } + + override suspend fun insertMany(tankList: List) { + TODO("Not yet implemented") + } + + override suspend fun updateTank(tank: Tank, image: Bitmap) { + service.updateTank(tank.tankId!!, tank.toRemote()) + } + + override suspend fun deleteTank(tank: Tank) { + TODO("Not yet implemented") + } + + override suspend fun delete(tankId: Long) { + service.deleteTank(tankId) + dbTankRepository.delete(tankId) + } + + override suspend fun deleteAll() { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/repository/RestUserRepository.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/repository/RestUserRepository.kt new file mode 100644 index 0000000..12f376e --- /dev/null +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/api/repository/RestUserRepository.kt @@ -0,0 +1,44 @@ +package ru.ulstu.`is`.pmu.tank.api.repository + +import kotlinx.coroutines.flow.Flow +import ru.ulstu.`is`.pmu.tank.api.ServerService +import ru.ulstu.`is`.pmu.tank.api.model.toLevel +import ru.ulstu.`is`.pmu.tank.api.model.toRemote +import ru.ulstu.`is`.pmu.tank.api.model.toUser +import ru.ulstu.`is`.pmu.tank.model.Level +import ru.ulstu.`is`.pmu.tank.model.LevelWithTanks +import ru.ulstu.`is`.pmu.tank.model.TankWithNationAndLevel +import ru.ulstu.`is`.pmu.tank.model.User +import ru.ulstu.`is`.pmu.tank.repository.LevelRepository +import ru.ulstu.`is`.pmu.tank.repository.OfflineLevelRepository +import ru.ulstu.`is`.pmu.tank.repository.OfflineUserRepository +import ru.ulstu.`is`.pmu.tank.repository.UserRepository + +class RestUserRepository( + private val service: ServerService, + private val dbUserRepository: OfflineUserRepository, +) : UserRepository { + override fun getAllUsers(): Flow> { + TODO("Not yet implemented") + } + + override suspend fun getSimpleUser(email: String): User { + return service.getUserByEmail(email).toUser() + } + + override fun getFullUser(uid: Long): Flow>> { + TODO("Not yet implemented") + } + + override suspend fun insertUser(user: User) { + service.insertUser(user.toRemote()) + } + + override suspend fun updateUser(user: User) { + service.updateUser(user.userId, user.toRemote()) + } + + override suspend fun deleteUser(user: User) { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/composeui/TankList.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/composeui/TankList.kt index 5363342..f296e13 100644 --- a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/composeui/TankList.kt +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/composeui/TankList.kt @@ -73,13 +73,11 @@ fun TankList( viewModel: TankListViewModel = viewModel(factory = AppViewModelProvider.Factory), listNations: NationDropDownViewModel = viewModel(factory = AppViewModelProvider.Factory) ) { - val tankListUiState by viewModel.tankListUiState.collectAsState() - // Lazy Column, Pass the numbers array if (navController != null) { TankList( nations = listNations.nationsListUiState, - listTanks = tankListUiState.tankList + listTanks = viewModel.tankListUiState.tankList ) { uid: Long -> val route = Screen.Constructor.route.replace("{id}", uid.toString()) navController.navigate(route) diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/composeui/edit/TankEditViewModel.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/composeui/edit/TankEditViewModel.kt index 72ea040..79b4ed1 100644 --- a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/composeui/edit/TankEditViewModel.kt +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/composeui/edit/TankEditViewModel.kt @@ -33,8 +33,6 @@ class TankEditViewModel( viewModelScope.launch { if (tankUid > 0) { tankUiState = tankRepository.getTank(tankUid) - .filterNotNull() - .first() .toUiState(true) } } diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/composeui/edit/UserEditViewModel.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/composeui/edit/UserEditViewModel.kt index bc01869..b586a00 100644 --- a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/composeui/edit/UserEditViewModel.kt +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/composeui/edit/UserEditViewModel.kt @@ -10,6 +10,7 @@ import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import ru.ulstu.`is`.pmu.tank.model.User +import ru.ulstu.`is`.pmu.tank.model.UserRole import ru.ulstu.`is`.pmu.tank.repository.UserRepository class UserEditViewModel( @@ -22,14 +23,12 @@ class UserEditViewModel( //private val userUid: Long = checkNotNull(savedStateHandle["id"]) - private val userUid: Long = 100L + private val userEmail: String = "egor@mail.ru" init { viewModelScope.launch { - if (userUid > 0) { - userUiState = userRepository.getSimpleUser(userUid) - .filterNotNull() - .first() + if (userEmail.length > 0) { + userUiState = userRepository.getSimpleUser(userEmail) .toUiState(true) } } @@ -44,8 +43,8 @@ class UserEditViewModel( suspend fun saveUser() { if (validateInput()) { - if (userUid > 0) { - userRepository.updateUser(userUiState.userDetails.toUser(userUid)) + if (userEmail.length > 0) { + userRepository.updateUser(userUiState.userDetails.toUser(100L)) } else { userRepository.insertUser(userUiState.userDetails.toUser()) } @@ -57,6 +56,7 @@ class UserEditViewModel( nickname.isNotBlank() && email.isNotBlank() && password.isNotBlank() + && role.value > -1 && balance > 0 } } @@ -71,6 +71,7 @@ data class UserDetails( val nickname: String = "", val email: String = "", val password: String = "", + val role: UserRole = UserRole.USER, val balance: Int = 0, ) @@ -79,6 +80,7 @@ fun UserDetails.toUser(uid: Long = 0): User = User( nickname = nickname, email = email, password = password, + role = role, balance = balance ) @@ -86,6 +88,7 @@ fun User.toDetails(): UserDetails = UserDetails( nickname = nickname, email = email, password = password, + role = role, balance = balance ) diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/composeui/list/TankListViewModel.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/composeui/list/TankListViewModel.kt index 10ff01e..3380ea4 100644 --- a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/composeui/list/TankListViewModel.kt +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/composeui/list/TankListViewModel.kt @@ -1,5 +1,8 @@ package ru.ulstu.`is`.pmu.tank.composeui.list +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.flow.SharingStarted @@ -15,21 +18,11 @@ class TankListViewModel( private val tankRepository: TankRepository, private var userId: Long = 100L ) : ViewModel() { - val tankListUiState: StateFlow = tankRepository.getForUserAll(userId).map { - TankListUiState(it) - }.stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(stopTimeoutMillis = AppDataContainer.TIMEOUT), - initialValue = TankListUiState() - ) + var tankListUiState by mutableStateOf(TankListUiState()) + private set - val usersTanksUiState: StateFlow = tankRepository.getUserTanks(userId).map { - UserTankListUiState(it) - }.stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(stopTimeoutMillis = AppDataContainer.TIMEOUT), - initialValue = UserTankListUiState() - ) + var usersTanksUiState by mutableStateOf(UserTankListUiState()) + private set suspend fun deleteTank(tank: Tank) { tankRepository.deleteTank(tank) diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/dao/LevelDao.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/dao/LevelDao.kt index 2657441..1427d7e 100644 --- a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/dao/LevelDao.kt +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/dao/LevelDao.kt @@ -30,14 +30,21 @@ interface LevelDao { @Query( "SELECT level FROM levels where levels.uid = :uid" ) - open fun getSimpleLevelUid(uid: Long): Flow + open fun getSimpleLevelUid(uid: Long): Level @Insert suspend fun insert(level: Level) + @Insert + suspend fun insertMany(level: List) + + @Update suspend fun update(level: Level) @Delete suspend fun delete(level: Level) + + @Query("DELETE FROM levels") + suspend fun deleteAll() } \ No newline at end of file diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/dao/NationDao.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/dao/NationDao.kt index c00cc10..9a7c201 100644 --- a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/dao/NationDao.kt +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/dao/NationDao.kt @@ -23,24 +23,33 @@ interface NationDao { suspend fun getNationsWithTanks(): List //получить конкретную нацию + @Transaction @Query("select * from nations where nations.uid = :uid") fun getNationUid(uid: Long): Flow //получить нацию без списка танков + @Transaction @Query( "SELECT nationName FROM nations where nations.uid = :uid" ) open fun getSimpleNationUid(uid: Long): Flow + @Transaction @Query("SELECT * FROM nations") fun pagingSource(): PagingSource @Insert suspend fun insert(nation: Nation) + @Insert + suspend fun insertMany(nations: List) + @Update suspend fun update(nation: Nation) @Delete suspend fun delete(nation: Nation) + + @Query("DELETE FROM nations") + suspend fun deleteAll() } diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/dao/RemoteKeysDao.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/dao/RemoteKeysDao.kt new file mode 100644 index 0000000..e45a080 --- /dev/null +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/dao/RemoteKeysDao.kt @@ -0,0 +1,20 @@ +package ru.ulstu.`is`.pmu.tank.dao + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import ru.ulstu.`is`.pmu.tank.model.RemoteKeyType +import ru.ulstu.`is`.pmu.tank.model.RemoteKeys + +@Dao +interface RemoteKeysDao { + @Query("SELECT * FROM remote_keys WHERE entityId = :entityId AND type = :type") + suspend fun getRemoteKeys(entityId: Int, type: RemoteKeyType): RemoteKeys? + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertAll(remoteKey: List) + + @Query("DELETE FROM remote_keys WHERE type = :type") + suspend fun clearRemoteKeys(type: RemoteKeyType) +} diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/dao/TankDao.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/dao/TankDao.kt index ccc5449..a02b0ea 100644 --- a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/dao/TankDao.kt +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/dao/TankDao.kt @@ -16,33 +16,36 @@ import ru.ulstu.`is`.pmu.tank.model.TankWithNationAndLevel @Dao interface TankDao { @Query("select * from tanks GROUP BY nationId, levelId ORDER BY nationId") - fun getAll(): Flow> + fun getAll(): List //получить конкретный танк @Query("select t.*, ti.data from tanks AS t " + "LEFT JOIN tank_images as ti on t.image_id = ti.image_id " + "where t.tankId = :uid") - fun getTankUid(uid: Long): Flow + fun getTankUid(uid: Long): Tank //получаем все танки пользователя по его Id @Query( - "SELECT t.tankId, t.name, t.price, t.image_id, l.level, n.nationName, ti.data AS image FROM UserTankCrossRef AS ut " + + "SELECT t.tankId, t.name, t.price, t.image_id, l.level, n.nationName, ti.data AS image FROM users_tanks AS ut " + "LEFT JOIN tanks as t on ut.tankId = t.tankId " + "LEFT JOIN tank_images as ti on t.image_id = ti.image_id " + "LEFT JOIN levels as l on t.levelId = l.uid " + "LEFT JOIN nations as n on t.nationId = n.uid " + "WHERE ut.userId = :uid GROUP BY t.nationId, t.levelId ORDER BY t.nationId" ) - fun getUserTanks(uid: Long): Flow> + fun getUserTanks(uid: Long): List @Query( - "SELECT t.* FROM tanks AS t WHERE t.tankId NOT IN (SELECT ut.tankId FROM UserTankCrossRef AS ut WHERE ut.userId = :uid)" + "SELECT t.* FROM tanks AS t WHERE t.tankId NOT IN (SELECT ut.tankId FROM users_tanks AS ut WHERE ut.userId = :uid)" ) - fun getNotUserTank(uid: Long): Flow> + fun getNotUserTank(uid: Long): List @Insert suspend fun insert(tank: Tank) + @Insert + suspend fun insertMany(tankList: List) + @Update suspend fun update(tank: Tank) @@ -51,4 +54,7 @@ interface TankDao { @Query("DELETE FROM tanks WHERE tankId = :id") suspend fun delete(id: Long) + + @Query("DELETE FROM tanks") + suspend fun deleteAll() } \ No newline at end of file diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/dao/UserDao.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/dao/UserDao.kt index c37cd8e..708a448 100644 --- a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/dao/UserDao.kt +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/dao/UserDao.kt @@ -11,7 +11,6 @@ import ru.ulstu.`is`.pmu.tank.model.Tank import ru.ulstu.`is`.pmu.tank.model.TankWithNationAndLevel import ru.ulstu.`is`.pmu.tank.model.User import ru.ulstu.`is`.pmu.tank.model.UserTankCrossRef -import ru.ulstu.`is`.pmu.tank.model.UserWithTanks @Dao interface UserDao { @@ -21,7 +20,7 @@ interface UserDao { //получить конкретного пользователя @Query( "SELECT u.*, t.*, l.level, n.nationName, ti.data AS image FROM users AS u " + - "LEFT JOIN UserTankCrossRef as ut on u.userId = ut.userId " + + "LEFT JOIN users_tanks as ut on u.userId = ut.userId " + "LEFT JOIN tanks as t on ut.tankId = t.tankId " + "LEFT JOIN tank_images as ti on ut.tankId = ti.image_id " + "LEFT JOIN levels as l on t.levelId = l.uid " + @@ -30,8 +29,8 @@ interface UserDao { ) fun getUserUid(uid: Long): Flow>> - @Query("select * from users where users.userId = :uid") - fun getSimpleUserUid(uid: Long): Flow + @Query("select * from users where users.email = :email") + fun getSimpleUserUid(email: String): User //добавить танк в ангар пользователя @Insert diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/database/AppDatabase.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/database/AppDatabase.kt index 953fe39..40cf4a6 100644 --- a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/database/AppDatabase.kt +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/database/AppDatabase.kt @@ -20,8 +20,8 @@ import ru.ulstu.`is`.pmu.tank.model.Nation import ru.ulstu.`is`.pmu.tank.model.Tank import ru.ulstu.`is`.pmu.tank.model.TankImage import ru.ulstu.`is`.pmu.tank.model.User +import ru.ulstu.`is`.pmu.tank.model.UserRole import ru.ulstu.`is`.pmu.tank.model.UserTankCrossRef -import ru.ulstu.`is`.pmu.tank.model.UserWithTanks //тут, собственно говоря, всё и мутится с БД :))) @Database(entities = [Nation::class, Level::class, Tank::class, User::class, UserTankCrossRef::class, TankImage::class], version = 1, exportSchema = false) @@ -34,7 +34,7 @@ abstract class AppDatabase : RoomDatabase() { abstract fun tankImageDao() : TankImageDao companion object { - private const val DB_NAME: String = "19-db" + private const val DB_NAME: String = "20-db" @Volatile private var INSTANCE: AppDatabase? = null @@ -129,7 +129,7 @@ abstract class AppDatabase : RoomDatabase() { //Users val userDao = database.userDao() - val user = User(100L,"3tankista73", "egor@mail.ru", "12032003", 10000000) + val user = User(100L,"3tankista73", "egor@mail.ru", "12032003", UserRole.ADMIN, 10000000) userDao.insert(user) @@ -148,14 +148,14 @@ abstract class AppDatabase : RoomDatabase() { AppDatabase::class.java, DB_NAME ) - .addCallback(object : Callback() { - override fun onCreate(db: SupportSQLiteDatabase) { - super.onCreate(db) - CoroutineScope(Dispatchers.IO).launch { - populateDatabase(appContext) - } - } - }) +// .addCallback(object : Callback() { +// override fun onCreate(db: SupportSQLiteDatabase) { +// super.onCreate(db) +// CoroutineScope(Dispatchers.IO).launch { +// populateDatabase(appContext) +// } +// } +// }) .build() .also { INSTANCE = it } } diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/model/RemoteKeys.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/model/RemoteKeys.kt new file mode 100644 index 0000000..bb6c382 --- /dev/null +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/model/RemoteKeys.kt @@ -0,0 +1,25 @@ +package ru.ulstu.`is`.pmu.tank.model + +import androidx.room.Entity +import androidx.room.PrimaryKey +import androidx.room.TypeConverter +import androidx.room.TypeConverters + +enum class RemoteKeyType(private val type: String) { + NATION(Nation::class.simpleName ?: "Nation"); + + @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? +) \ No newline at end of file diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/model/User.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/model/User.kt index 67cdfef..ef77360 100644 --- a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/model/User.kt +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/model/User.kt @@ -17,6 +17,8 @@ data class User ( val email: String, @ColumnInfo(name = "password") val password: String, + @ColumnInfo(name = "role") + val role: UserRole, @ColumnInfo(name = "balance") val balance: Int ){ @@ -25,8 +27,9 @@ data class User ( nickname: String, email: String, password: String, + role: UserRole, balance: Int - ) : this(0L, nickname, email, password, balance) + ) : this(0L, nickname, email, password, UserRole.ADMIN, balance) companion object { fun getUser(index: Long = 0L): User { @@ -35,6 +38,7 @@ data class User ( "3tankista73", "egor@mail.ru", "1234567890!", + UserRole.USER, 10000000 ) } @@ -50,6 +54,7 @@ data class User ( if (nickname != other.nickname) return false if (email != other.email) return false if (password != other.password) return false + if (role != other.role) return false if (balance != other.balance) return false return true @@ -60,6 +65,7 @@ data class User ( result = 31 * result + nickname.hashCode() result = 31 * result + email.hashCode() result = 31 * result + password.hashCode() + result = 31 * result + role.hashCode() result = 31 * result + balance return result } diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/model/UserRole.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/model/UserRole.kt new file mode 100644 index 0000000..a0ebe3a --- /dev/null +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/model/UserRole.kt @@ -0,0 +1,6 @@ +package ru.ulstu.`is`.pmu.tank.model + +enum class UserRole(val value: Int) { + USER(0), + ADMIN(1) +} \ No newline at end of file diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/model/UserTankCrossRef.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/model/UserTankCrossRef.kt index f1869f5..d384406 100644 --- a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/model/UserTankCrossRef.kt +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/model/UserTankCrossRef.kt @@ -8,7 +8,10 @@ import androidx.room.PrimaryKey import org.jetbrains.annotations.NotNull //many to many for user and tank -@Entity(primaryKeys = ["userId", "tankId"]) +@Entity( + tableName = "users_tanks", + primaryKeys = ["userId", "tankId"] +) data class UserTankCrossRef( val userId: Long, val tankId: Long diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/LevelRepository.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/LevelRepository.kt index 5318242..f508ef1 100644 --- a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/LevelRepository.kt +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/LevelRepository.kt @@ -6,9 +6,11 @@ import ru.ulstu.`is`.pmu.tank.model.LevelWithTanks interface LevelRepository { suspend fun getAllLevels(): List - fun getSimpleLevel(uid: Long): Flow + suspend fun getSimpleLevel(uid: Long): Level fun getFullLevel(uid: Long): Flow suspend fun insertLevel(level: Level) + suspend fun insertMany(levels: List) suspend fun updateLevel(level: Level) suspend fun deleteLevel(level: Level) + suspend fun deleteAll() } diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/NationRepository.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/NationRepository.kt index 1e4bb70..63590cc 100644 --- a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/NationRepository.kt +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/NationRepository.kt @@ -1,16 +1,21 @@ package ru.ulstu.`is`.pmu.tank.repository +import androidx.paging.PagingData import androidx.paging.PagingSource import kotlinx.coroutines.flow.Flow +import ru.ulstu.`is`.pmu.tank.api.model.NationRemote import ru.ulstu.`is`.pmu.tank.model.Nation import ru.ulstu.`is`.pmu.tank.model.NationWithTanks interface NationRepository { suspend fun getAllNations(): List + suspend fun getAll(): Flow> fun getSimpleNation(uid: Long): Flow fun getFullNation(uid: Long): Flow fun pagingSource(): PagingSource suspend fun insertNation(nation: Nation) + suspend fun insertNations(nations: List) suspend fun updateNation(nation: Nation) suspend fun deleteNation(nation: Nation) + suspend fun deleteNations() } \ No newline at end of file diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/OfflineLevelRepository.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/OfflineLevelRepository.kt index 2cfcca0..b50b1d3 100644 --- a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/OfflineLevelRepository.kt +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/OfflineLevelRepository.kt @@ -8,13 +8,16 @@ import ru.ulstu.`is`.pmu.tank.model.LevelWithTanks class OfflineLevelRepository(private val levelDao: LevelDao) : LevelRepository { override suspend fun getAllLevels(): List = levelDao.getAll() - override fun getSimpleLevel(uid: Long): Flow = levelDao.getSimpleLevelUid(uid) + override suspend fun getSimpleLevel(uid: Long): Level = levelDao.getSimpleLevelUid(uid) override fun getFullLevel(uid: Long): Flow = levelDao.getLevelUid(uid) override suspend fun insertLevel(level: Level) = levelDao.insert(level) + override suspend fun insertMany(levels: List) = levelDao.insertMany(levels) override suspend fun updateLevel(level: Level) = levelDao.update(level) override suspend fun deleteLevel(level: Level) = levelDao.delete(level) + + override suspend fun deleteAll() = levelDao.deleteAll() } \ No newline at end of file diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/OfflineNationRepository.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/OfflineNationRepository.kt index 1490e5f..3bf598f 100644 --- a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/OfflineNationRepository.kt +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/OfflineNationRepository.kt @@ -1,5 +1,6 @@ package ru.ulstu.`is`.pmu.tank.repository +import androidx.paging.PagingData import androidx.paging.PagingSource import kotlinx.coroutines.flow.Flow import ru.ulstu.`is`.pmu.tank.dao.NationDao @@ -8,16 +9,25 @@ import ru.ulstu.`is`.pmu.tank.model.NationWithTanks class OfflineNationRepository(private val nationDao: NationDao) : NationRepository { override suspend fun getAllNations(): List = nationDao.getAll() + override suspend fun getAll(): Flow> { + TODO("Not yet implemented") + } override fun getSimpleNation(uid: Long): Flow = nationDao.getSimpleNationUid(uid) override fun getFullNation(uid: Long): Flow = nationDao.getNationUid(uid) - override fun pagingSource(): PagingSource = nationDao.pagingSource() + override fun pagingSource(): PagingSource { + return nationDao.pagingSource() + } override suspend fun insertNation(nation: Nation) = nationDao.insert(nation) override suspend fun updateNation(nation: Nation) = nationDao.update(nation) override suspend fun deleteNation(nation: Nation) = nationDao.delete(nation) + + override suspend fun insertNations(nations: List) = nationDao.insertMany(nations) + + override suspend fun deleteNations() = nationDao.deleteAll() } \ No newline at end of file diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/OfflineRemoteKeyRepository.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/OfflineRemoteKeyRepository.kt new file mode 100644 index 0000000..16b995c --- /dev/null +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/OfflineRemoteKeyRepository.kt @@ -0,0 +1,16 @@ +package ru.ulstu.`is`.pmu.tank.repository + +import ru.ulstu.`is`.pmu.tank.dao.RemoteKeysDao +import ru.ulstu.`is`.pmu.tank.model.RemoteKeyType +import ru.ulstu.`is`.pmu.tank.model.RemoteKeys + +class OfflineRemoteKeyRepository(private val remoteKeysDao: RemoteKeysDao) : RemoteKeyRepository { + override suspend fun getAllRemoteKeys(id: Int, type: RemoteKeyType) = + remoteKeysDao.getRemoteKeys(id, type) + + override suspend fun createRemoteKeys(remoteKeys: List) = + remoteKeysDao.insertAll(remoteKeys) + + override suspend fun deleteRemoteKey(type: RemoteKeyType) = + remoteKeysDao.clearRemoteKeys(type) +} diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/OfflineTankRepository.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/OfflineTankRepository.kt index 70faccf..ad0ea96 100644 --- a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/OfflineTankRepository.kt +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/OfflineTankRepository.kt @@ -13,13 +13,13 @@ class OfflineTankRepository( private val tankDao: TankDao, private val tankImageDao: TankImageDao ) : TankRepository { - override fun getAll(): Flow> = tankDao.getAll() + override suspend fun getAll(): List = tankDao.getAll() - override fun getForUserAll(userId: Long): Flow> = tankDao.getNotUserTank(userId) + override suspend fun getForUserAll(userId: Long): List = tankDao.getNotUserTank(userId) - override fun getTank(uid: Long): Flow = tankDao.getTankUid(uid) + override suspend fun getTank(uid: Long): Tank = tankDao.getTankUid(uid) - override fun getUserTanks(uid: Long): Flow> = tankDao.getUserTanks(uid) + override suspend fun getUserTanks(uid: Long): List = tankDao.getUserTanks(uid) override suspend fun insertTank(tank: Tank, image: Bitmap) { val imageId = tankImageDao.insert( @@ -32,6 +32,10 @@ class OfflineTankRepository( tankDao.insert(tank.copy(imageId = imageId)) } + override suspend fun insertMany(tankList: List) { + tankDao.insertMany(tankList) + } + override suspend fun updateTank(tank: Tank, image: Bitmap) { val imageId = tankImageDao.insert( TankImage( @@ -49,4 +53,8 @@ class OfflineTankRepository( override suspend fun delete(tankId: Long) { tankDao.delete(tankId) } + + override suspend fun deleteAll() { + tankDao.deleteAll() + } } \ No newline at end of file diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/OfflineUserRepository.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/OfflineUserRepository.kt index 8287561..6cdeeb5 100644 --- a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/OfflineUserRepository.kt +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/OfflineUserRepository.kt @@ -8,7 +8,7 @@ import ru.ulstu.`is`.pmu.tank.model.User class OfflineUserRepository(private val userDao: UserDao) : UserRepository { override fun getAllUsers(): Flow> = userDao.getAll() - override fun getSimpleUser(uid: Long): Flow = userDao.getSimpleUserUid(uid) + override suspend fun getSimpleUser(email: String): User = userDao.getSimpleUserUid(email) override fun getFullUser(uid: Long): Flow>> = userDao.getUserUid(uid) diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/RemoteKeyRepository.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/RemoteKeyRepository.kt new file mode 100644 index 0000000..2a9942b --- /dev/null +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/RemoteKeyRepository.kt @@ -0,0 +1,10 @@ +package ru.ulstu.`is`.pmu.tank.repository + +import ru.ulstu.`is`.pmu.tank.model.RemoteKeyType +import ru.ulstu.`is`.pmu.tank.model.RemoteKeys + +interface RemoteKeyRepository { + suspend fun getAllRemoteKeys(id: Int, type: RemoteKeyType): RemoteKeys? + suspend fun createRemoteKeys(remoteKeys: List) + suspend fun deleteRemoteKey(type: RemoteKeyType) +} \ No newline at end of file diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/TankRepository.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/TankRepository.kt index 2063e25..37c654b 100644 --- a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/TankRepository.kt +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/TankRepository.kt @@ -7,12 +7,14 @@ import ru.ulstu.`is`.pmu.tank.model.TankExtra import ru.ulstu.`is`.pmu.tank.model.TankWithNationAndLevel interface TankRepository { - fun getAll(): Flow> - fun getForUserAll(userId: Long): Flow> - fun getTank(uid: Long): Flow - fun getUserTanks(uid: Long): Flow> + suspend fun getAll(): List + suspend fun getForUserAll(userId: Long): List + suspend fun getTank(uid: Long): Tank + suspend fun getUserTanks(uid: Long): List suspend fun insertTank(tank: Tank, image: Bitmap) + suspend fun insertMany(tankList: List) suspend fun updateTank(tank: Tank, image: Bitmap) suspend fun deleteTank(tank: Tank) suspend fun delete(tankId: Long) + suspend fun deleteAll() } \ No newline at end of file diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/UserRepository.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/UserRepository.kt index bf27a96..7cd761a 100644 --- a/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/UserRepository.kt +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tank/repository/UserRepository.kt @@ -6,7 +6,7 @@ import ru.ulstu.`is`.pmu.tank.model.User interface UserRepository { fun getAllUsers(): Flow> - fun getSimpleUser(uid: Long): Flow + suspend fun getSimpleUser(email: String): User fun getFullUser(uid: Long): Flow>> suspend fun insertUser(user: User) suspend fun updateUser(user: User) diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/tanks/composeui/Hangar.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/tanks/composeui/Hangar.kt index d41e25b..a9b7d44 100644 --- a/compose/app/src/main/java/ru/ulstu/is/pmu/tanks/composeui/Hangar.kt +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/tanks/composeui/Hangar.kt @@ -1,14 +1,11 @@ package ru.ulstu.`is`.pmu.tanks.composeui import android.content.res.Configuration -import android.util.Log -import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding @@ -20,20 +17,12 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.runtime.key -import androidx.compose.runtime.mutableStateListOf -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.asImageBitmap -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview @@ -41,23 +30,8 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController -import androidx.navigation.compose.currentBackStackEntryAsState -import androidx.navigation.compose.rememberNavController -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.FlowCollector -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.withContext -import ru.ulstu.`is`.pmu.R -import ru.ulstu.`is`.pmu.composeui.navigation.Screen import ru.ulstu.`is`.pmu.tank.composeui.list.TankListViewModel -import ru.ulstu.`is`.pmu.tank.database.AppDatabase -import ru.ulstu.`is`.pmu.tank.model.Level -import ru.ulstu.`is`.pmu.tank.model.Nation -import ru.ulstu.`is`.pmu.tank.model.Tank import ru.ulstu.`is`.pmu.tank.model.TankWithNationAndLevel -import ru.ulstu.`is`.pmu.tank.model.UserWithTanks -import ru.ulstu.`is`.pmu.tanks.composeui.image.CuteImage import ru.ulstu.`is`.pmu.tanks.composeui.image.Dimensions import ru.ulstu.`is`.pmu.tanks.composeui.image.RoundedCorderImage import ru.ulstu.`is`.pmu.ui.AppViewModelProvider @@ -71,10 +45,9 @@ fun Hangar( viewModel: TankListViewModel = viewModel(factory = AppViewModelProvider.Factory) ){ viewModel.setUserId(100L) - val userTankListUiState by viewModel.usersTanksUiState.collectAsState() //новый вызов основного списка - Hangar(tankList = userTankListUiState.userTankList ) + Hangar(tankList = viewModel.usersTanksUiState.userTankList ) } @OptIn(ExperimentalMaterial3Api::class) diff --git a/compose/app/src/main/java/ru/ulstu/is/pmu/ui/ImageTools.kt b/compose/app/src/main/java/ru/ulstu/is/pmu/ui/ImageTools.kt index e6d71fc..91ab50d 100644 --- a/compose/app/src/main/java/ru/ulstu/is/pmu/ui/ImageTools.kt +++ b/compose/app/src/main/java/ru/ulstu/is/pmu/ui/ImageTools.kt @@ -1,6 +1,9 @@ package com.application.ui import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.util.Base64 +import java.io.ByteArrayOutputStream import kotlin.math.sqrt const val miniatureBound: Int = 250 @@ -23,4 +26,22 @@ fun Bitmap.resize(bound: Int): Bitmap { val newWidth: Int = (width / factor).toInt() val newHeight: Int = (height / factor).toInt() return Bitmap.createScaledBitmap(this, newWidth, newHeight, false) +} + +fun Bitmap.toByteArray(): ByteArray { + val outputStream = ByteArrayOutputStream() + this.compress(Bitmap.CompressFormat.PNG, 1, outputStream) + return outputStream.toByteArray() +} + +fun Bitmap.toBase64(): String { + return Base64.encodeToString(this.toByteArray(), Base64.DEFAULT) +} + +fun ByteArray.toBitmap(): Bitmap { + return BitmapFactory.decodeByteArray(this, 0, this.size) +} + +fun String.toBitmap(): Bitmap { + return Base64.decode(this, Base64.DEFAULT).toBitmap() } \ No newline at end of file