diff --git a/.gitignore b/.gitignore
index 1b938a6..aa724b7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,26 +1,15 @@
-# ---> Kotlin
-# Compiled class file
-*.class
-
-# Log file
-*.log
-
-# BlueJ files
-*.ctxt
-
-# Mobile Tools for Java (J2ME)
-.mtj.tmp/
-
-# Package Files #
-*.jar
-*.war
-*.nar
-*.ear
-*.zip
-*.tar.gz
-*.rar
-
-# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
-hs_err_pid*
-replay_pid*
-
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 0000000..b3405b3
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+My Application
\ No newline at end of file
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
new file mode 100644
index 0000000..7643783
--- /dev/null
+++ b/.idea/codeStyles/Project.xml
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ xmlns:android
+
+ ^$
+
+
+
+
+
+
+
+
+ xmlns:.*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*:id
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ .*:name
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ name
+
+ ^$
+
+
+
+
+
+
+
+
+ style
+
+ ^$
+
+
+
+
+
+
+
+
+ .*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*
+
+ http://schemas.android.com/apk/res/android
+
+
+ ANDROID_ATTRIBUTE_ORDER
+
+
+
+
+
+
+ .*
+
+ .*
+
+
+ BY_NAME
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 0000000..79ee123
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..b589d56
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644
index 0000000..ae388c2
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..44ca2d9
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
new file mode 100644
index 0000000..69e8615
--- /dev/null
+++ b/.idea/kotlinc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..ac801d8
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/.idea/.gitignore b/app/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/app/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/app/.idea/gradle.xml b/app/.idea/gradle.xml
new file mode 100644
index 0000000..0364d75
--- /dev/null
+++ b/app/.idea/gradle.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/.idea/misc.xml b/app/.idea/misc.xml
new file mode 100644
index 0000000..a318cae
--- /dev/null
+++ b/app/.idea/misc.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/.idea/vcs.xml b/app/.idea/vcs.xml
new file mode 100644
index 0000000..6c0b863
--- /dev/null
+++ b/app/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 3c862e9..96251d9 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -1,18 +1,17 @@
-import org.jetbrains.kotlin.kapt3.base.Kapt.kapt
-
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
- id("kotlin-kapt")
+ id("com.google.devtools.ksp")
+ id("org.jetbrains.kotlin.plugin.serialization")
}
android {
- namespace = "com.example.labwork"
- compileSdk = 34
+ namespace = "com.example.myapplication"
+ compileSdk = 33
defaultConfig {
- applicationId = "com.example.labwork"
- minSdk = 24
+ applicationId = "com.example.myapplication"
+ minSdk = 26
targetSdk = 33
versionCode = 1
versionName = "1.0"
@@ -33,6 +32,7 @@ android {
}
}
compileOptions {
+ isCoreLibraryDesugaringEnabled = true
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
@@ -43,7 +43,7 @@ android {
compose = true
}
composeOptions {
- kotlinCompilerExtensionVersion = "1.4.3"
+ kotlinCompilerExtensionVersion = "1.4.5"
}
packaging {
resources {
@@ -53,39 +53,42 @@ android {
}
dependencies {
+ implementation("org.threeten:threetenbp:1.5.0")
+ implementation("androidx.datastore:datastore-preferences:1.0.0")
+ implementation("io.github.vanpra.compose-material-dialogs:datetime:0.8.1-rc")
+ coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.6")
- implementation("androidx.navigation:navigation-compose:2.7.4")
- implementation("androidx.compose.material:material:1.5.4")
-
- implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9")
-
- implementation("androidx.room:room-ktx:2.6.0")
- kapt("androidx.room:room-compiler:2.6.0")
- implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2")
- implementation ("androidx.lifecycle:lifecycle-runtime-ktx:2.4.0")
-
- val paging_version = "3.1.1"
- implementation ("androidx.paging:paging-runtime:$paging_version")
-// alternatively - without Android dependencies for tests
- testImplementation ("androidx.paging:paging-common:$paging_version")
-// optional - RxJava2 support
- implementation ("androidx.paging:paging-rxjava2:$paging_version")
-// optional - RxJava3 support
- implementation ("androidx.paging:paging-rxjava3:$paging_version")
-// optional - Guava ListenableFuture support
- implementation ("androidx.paging:paging-guava:$paging_version")
-// optional - Jetpack Compose integration
- implementation ("androidx.paging:paging-compose:1.0.0-alpha18")
-
-
+ // Core
implementation("androidx.core:core-ktx:1.9.0")
- implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")
- implementation("androidx.activity:activity-compose:1.7.0")
+ implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
+
+ // UI
+ implementation("androidx.activity:activity-compose:1.7.2")
implementation(platform("androidx.compose:compose-bom:2023.03.00"))
+ implementation("androidx.navigation:navigation-compose:2.6.0")
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-graphics")
implementation("androidx.compose.ui:ui-tooling-preview")
- implementation("androidx.compose.material3:material3")
+ implementation("androidx.compose.material3:material3:1.1.2")
+ implementation("androidx.compose.material:material:1.4.3")
+
+ // Room
+ val room_version = "2.5.2"
+ implementation("androidx.room:room-runtime:$room_version")
+ annotationProcessor("androidx.room:room-compiler:$room_version")
+ ksp("androidx.room:room-compiler:$room_version")
+ implementation("androidx.room:room-ktx:$room_version")
+ implementation("androidx.room:room-paging:$room_version")
+
+ // 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")
+
+ // Tests
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
@@ -93,4 +96,6 @@ dependencies {
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
debugImplementation("androidx.compose.ui:ui-tooling")
debugImplementation("androidx.compose.ui:ui-test-manifest")
+
+ //
}
\ No newline at end of file
diff --git a/app/src/androidTest/java/com/example/labwork/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/example/myapplication/ExampleInstrumentedTest.kt
similarity index 83%
rename from app/src/androidTest/java/com/example/labwork/ExampleInstrumentedTest.kt
rename to app/src/androidTest/java/com/example/myapplication/ExampleInstrumentedTest.kt
index 8741692..e9489e1 100644
--- a/app/src/androidTest/java/com/example/labwork/ExampleInstrumentedTest.kt
+++ b/app/src/androidTest/java/com/example/myapplication/ExampleInstrumentedTest.kt
@@ -1,13 +1,11 @@
-package com.example.labwork
+package com.example.myapplication
-import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
-
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.Assert.*
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.Assert.*
-
/**
* Instrumented test, which will execute on an Android device.
*
@@ -19,6 +17,6 @@ class ExampleInstrumentedTest {
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
- assertEquals("com.example.labwork", appContext.packageName)
+ assertEquals("com.example.myapplication", appContext.packageName)
}
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e1b1914..4362861 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -2,22 +2,25 @@
+
+
+ android:theme="@style/Theme.Pmudemo"
+ tools:targetApi="31"
+ android:networkSecurityConfig="@xml/network_security_config">
+ android:theme="@style/Theme.Pmudemo">
diff --git a/app/src/main/java/com/example/labwork/BicycleRentApplication.kt b/app/src/main/java/com/example/labwork/BicycleRentApplication.kt
deleted file mode 100644
index 34f56f4..0000000
--- a/app/src/main/java/com/example/labwork/BicycleRentApplication.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.example.labwork
-
-import android.app.Application
-import com.example.labwork.database.AppContainer
-import com.example.labwork.database.AppDataContainer
-
-class BicycleRentApplication : Application() {
- lateinit var container: AppContainer
-
- override fun onCreate() {
- super.onCreate()
- container = AppDataContainer(this)
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/labwork/MainActivity.kt b/app/src/main/java/com/example/labwork/MainActivity.kt
deleted file mode 100644
index 16a6e38..0000000
--- a/app/src/main/java/com/example/labwork/MainActivity.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.example.labwork
-
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.activity.viewModels
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.tooling.preview.Preview
-import androidx.lifecycle.viewmodel.compose.viewModel
-import com.example.labwork.button_navigation.MainScreen
-import com.example.labwork.database.AppDatabase
-import com.example.labwork.ui.theme.LabWorkTheme
-import com.example.labwork.viewmodel.AppViewModelProvider
-import com.example.labwork.viewmodel.BicycleViewModel
-import com.example.labwork.viewmodel.UserViewModel
-
-
-class MainActivity : ComponentActivity() {
- private val userViewModel: UserViewModel by viewModels()
- private val bicycleViewModel: BicycleViewModel by viewModels()
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- setContent {
- LabWorkTheme {
- MainScreen(viewModel(factory = AppViewModelProvider.Factory), userViewModel = viewModel(factory = AppViewModelProvider.Factory))
- }
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/labwork/button_navigation/MainScreen.kt b/app/src/main/java/com/example/labwork/button_navigation/MainScreen.kt
deleted file mode 100644
index 8c2ab61..0000000
--- a/app/src/main/java/com/example/labwork/button_navigation/MainScreen.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.example.labwork.button_navigation
-
-import android.annotation.SuppressLint
-
-import androidx.compose.material.Scaffold
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
-import androidx.lifecycle.viewmodel.compose.viewModel
-import androidx.navigation.compose.rememberNavController
-import com.example.labwork.database.DAO.BicycleDao
-import com.example.labwork.database.DAO.UserDao
-import com.example.labwork.repository.OfflineBicycleRepository
-import com.example.labwork.repository.OfflineUserRepository
-import com.example.labwork.viewmodel.AppViewModelProvider
-import com.example.labwork.viewmodel.BicycleViewModel
-import com.example.labwork.viewmodel.UserViewModel
-
-@SuppressLint("UnusedMaterialScaffoldPaddingParameter")
-@Composable
-fun MainScreen(bicycleViewModel: BicycleViewModel = viewModel(factory = AppViewModelProvider.Factory),
- userViewModel: UserViewModel = viewModel(factory = AppViewModelProvider.Factory)
-) {
- val navController = rememberNavController()
-
- Scaffold(
- bottomBar = {
- SlideNavigation(navController = navController)
- }
- ) {
- SlideGraph(
- navHostController = navController,
- bicycleViewModel = bicycleViewModel,
- userViewModel = userViewModel
- )
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/labwork/button_navigation/Screens.kt b/app/src/main/java/com/example/labwork/button_navigation/Screens.kt
deleted file mode 100644
index a225f6d..0000000
--- a/app/src/main/java/com/example/labwork/button_navigation/Screens.kt
+++ /dev/null
@@ -1,104 +0,0 @@
-package com.example.labwork.button_navigation
-
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxHeight
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.lazy.itemsIndexed
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.Button
-import androidx.compose.material.ButtonDefaults
-import androidx.compose.material.Text
-import androidx.compose.material3.CircularProgressIndicator
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.text.style.TextAlign
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
-import androidx.lifecycle.viewmodel.compose.viewModel
-import androidx.navigation.NavHostController
-import androidx.paging.LoadState
-import androidx.paging.compose.collectAsLazyPagingItems
-import com.example.labwork.pages.ListInfo
-import com.example.labwork.pages.product.FormNewProduct
-import com.example.labwork.pages.product.ListProduct
-import com.example.labwork.pages.user.RegisteryOrLogin
-import com.example.labwork.ui.theme.LightBluePolitech
-import com.example.labwork.viewmodel.AppViewModelProvider
-import com.example.labwork.viewmodel.BicycleScreenViewModel
-import com.example.labwork.viewmodel.BicycleViewModel
-import com.example.labwork.viewmodel.UserViewModel
-
-@Composable
-fun ScreenInfo() {
- ListInfo()
-}
-
-@Composable
-fun ScreenProfile(userViewModel: UserViewModel, navHostController: NavHostController, bicycleViewModel: BicycleViewModel) {
- RegisteryOrLogin(userViewModel, navHostController, bicycleViewModel)
-}
-
-@Composable
-fun ScreenListProduct(
- bicycleViewModel: BicycleViewModel,
- navHostController: NavHostController,
- bicycleScreenViewModel: BicycleScreenViewModel = viewModel(factory = AppViewModelProvider.Factory)
-) {
- val bicycles = bicycleScreenViewModel.bicycles.collectAsLazyPagingItems()
-
- LaunchedEffect(Unit) {
- bicycleViewModel.fetchBicycles()
- }
-
-
-
- Column(
- modifier = Modifier.fillMaxHeight().padding(bottom = 65.dp)
- ) {
- FormNewProduct(bicycleViewModel, navHostController)
-
- LazyColumn {
- items(count = bicycles.itemCount) { index ->
- val bicycle = bicycles[index]
- if (bicycle != null) {
- ListProduct(
- item = bicycle,
- bicycleViewModel = bicycleViewModel,
- navHostController = navHostController
- )
- }
- }
- bicycles.apply {
- when {
- loadState.refresh is LoadState.Loading -> {
- item { CircularProgressIndicator(modifier = Modifier.fillParentMaxSize(), color = LightBluePolitech) }
- }
- loadState.append is LoadState.Loading -> {
- item { CircularProgressIndicator(modifier = Modifier.fillParentMaxSize(), color = LightBluePolitech) }
- }
- loadState.refresh is LoadState.Error -> {
- val err = bicycles.loadState.refresh as LoadState.Error
- item { Text(err.error.localizedMessage) }
- }
- loadState.append is LoadState.Error -> {
- val err = bicycles.loadState.append as LoadState.Error
- item { Text(err.error.localizedMessage) }
- }
- }
- }
- }
- }
-}
-
diff --git a/app/src/main/java/com/example/labwork/button_navigation/SlideGraph.kt b/app/src/main/java/com/example/labwork/button_navigation/SlideGraph.kt
deleted file mode 100644
index 81e7e2f..0000000
--- a/app/src/main/java/com/example/labwork/button_navigation/SlideGraph.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.example.labwork.button_navigation
-
-
-import androidx.compose.runtime.Composable
-import androidx.lifecycle.viewmodel.compose.viewModel
-import androidx.navigation.NavHostController
-import androidx.navigation.compose.NavHost
-import androidx.navigation.compose.composable
-import com.example.labwork.repository.OfflineBicycleRepository
-import com.example.labwork.repository.OfflineUserRepository
-import com.example.labwork.viewmodel.AppViewModelProvider
-import com.example.labwork.viewmodel.BicycleViewModel
-import com.example.labwork.viewmodel.UserViewModel
-
-
-@Composable
-fun SlideGraph(
- navHostController: NavHostController,
- bicycleViewModel: BicycleViewModel = viewModel(factory = AppViewModelProvider.Factory),
- userViewModel: UserViewModel = viewModel(factory = AppViewModelProvider.Factory)
-) {
-
- NavHost(navController = navHostController, startDestination = "Profile") {
- composable("Profile") {
- ScreenProfile(userViewModel, bicycleViewModel = bicycleViewModel, navHostController = navHostController)
- }
- composable("Info") {
- ScreenInfo()
- }
- composable("ListProduct") {
- ScreenListProduct(bicycleViewModel, navHostController = navHostController)
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/labwork/button_navigation/SlideItem.kt b/app/src/main/java/com/example/labwork/button_navigation/SlideItem.kt
deleted file mode 100644
index e1158da..0000000
--- a/app/src/main/java/com/example/labwork/button_navigation/SlideItem.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.example.labwork.button_navigation
-
-import com.example.labwork.R
-
-sealed class SlideItem(val title: String, val iconId: Int, val route: String){
- object ScreenInfo: SlideItem("Информация", R.drawable.baseline_info,"Info" )
- object ScreenProfile: SlideItem("Профиль", R.drawable.baseline_account_circle,"Profile" )
- object ScreenListProduct: SlideItem("Товары", R.drawable.baseline_manage_search,"ListProduct" )
-}
diff --git a/app/src/main/java/com/example/labwork/button_navigation/SlideNavigation.kt b/app/src/main/java/com/example/labwork/button_navigation/SlideNavigation.kt
deleted file mode 100644
index c939b7a..0000000
--- a/app/src/main/java/com/example/labwork/button_navigation/SlideNavigation.kt
+++ /dev/null
@@ -1,67 +0,0 @@
-package com.example.labwork.button_navigation
-
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.BottomNavigation
-import androidx.compose.material.BottomNavigationItem
-import androidx.compose.material.Icon
-import androidx.compose.material.SnackbarDefaults.backgroundColor
-import androidx.compose.material.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
-import androidx.compose.ui.draw.shadow
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.res.painterResource
-import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
-import androidx.navigation.NavController
-import androidx.navigation.compose.currentBackStackEntryAsState
-import com.example.labwork.ui.theme.DarkBluePolitech
-import com.example.labwork.ui.theme.LightBluePolitech
-
-
-@Composable
-fun SlideNavigation(
- navController: NavController
-) {
- val listItems = listOf(
- SlideItem.ScreenInfo,
- SlideItem.ScreenProfile,
- SlideItem.ScreenListProduct
- )
-
- BottomNavigation(
- backgroundColor = Color.White,
- modifier = Modifier.padding(10.dp),
- elevation = 5.dp
-
- ) {
- val backStackEntry by navController.currentBackStackEntryAsState()
- val currentRout = backStackEntry?.destination?.route
- listItems.forEach { item ->
- BottomNavigationItem(
- selected = currentRout == item.route,
- onClick = {
- navController.navigate(item.route)
- },
- icon = {
- Icon(
- painter = painterResource(id = item.iconId),
- contentDescription = "Icon"
- )
- },
- label = {
- Text(
- text = item.title,
- fontSize = 9.sp
- )
- },
- selectedContentColor = LightBluePolitech,
- unselectedContentColor = DarkBluePolitech
- )
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/labwork/database/AppContainer.kt b/app/src/main/java/com/example/labwork/database/AppContainer.kt
deleted file mode 100644
index 09bdcbe..0000000
--- a/app/src/main/java/com/example/labwork/database/AppContainer.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.example.labwork.database
-
-import android.content.Context
-import com.example.labwork.repository.BicycleRepository
-import com.example.labwork.repository.OfflineBicycleRepository
-import com.example.labwork.repository.OfflineUserRepository
-import com.example.labwork.repository.UserRepository
-
-interface AppContainer {
- val userRepository: UserRepository
- val bicycleRepository: BicycleRepository
-}
-
-class AppDataContainer(private val context: Context) : AppContainer {
- override val userRepository: UserRepository by lazy {
- OfflineUserRepository(AppDatabase.getInstance(context).userDao())
- }
- override val bicycleRepository: BicycleRepository by lazy {
- OfflineBicycleRepository(AppDatabase.getInstance(context).bicycleDao())
- }
-
- companion object {
- const val TIMEOUT = 5000L
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/labwork/database/AppDatabase.kt b/app/src/main/java/com/example/labwork/database/AppDatabase.kt
deleted file mode 100644
index 2b9191c..0000000
--- a/app/src/main/java/com/example/labwork/database/AppDatabase.kt
+++ /dev/null
@@ -1,82 +0,0 @@
-package com.example.labwork.database
-
-import android.content.Context
-import androidx.room.Database
-import androidx.room.Room
-import androidx.room.RoomDatabase
-import androidx.sqlite.db.SupportSQLiteDatabase
-import com.example.labwork.database.DAO.BicycleDao
-import com.example.labwork.database.DAO.UserDao
-import com.example.labwork.models.Bicycle
-import com.example.labwork.models.User
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
-import java.util.concurrent.Executors
-
-@Database(entities = [User::class, Bicycle::class], version = 1)
-abstract class AppDatabase : RoomDatabase() {
- abstract fun userDao(): UserDao
- abstract fun bicycleDao(): BicycleDao
-
- companion object {
- private var instance: AppDatabase? = null
-
- fun getInstance(context: Context): AppDatabase {
- if (instance == null) {
- synchronized(AppDatabase::class) {
- instance = Room.databaseBuilder(
- context.applicationContext,
- AppDatabase::class.java,
- "my-database"
- )
- .addCallback(object : RoomDatabase.Callback() {
- override fun onCreate(db: SupportSQLiteDatabase) {
- super.onCreate(db)
- Executors.newSingleThreadExecutor().execute {
- CoroutineScope(Dispatchers.IO).launch {
- populateDatabase(instance!!)
- }
- }
- }
- })
- .build()
- }
- }
- return instance!!
- }
-
- private suspend fun populateDatabase(database: AppDatabase) {
- val userDao = database.userDao()
- val bicycleDao = database.bicycleDao()
-
- // Создание пользователей
- val user1 = User(null, "John Doe", "john.doe@example.com", "password123")
- val user2 = User(null, "Jane Smith", "jane.smith@example.com", "password456")
-
- // Вставка пользователей в базу данных
- userDao.insertUser(user1)
- userDao.insertUser(user2)
-
- // Создание велосипедов
- val bicycle1 = Bicycle(brand = "Trek", model = "Велосипед 1", color = "Black", userId = 1, id = null)
- val bicycle2 = Bicycle(brand = "Trek", model = "Велосипед 2", color = "Black", userId = null, id = null)
- val bicycle3 = Bicycle(brand = "Trek", model = "Велосипед 3", color = "Black", userId = 1, id = null)
- val bicycle4 = Bicycle(brand = "Trek", model = "Велосипед 4", color = "Black", userId = null, id = null)
- val bicycle5 = Bicycle(brand = "Trek", model = "Велосипед 5", color = "Black", userId = null, id = null)
- val bicycle6 = Bicycle(brand = "Trek", model = "Велосипед 6", color = "Black", userId = null, id = null)
-
- // Вставка велосипедов в базу данных
- bicycleDao.insertBicycle(bicycle1)
- bicycleDao.insertBicycle(bicycle2)
- bicycleDao.insertBicycle(bicycle3)
- bicycleDao.insertBicycle(bicycle4)
- bicycleDao.insertBicycle(bicycle5)
- bicycleDao.insertBicycle(bicycle6)
-
- }
-
- }
-}
-
-
diff --git a/app/src/main/java/com/example/labwork/database/DAO/BicycleDao.kt b/app/src/main/java/com/example/labwork/database/DAO/BicycleDao.kt
deleted file mode 100644
index 5dac7cb..0000000
--- a/app/src/main/java/com/example/labwork/database/DAO/BicycleDao.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.example.labwork.database.DAO
-
-import androidx.room.Dao
-import androidx.room.Delete
-import androidx.room.Insert
-import androidx.room.Query
-import androidx.room.Update
-import com.example.labwork.models.Bicycle
-
-@Dao
-interface BicycleDao {
- @Insert
- suspend fun insertBicycle(bicycle: Bicycle)
-
- @Update
- suspend fun updateBicycle(bicycle: Bicycle)
-
- @Delete
- suspend fun deleteBicycle(bicycle: Bicycle)
-
- @Query("SELECT * FROM bicycles")
- suspend fun getAllBicycles(): List
-
- @Query("SELECT * FROM bicycles WHERE id = :bicycleId")
- suspend fun getBicycleById(bicycleId: Int?): Bicycle
-
- @Query("SELECT * FROM bicycles WHERE userId = :userId")
- suspend fun getBicyclesByUserId(userId: Int): List
-
- @Query("SELECT * FROM bicycles ORDER BY id ASC LIMIT :limit OFFSET :offset")
- suspend fun getBicycles(limit: Int, offset: Int): List
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/labwork/database/DAO/UserDao.kt b/app/src/main/java/com/example/labwork/database/DAO/UserDao.kt
deleted file mode 100644
index f218aad..0000000
--- a/app/src/main/java/com/example/labwork/database/DAO/UserDao.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-package com.example.labwork.database.DAO
-
-import androidx.room.Dao
-import androidx.room.Delete
-import androidx.room.Insert
-import androidx.room.Query
-import androidx.room.Update
-import com.example.labwork.models.User
-
-@Dao
-interface UserDao {
- @Insert
- suspend fun insertUser(user: User)
-
- @Update
- suspend fun updateUser(user: User)
-
- @Delete
- suspend fun deleteUser(user: User)
-
- @Query("SELECT * FROM users")
- suspend fun getAllUsers(): List
-
- @Query("SELECT * FROM users WHERE id = :userId")
- suspend fun getUserById(userId: Int): User
-
- @Query("SELECT * FROM users WHERE email = :email")
- suspend fun getUserByEmail(email: String): User
-
- @Query("SELECT password FROM users WHERE email = :email")
- suspend fun getPasswordByEmail(email: String): String
-
- @Query("SELECT * FROM users WHERE email = :email AND password = :password")
- suspend fun getUserByEmailAndPassword(email: String, password: String): User?
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/labwork/database/Paging/BicyclePagingSource.kt b/app/src/main/java/com/example/labwork/database/Paging/BicyclePagingSource.kt
deleted file mode 100644
index 14dd00d..0000000
--- a/app/src/main/java/com/example/labwork/database/Paging/BicyclePagingSource.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-package com.example.labwork.database.Paging
-
-import android.util.Log
-import androidx.paging.PagingSource
-import androidx.paging.PagingState
-import com.example.labwork.database.DAO.BicycleDao
-import com.example.labwork.models.Bicycle
-import kotlinx.coroutines.delay
-
-class BicyclePagingSource(private val dao: BicycleDao,
-) : PagingSource() {
- override suspend fun load(params: LoadParams): LoadResult {
- val page = params.key ?: 0
-
- return try {
- Log.d("MainPagingSource", "load: $page")
- val entities = dao.getBicycles(params.loadSize, page * params.loadSize)
- if (page != 0) delay(2000)
- LoadResult.Page(
- data = entities,
- prevKey = if (page == 0) null else page - 1,
- nextKey = if (entities.isEmpty()) null else page + 1
- )
- } catch (e: Exception) {
- LoadResult.Error(e)
- }
- }
-
- override val jumpingSupported: Boolean = true
-
- override fun getRefreshKey(state: PagingState): Int? {
- return state.anchorPosition?.let { anchorPosition ->
- val anchorPage = state.closestPageToPosition(anchorPosition)
- anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/labwork/models/Bicycle.kt b/app/src/main/java/com/example/labwork/models/Bicycle.kt
deleted file mode 100644
index 1139784..0000000
--- a/app/src/main/java/com/example/labwork/models/Bicycle.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.example.labwork.models
-
-import androidx.room.Entity
-import androidx.room.ForeignKey
-import androidx.room.PrimaryKey
-
-@Entity(tableName = "bicycles",
- foreignKeys = [ForeignKey(entity = User::class,
- parentColumns = ["id"],
- childColumns = ["userId"],
- onDelete = ForeignKey.SET_NULL)])
-data class Bicycle(
- @PrimaryKey(autoGenerate = true)
- val id: Int?,
- var brand: String,
- var model: String,
- var color: String,
- val userId: Int?
-)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/labwork/models/User.kt b/app/src/main/java/com/example/labwork/models/User.kt
deleted file mode 100644
index 5f3e6a5..0000000
--- a/app/src/main/java/com/example/labwork/models/User.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.example.labwork.models
-
-import androidx.room.Entity
-import androidx.room.ForeignKey
-import androidx.room.PrimaryKey
-
-@Entity(tableName = "users")
-data class User(
- @PrimaryKey(autoGenerate = true)
- val id: Int?,
- var name: String,
- var email: String,
- var password: String,
-)
-
diff --git a/app/src/main/java/com/example/labwork/pages/ListInfo.kt b/app/src/main/java/com/example/labwork/pages/ListInfo.kt
deleted file mode 100644
index 41f5d01..0000000
--- a/app/src/main/java/com/example/labwork/pages/ListInfo.kt
+++ /dev/null
@@ -1,338 +0,0 @@
-package com.example.labwork.pages
-
-import androidx.compose.foundation.Image
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.Tab
-import androidx.compose.material.TabRow
-import androidx.compose.material.TabRowDefaults
-import androidx.compose.material.TabRowDefaults.tabIndicatorOffset
-import androidx.compose.material.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.setValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Alignment.Companion.CenterHorizontally
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
-import androidx.compose.ui.draw.shadow
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.layout.ContentScale
-import androidx.compose.ui.res.painterResource
-import androidx.compose.ui.text.font.FontWeight
-import androidx.compose.ui.text.style.TextAlign
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
-import com.example.labwork.R
-import com.example.labwork.ui.theme.LightBluePolitech
-
-
-@Composable
-fun ListInfo() {
- var selectedTab by remember { mutableStateOf(0) }
- Box(modifier = Modifier.fillMaxSize()) {
- Column(
- modifier = Modifier
- .padding(16.dp)
- .align(Alignment.TopCenter)
- ) {
- Box(
- modifier = Modifier
- .size(200.dp)
- .align(CenterHorizontally)
- .clip(shape = RoundedCornerShape(8.dp))
- .shadow(16.dp, shape = RoundedCornerShape(8.dp))
- ) {
- Image(
- painter = painterResource(id = R.drawable.baseline_directions_bike_24),
- contentDescription = "Menu Image",
- Modifier.fillMaxSize()
- )
- }
- Spacer(modifier = Modifier.height(16.dp))
- TabRow(
- selectedTabIndex = selectedTab,
- backgroundColor = LightBluePolitech,
- contentColor = Color.White,
- indicator = { tabPositions ->
- TabRowDefaults.Indicator(
- color = Color.White,
- height = 4.dp,
- modifier = Modifier
- .tabIndicatorOffset(tabPositions[selectedTab])
- )
- },
- modifier = Modifier.shadow(16.dp)
- ) {
- Tab(
- selected = selectedTab == 0,
- onClick = { selectedTab = 0 }
- ) {
- Text(
- text = "Университет",
- modifier = Modifier.padding(8.dp),
- textAlign = TextAlign.Center,
- )
- }
- Tab(
- selected = selectedTab == 1,
- onClick = { selectedTab = 1 }
- ) {
- Text(
- text = "Аренда велосипедов",
- modifier = Modifier.padding(8.dp),
- textAlign = TextAlign.Center,
- )
- }
- Tab(
- selected = selectedTab == 2,
- onClick = { selectedTab = 2 }
- ) {
- Text(
- text = "Контакты разработчиков",
- modifier = Modifier
- .padding(8.dp),
- textAlign = TextAlign.Center,
- )
- }
- }
- Spacer(modifier = Modifier.height(16.dp))
- when (selectedTab) {
- 0 -> InfoUniver()
- 1 -> InfoBike()
- 2 -> InfoProgrammer()
- }
- }
- }
-}
-
-@Composable
-fun InfoUniver() {
- LazyColumn(
- modifier = Modifier
- .fillMaxSize()
- .padding(bottom = 65.dp),
- contentPadding = PaddingValues(16.dp)
- ) {
- item {
- Text(
- text = "Основные сведения",
- fontSize = 24.sp,
- fontWeight = FontWeight.Bold
- )
- Spacer(modifier = Modifier.height(16.dp))
-
- Text(
- "Полное наименование образовательной организации",
- fontSize = 16.sp,
- fontWeight = FontWeight.Bold
- )
- Text(
- text = "Федеральное государственное бюджетное образовательное учреждение высшего образования \"Ульяновский государственный технический университет\""
- )
- Spacer(modifier = Modifier.height(8.dp))
-
- Text(
- "Сокращенное (при наличии) наименование образовательной организации",
- fontSize = 16.sp,
- fontWeight = FontWeight.Bold
- )
- Text(text = "УлГТУ, Ульяновский государственный технический университет")
- Spacer(modifier = Modifier.height(8.dp))
-
- Text(
- "Дата создания образовательной организации",
- fontSize = 16.sp,
- fontWeight = FontWeight.Bold
- )
- Text(text = "18.09.1957")
- Spacer(modifier = Modifier.height(8.dp))
-
- Text(
- "Адрес местонахождения образовательной организации",
- fontSize = 16.sp,
- fontWeight = FontWeight.Bold
- )
- Text(text = "432027, Ульяновская область, г. Ульяновск, улица Северный Венец, дом 32")
- Spacer(modifier = Modifier.height(8.dp))
-
- Text(
- "Филиалы образовательной организации",
- fontSize = 16.sp,
- fontWeight = FontWeight.Bold
- )
- Text(text = "Имеются")
- Spacer(modifier = Modifier.height(8.dp))
-
- Text(
- "Представительства образовательной организации",
- fontSize = 16.sp,
- fontWeight = FontWeight.Bold
- )
- Text(text = "Имеются")
- Spacer(modifier = Modifier.height(8.dp))
-
- Text(
- "Режим, график работы",
- fontSize = 16.sp,
- fontWeight = FontWeight.Bold
- )
- Text(text = "Понедельник-четверг - 8.30 - 17.22")
- Text(text = "Пятница- 8.30 - 16.22")
- Text(text = "Перерыв 13.00 до 13.40")
- Spacer(modifier = Modifier.height(8.dp))
-
- Text(
- "Контактные телефоны",
- fontSize = 16.sp,
- fontWeight = FontWeight.Bold
- )
- Text(text = "8(8422)43-02-37")
- Spacer(modifier = Modifier.height(8.dp))
-
- Text(
- "Адреса электронной почты",
- fontSize = 16.sp,
- fontWeight = FontWeight.Bold
- )
- Text(text = "rector@ulstu.ru")
- }
- }
-}
-
-@Composable
-fun InfoBike() {
- LazyColumn(
- modifier = Modifier
- .fillMaxSize()
- .padding(bottom = 65.dp),
- contentPadding = PaddingValues(16.dp)
- ) {
- item {
- Text(
- text = "Основные сведения",
- fontSize = 24.sp,
- fontWeight = FontWeight.Bold
- )
- Spacer(modifier = Modifier.height(16.dp))
-
- Text("Технический университет подобным сокровищем обладает!",modifier = Modifier.padding(bottom = 16.dp))
-
- Text(
- text = "Аренда велосипедов стала популярной и удобной системой передвижения в нашем университете. " +
- "Это новая и интересная инициатива, которая предоставляет возможность студентам и персоналу " +
- "университета арендовать велосипеды для своих транспортных нужд."
- ,modifier = Modifier.padding(bottom = 16.dp)
- )
-
- Text(
- text = "Система аренды велосипедов в университете работает через специальное мобильное приложение, " +
- "которое позволяет зарегистрированным пользователям легко и быстро арендовать велосипеды. " +
- "Велосипеды предоставляются в зоне университета и пользуются большим спросом среди студентов, " +
- "которые хотят быстро и удобно перемещаться по территорий и её окрестности."
- ,modifier = Modifier.padding(bottom = 16.dp)
- )
-
- Text(
- text = "В целом, система аренды велосипедов в университете представляет собой инновационный и удобный способ транспортировки. " +
- "Она делает перемещение по университету более доступным, эффективным и приятным. " +
- "Кроме того, аренда велосипедов способствует здоровому образу жизни и позволяет студентам" +
- " в полной мере наслаждаться красотами университетского окружения.",
- modifier = Modifier.padding(bottom = 16.dp)
- )
-
- Spacer(modifier = Modifier.height(16.dp))
-
- Text("Адреса электронной почты",
- fontSize = 16.sp,
- fontWeight = FontWeight.Bold)
- Text(text = "arendabike@ulstu.ru")
- }
- }
-}
-
-@Composable
-fun InfoProgrammer() {
- LazyColumn(
- modifier = Modifier
- .fillMaxSize()
- .padding(bottom = 65.dp),
- contentPadding = PaddingValues(16.dp)
- ) {
- item {
- Text(
- text = "Основные сведения",
- fontSize = 24.sp,
- fontWeight = FontWeight.Bold
- )
-
- Box(
- modifier = Modifier
- .size(200.dp)
- .clip(shape = RoundedCornerShape(8.dp))
- ) {
- Image(
- painter = painterResource(id = R.drawable.kashinmaksim),
- contentDescription = "Maxim",
- contentScale = ContentScale.Crop,
- modifier = Modifier.fillMaxSize()
- )
- }
-
- Spacer(modifier = Modifier.height(16.dp))
-
- Text("Подробная информация",
- fontSize = 16.sp,
- fontWeight = FontWeight.Bold)
- Text(
- text = "Кашин Максим Игоревич"
- )
-
- Text("Личная информация",
- fontSize = 16.sp,
- fontWeight = FontWeight.Bold)
- Text(
- text = "Родной город: Сыктывкар"
- )
- Text(
- text = "Языки: Русский"
- )
- Text(
- text = "День рождения: 23 декабря"
- )
-
- Text("Место работы",
- fontSize = 16.sp,
- fontWeight = FontWeight.Bold)
- Text(
- text = "ДЦТ ЛАОП"
- )
-
- Text("Образование",
- fontSize = 16.sp,
- fontWeight = FontWeight.Bold)
- Text(
- text = "Вуз: УлГТУ"
- )
- Text(
- text = "Факультет: Факультет информационных систем и технологий"
- )
- Text(
- text = "Специальность: Программная инженерия"
- )
- Text(
- text = "Школа: Школа № 7 Димитровград до 2021 г."
- )
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/labwork/pages/product/FormProduct.kt b/app/src/main/java/com/example/labwork/pages/product/FormProduct.kt
deleted file mode 100644
index 4c5fe3c..0000000
--- a/app/src/main/java/com/example/labwork/pages/product/FormProduct.kt
+++ /dev/null
@@ -1,247 +0,0 @@
-package com.example.labwork.pages.product
-
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.Button
-import androidx.compose.material.ButtonDefaults
-import androidx.compose.material.Text
-import androidx.compose.material.TextField
-import androidx.compose.material.TextFieldDefaults
-import androidx.compose.runtime.Composable
-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.graphics.Color
-import androidx.compose.ui.text.style.TextAlign
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
-import androidx.navigation.NavHostController
-import com.example.labwork.database.DAO.BicycleDao
-import com.example.labwork.models.Bicycle
-import com.example.labwork.ui.theme.LightBluePolitech
-import com.example.labwork.viewmodel.BicycleViewModel
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.launch
-@Composable
-fun FormNewProduct(
- bicycleViewModel: BicycleViewModel,
- navHostController: NavHostController
-) {
- val isFormVisible = remember { mutableStateOf(false) }
- var brand by remember { mutableStateOf("") }
- var model by remember { mutableStateOf("") }
- var color by remember { mutableStateOf("") }
-
- Button(
- colors = ButtonDefaults.buttonColors(backgroundColor = LightBluePolitech),
- modifier = Modifier
- .fillMaxWidth()
- .padding(9.dp),
- onClick = {
- // Установите значение состояния открытия формы как true
- isFormVisible.value = true
- },
- shape = RoundedCornerShape(15.dp)
- ) {
- Text(
- text = "Добавить",
- color = Color.White,
- fontSize = 10.sp,
- textAlign = TextAlign.Center
- )
- }
-
- if (isFormVisible.value) {
- Column(
- modifier = Modifier.fillMaxWidth()
- ) {
- // Поле ввода для бренда
- TextField(
- value = brand,
- onValueChange = { brand = it },
- placeholder = { Text("Бренд") },
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp, vertical = 8.dp),
- colors = TextFieldDefaults.textFieldColors(backgroundColor = Color.White)
- )
-
- // Поле ввода для модели
- TextField(
- value = model,
- onValueChange = { model = it },
- placeholder = { Text("Модель") },
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp, vertical = 8.dp),
- colors = TextFieldDefaults.textFieldColors(backgroundColor = Color.White)
- )
-
- // Поле ввода для цвета
- TextField(
- value = color,
- onValueChange = { color = it },
- placeholder = { Text("Цвет") },
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp, vertical = 8.dp),
- colors = TextFieldDefaults.textFieldColors(backgroundColor = Color.White)
- )
-
- Button(
- colors = ButtonDefaults.buttonColors(backgroundColor = LightBluePolitech),
- modifier = Modifier
- .fillMaxWidth()
- .padding(9.dp),
- onClick = {
- val newBicycle = Bicycle(null, brand, model, color, null)
- bicycleViewModel.insertBicycle(newBicycle)
- isFormVisible.value = false
- navHostController.navigate("ListProduct")
- },
- shape = RoundedCornerShape(15.dp)
- ) {
- Text(
- text = "Принять",
- color = Color.White,
- fontSize = 10.sp,
- textAlign = TextAlign.Center
- )
- }
- Button(
- colors = ButtonDefaults.buttonColors(backgroundColor = LightBluePolitech),
- modifier = Modifier
- .fillMaxWidth()
- .padding(9.dp),
- onClick = {
- isFormVisible.value = false
- navHostController.navigate("ListProduct")
- },
- shape = RoundedCornerShape(15.dp)
- ) {
- Text(
- text = "Отменить",
- color = Color.White,
- fontSize = 10.sp,
- textAlign = TextAlign.Center
- )
- }
- }
-
- }
-}
-
-@Composable
-fun FormUpdateProduct(
- item: Bicycle,
- bicycleViewModel: BicycleViewModel,
- navHostController: NavHostController
-) {
- val isFormVisible = remember { mutableStateOf(false) }
- var brand by remember { mutableStateOf(item.brand) }
- var model by remember { mutableStateOf(item.model) }
- var color by remember { mutableStateOf(item.color) }
-
- Button(
- colors = ButtonDefaults.buttonColors(backgroundColor = LightBluePolitech),
- modifier = Modifier
- .padding(start = 9.dp, bottom = 9.dp)
- .size(height = 32.dp, width = 128.dp),
- onClick = {
- // Установите значение состояния открытия формы как true
- isFormVisible.value = true
- },
- shape = RoundedCornerShape(15.dp)
- ) {
- Text(
- text = "Редактировать",
- color = Color.White,
- fontSize = 10.sp,
- textAlign = TextAlign.Center
- )
- }
-
- if (isFormVisible.value) {
- Column(
- modifier = Modifier.fillMaxWidth()
- ) {
- // Поле ввода для бренда
- TextField(
- value = brand,
- onValueChange = { brand = it },
- placeholder = { Text("Бренд") },
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp, vertical = 8.dp),
- colors = TextFieldDefaults.textFieldColors(backgroundColor = Color.White)
- )
-
- // Поле ввода для модели
- TextField(
- value = model,
- onValueChange = { model = it },
- placeholder = { Text("Модель") },
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp, vertical = 8.dp),
- colors = TextFieldDefaults.textFieldColors(backgroundColor = Color.White)
- )
-
- // Поле ввода для цвета
- TextField(
- value = color,
- onValueChange = { color = it },
- placeholder = { Text("Цвет") },
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp, vertical = 8.dp),
- colors = TextFieldDefaults.textFieldColors(backgroundColor = Color.White)
- )
-
- Button(
- colors = ButtonDefaults.buttonColors(backgroundColor = LightBluePolitech),
- modifier = Modifier
- .fillMaxWidth()
- .padding(9.dp),
- onClick = {
- val newBicycle = Bicycle(item.id, brand, model, color, null)
- bicycleViewModel.updateBicycle(newBicycle)
- isFormVisible.value = false
- navHostController.navigate("ListProduct")
- },
- shape = RoundedCornerShape(15.dp)
- ) {
- Text(
- text = "Принять",
- color = Color.White,
- fontSize = 10.sp,
- textAlign = TextAlign.Center
- )
- }
- Button(
- colors = ButtonDefaults.buttonColors(backgroundColor = LightBluePolitech),
- modifier = Modifier
- .fillMaxWidth()
- .padding(9.dp),
- onClick = {
- isFormVisible.value = false
- navHostController.navigate("ListProduct")
- },
- shape = RoundedCornerShape(15.dp)
- ) {
- Text(
- text = "Отменить",
- color = Color.White,
- fontSize = 10.sp,
- textAlign = TextAlign.Center
- )
- }
- }
-
- }
-}
diff --git a/app/src/main/java/com/example/labwork/pages/product/ListProduct.kt b/app/src/main/java/com/example/labwork/pages/product/ListProduct.kt
deleted file mode 100644
index 6eb63ab..0000000
--- a/app/src/main/java/com/example/labwork/pages/product/ListProduct.kt
+++ /dev/null
@@ -1,170 +0,0 @@
-package com.example.labwork.pages.product
-
-import androidx.compose.animation.AnimatedVisibility
-import androidx.compose.animation.core.animateDpAsState
-import androidx.compose.animation.core.animateFloatAsState
-import androidx.compose.animation.expandIn
-import androidx.compose.animation.fadeIn
-import androidx.compose.animation.fadeOut
-import androidx.compose.animation.shrinkOut
-import androidx.compose.foundation.Image
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.Button
-import androidx.compose.material.ButtonDefaults
-import androidx.compose.material.Card
-import androidx.compose.material.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.scale
-import androidx.compose.ui.draw.shadow
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.layout.ContentScale
-import androidx.compose.ui.res.painterResource
-import androidx.compose.ui.text.font.FontWeight
-import androidx.compose.ui.text.style.TextAlign
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
-import androidx.navigation.NavHostController
-import com.example.labwork.R
-import com.example.labwork.models.Bicycle
-import com.example.labwork.ui.theme.LightBluePolitech
-import com.example.labwork.viewmodel.BicycleViewModel
-
-
-@Composable
-fun ListProduct(item: Bicycle, bicycleViewModel : BicycleViewModel, navHostController: NavHostController) {
- var isFullAbout by remember { mutableStateOf(false) }
- val scale by animateFloatAsState(if (isFullAbout) 1f else 0f)
- val textSize by animateDpAsState(if (isFullAbout) 18.dp else 24.dp)
-
- Card(
- modifier = Modifier
- .fillMaxWidth()
- .padding(10.dp),
- shape = RoundedCornerShape(15.dp),
- elevation = 5.dp
- ) {
- Box {
- Row(verticalAlignment = Alignment.CenterVertically) {
- Column {
- Image(
- painter = painterResource(id = R.drawable.baseline_directions_bike_24),
- contentDescription = "book",
- contentScale = ContentScale.Crop,
- modifier = Modifier
- .padding(10.dp)
- .size(128.dp)
- .shadow(2.dp)
- )
- Button(
- colors = ButtonDefaults.buttonColors(backgroundColor = LightBluePolitech),
- modifier = Modifier
- .padding(start = 9.dp, bottom = 9.dp)
- .size(height = 32.dp, width = 128.dp),
- onClick = {
- isFullAbout = !isFullAbout
- },
- shape = RoundedCornerShape(15.dp)
- ) {
- Text(
- text = if(isFullAbout) "Убрать" else "Подробнее",
- color = Color.White,
- fontSize = 10.sp,
- textAlign = TextAlign.Center,
- )
- }
- Column {
- FormUpdateProduct(item, bicycleViewModel, navHostController)
- Button(
- colors = ButtonDefaults.buttonColors(backgroundColor = LightBluePolitech),
- modifier = Modifier
- .padding(start = 9.dp, bottom = 9.dp)
- .size(height = 32.dp, width = 128.dp),
- onClick = {
- bicycleViewModel.deleteBicycle(item)
- navHostController.navigate("ListProduct")
- },
- shape = RoundedCornerShape(15.dp)
- ) {
- Text(
- text = "Удалить",
- color = Color.White,
- fontSize = 10.sp,
- textAlign = TextAlign.Center,
- )
- }
- }
- }
- Column {
- Text(
- text = item.model,
- color = Color.Black,
- fontSize = textSize.value.sp,
- fontWeight = FontWeight.Bold,
- textAlign = TextAlign.Center,
- modifier = Modifier
- .fillMaxWidth()
- .padding(top = 8.dp)
- )
-
- AnimatedVisibility(
- visible = isFullAbout,
- enter = fadeIn() + expandIn(),
- exit = fadeOut() + shrinkOut(),
- modifier = Modifier.scale(scale)
- ) {
- Column {
- Text(
- color = Color.Black,
- text = item.brand,
- maxLines = 10,
- fontSize = 14.sp,
- textAlign = TextAlign.Center,
- modifier = Modifier
- .fillMaxWidth()
- )
- Text(
- color = Color.Black,
- text = item.color,
- maxLines = 10,
- fontSize = 14.sp,
- textAlign = TextAlign.Center,
- modifier = Modifier
- .fillMaxWidth()
- )
- Text(
- color = Color.Black,
- text = item.brand,
- maxLines = 10,
- fontSize = 14.sp,
- textAlign = TextAlign.Center,
- modifier = Modifier
- .fillMaxWidth()
- )
- Text(
- color = Color.Black,
- text = if (item.userId != null) "Занято" else "Свободно",
- maxLines = 10,
- fontSize = 14.sp,
- textAlign = TextAlign.Center,
- modifier = Modifier
- .fillMaxWidth()
- )
- }
- }
- }
- }
- }
- }
-}
diff --git a/app/src/main/java/com/example/labwork/pages/user/LoginPage.kt b/app/src/main/java/com/example/labwork/pages/user/LoginPage.kt
deleted file mode 100644
index c0bee25..0000000
--- a/app/src/main/java/com/example/labwork/pages/user/LoginPage.kt
+++ /dev/null
@@ -1,167 +0,0 @@
-package com.example.labwork.pages.user
-
-import android.widget.Toast
-import androidx.compose.foundation.Image
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.Button
-import androidx.compose.material.ButtonDefaults
-import androidx.compose.material.IconButton
-import androidx.compose.material.Text
-import androidx.compose.material.TextField
-import androidx.compose.material.TextFieldDefaults
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.res.painterResource
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.input.PasswordVisualTransformation
-import androidx.compose.ui.text.input.VisualTransformation
-import androidx.compose.ui.text.style.TextAlign
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
-import androidx.navigation.NavController
-import androidx.navigation.NavHostController
-import com.example.labwork.R
-import com.example.labwork.models.User
-import com.example.labwork.ui.theme.LightBluePolitech
-import com.example.labwork.viewmodel.BicycleViewModel
-import com.example.labwork.viewmodel.UserViewModel
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.launch
-
-@Composable
-fun LoginPage(
- navController: NavController,
- navHostController: NavHostController,
- userViewModel: UserViewModel,
- bicycleViewModel: BicycleViewModel
-) {
- var email by remember { mutableStateOf("") }
- var password by remember { mutableStateOf("") }
- var showPassword by remember { mutableStateOf(false) }
- var user by remember { mutableStateOf(null) }
- var acceptLogin by remember { mutableStateOf(false) }
-
- if (acceptLogin == true) {
- user?.let { userProfile ->
- ProfileForm(item = userProfile, userViewModel = userViewModel, navHostController = navHostController, bicycleViewModel = bicycleViewModel)
- }
- }
-
- else {
- Column(
- modifier = Modifier
- .fillMaxSize()
- .padding(16.dp),
- verticalArrangement = Arrangement.Center,
- horizontalAlignment = Alignment.CenterHorizontally
- ) {
- Image(
- painter = painterResource(R.drawable.logo_ulstu),
- contentDescription = "Logo",
- modifier = Modifier.size(200.dp)
- )
- Spacer(modifier = Modifier.height(16.dp))
-
- TextField(
- value = email,
- onValueChange = { email = it },
- label = { Text("Почта") },
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp),
- textStyle = TextStyle(fontSize = 16.sp),
- colors = TextFieldDefaults.textFieldColors(
- cursorColor = LightBluePolitech,
- backgroundColor = Color.White,
- textColor = LightBluePolitech,
- unfocusedLabelColor = LightBluePolitech,
- focusedIndicatorColor = LightBluePolitech,
- unfocusedIndicatorColor = LightBluePolitech,
- focusedLabelColor = LightBluePolitech
- ),
- singleLine = true
- )
- Spacer(modifier = Modifier.height(8.dp))
-
- TextField(
- value = password,
- onValueChange = { password = it },
- label = { Text("Пароль") },
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp),
- textStyle = TextStyle(fontSize = 16.sp),
- colors = TextFieldDefaults.textFieldColors(
- cursorColor = LightBluePolitech,
- backgroundColor = Color.White,
- textColor = LightBluePolitech,
- unfocusedLabelColor = LightBluePolitech,
- focusedIndicatorColor = LightBluePolitech,
- unfocusedIndicatorColor = LightBluePolitech,
- focusedLabelColor = LightBluePolitech
- ),
- singleLine = true,
- visualTransformation = if (showPassword) VisualTransformation.None else PasswordVisualTransformation(),
- trailingIcon = {
- IconButton(
- onClick = { showPassword = !showPassword }
- ) {
- Image(
- painter = if (showPassword) painterResource(R.drawable.baseline_visibility) else painterResource(
- R.drawable.baseline_visibility_off
- ),
- contentDescription = "LogoVissable",
- modifier = Modifier.size(24.dp)
- )
- }
- }
- )
- Spacer(modifier = Modifier.height(8.dp))
-
- Button(
- onClick = {
- GlobalScope.launch {
- val result = userViewModel.login(email, password)
- user = userViewModel.getUserByEmail(email)
- if (result) {
- acceptLogin = true
- }
- }
- },
- colors = ButtonDefaults.buttonColors(backgroundColor = LightBluePolitech,),
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp)
- ) {
- Text(text = "Авторизоваться", color = Color.White)
- }
- Spacer(modifier = Modifier.height(8.dp))
-
- Button(
- onClick = { navController.navigate("register") },
- colors = ButtonDefaults.buttonColors(backgroundColor = LightBluePolitech),
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp)
- ) {
- Text(text = "Регистрация", color = Color.White)
- }
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/labwork/pages/user/ProfileForm.kt b/app/src/main/java/com/example/labwork/pages/user/ProfileForm.kt
deleted file mode 100644
index fabc1ab..0000000
--- a/app/src/main/java/com/example/labwork/pages/user/ProfileForm.kt
+++ /dev/null
@@ -1,156 +0,0 @@
-package com.example.labwork.pages.user
-
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.foundation.text.ClickableText
-import androidx.compose.material.Button
-import androidx.compose.material.ButtonDefaults
-import androidx.compose.material.DropdownMenuItem
-import androidx.compose.material.Text
-import androidx.compose.material.TextField
-import androidx.compose.material.TextFieldDefaults
-import androidx.compose.material3.DropdownMenu
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
-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.graphics.Color
-import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.style.TextAlign
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
-import androidx.lifecycle.viewmodel.compose.viewModel
-import androidx.navigation.NavHostController
-import com.example.labwork.database.DAO.BicycleDao
-import com.example.labwork.database.DAO.UserDao
-import com.example.labwork.models.Bicycle
-import com.example.labwork.models.User
-import com.example.labwork.ui.theme.LightBluePolitech
-import com.example.labwork.viewmodel.BicycleViewModel
-import com.example.labwork.viewmodel.UserViewModel
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.launch
-
-@Composable
-fun ProfileForm(item: User, userViewModel: UserViewModel, navHostController: NavHostController, bicycleViewModel: BicycleViewModel) {
-
- val bicycles by bicycleViewModel.bicycles.collectAsState(emptyList())
-
- var selectedBicycleId by remember { mutableStateOf(null) }
- val selectedBicycle = bicycles.find { it.id == selectedBicycleId }
- val expandedDropdown = remember { mutableStateOf(false) }
-
- var email by remember { mutableStateOf(item.email) }
- var name by remember { mutableStateOf(item.name) }
- var password by remember { mutableStateOf(item.password) }
-
- Column(
- modifier = Modifier.fillMaxWidth()
- ) {
- TextField(
- value = email,
- onValueChange = { email = it },
- placeholder = { Text("Почта") },
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp, vertical = 8.dp),
- colors = TextFieldDefaults.textFieldColors(backgroundColor = Color.White)
- )
-
- TextField(
- value = name,
- onValueChange = { name = it },
- placeholder = { Text("Имя") },
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp, vertical = 8.dp),
- colors = TextFieldDefaults.textFieldColors(backgroundColor = Color.White)
- )
-
- TextField(
- value = password,
- onValueChange = { password = it },
- placeholder = { Text("Пароль") },
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp, vertical = 8.dp),
- colors = TextFieldDefaults.textFieldColors(backgroundColor = Color.White)
- )
-
- Button(
- onClick = { expandedDropdown.value = true },
- modifier = Modifier.padding(9.dp),
- shape = RoundedCornerShape(15.dp),
- colors = ButtonDefaults.buttonColors(backgroundColor = LightBluePolitech),
- ) {
- Text(
- text = AnnotatedString(selectedBicycle?.brand ?: "Выберите велосипед"),
- color = Color.White,
- fontSize = 10.sp,
- textAlign = TextAlign.Center
- )
- }
-
- DropdownMenu(
- expanded = expandedDropdown.value,
- onDismissRequest = { expandedDropdown.value = false }
- ) {
- bicycles.forEach { bicycle ->
- DropdownMenuItem(
- onClick = {
- selectedBicycleId = bicycle.id
- expandedDropdown.value = false
- println(item.id)
- println(bicycle.id)
- }
- ) {
- Text(text = "${bicycle.brand} ${bicycle.model}")
- }
- }
- }
-
- Button(
- colors = ButtonDefaults.buttonColors(backgroundColor = LightBluePolitech),
- modifier = Modifier
- .fillMaxWidth()
- .padding(9.dp),
- onClick = {
- userViewModel.updateUser(User(item.id!!, email, name, password))
- //userViewModel.updateBicycleUserId(selectedBicycleId!!, item.id) // Обновление userId для Bicycle
- navHostController.navigate("ListProduct")
- },
- shape = RoundedCornerShape(15.dp)
- ) {
- Text(
- text = "Принять",
- color = Color.White,
- fontSize = 10.sp,
- textAlign = TextAlign.Center
- )
- }
- Button(
- colors = ButtonDefaults.buttonColors(backgroundColor = LightBluePolitech),
- modifier = Modifier
- .fillMaxWidth()
- .padding(9.dp),
- onClick = {
- navHostController.navigate("ListProduct")
- },
- shape = RoundedCornerShape(15.dp)
- ) {
- Text(
- text = "Отменить",
- color = Color.White,
- fontSize = 10.sp,
- textAlign = TextAlign.Center
- )
- }
- }
-}
-
diff --git a/app/src/main/java/com/example/labwork/pages/user/RegOrLog.kt b/app/src/main/java/com/example/labwork/pages/user/RegOrLog.kt
deleted file mode 100644
index dafb039..0000000
--- a/app/src/main/java/com/example/labwork/pages/user/RegOrLog.kt
+++ /dev/null
@@ -1,57 +0,0 @@
-package com.example.labwork.pages.user
-
-import androidx.compose.foundation.Image
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
-import androidx.compose.material.Button
-import androidx.compose.material.ButtonDefaults
-import androidx.compose.material.IconButton
-import androidx.compose.material.Text
-import androidx.compose.material.TextField
-import androidx.compose.material.TextFieldDefaults
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.res.painterResource
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.input.PasswordVisualTransformation
-import androidx.compose.ui.text.input.VisualTransformation
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
-import androidx.navigation.NavController
-import androidx.navigation.NavHostController
-import androidx.navigation.compose.NavHost
-import androidx.navigation.compose.composable
-import androidx.navigation.compose.rememberNavController
-import com.example.labwork.R
-import com.example.labwork.database.DAO.BicycleDao
-import com.example.labwork.database.DAO.UserDao
-import com.example.labwork.ui.theme.LightBluePolitech
-import com.example.labwork.viewmodel.BicycleViewModel
-import com.example.labwork.viewmodel.UserViewModel
-
-@Composable
-fun RegisteryOrLogin(userViewModel: UserViewModel, navHostController: NavHostController, bicycleViewModel: BicycleViewModel) {
- val navController = rememberNavController()
-
- NavHost(navController, startDestination = "login") {
- composable("login") {
- LoginPage(navController = navController, userViewModel = userViewModel, navHostController = navHostController, bicycleViewModel = bicycleViewModel)
- }
- composable("register") {
- RegisteryPage(navController = navController, userViewModel = userViewModel, navHostController = navHostController)
- }
- }
-}
-
diff --git a/app/src/main/java/com/example/labwork/pages/user/RegisteryPage.kt b/app/src/main/java/com/example/labwork/pages/user/RegisteryPage.kt
deleted file mode 100644
index 466e0f0..0000000
--- a/app/src/main/java/com/example/labwork/pages/user/RegisteryPage.kt
+++ /dev/null
@@ -1,165 +0,0 @@
-package com.example.labwork.pages.user
-
-import androidx.compose.foundation.Image
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
-import androidx.compose.material.Button
-import androidx.compose.material.ButtonDefaults
-import androidx.compose.material.Text
-import androidx.compose.material.TextField
-import androidx.compose.material.TextFieldDefaults
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.res.painterResource
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.input.PasswordVisualTransformation
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
-import androidx.navigation.NavController
-import androidx.navigation.NavHostController
-import com.example.labwork.R
-import com.example.labwork.database.DAO.BicycleDao
-import com.example.labwork.database.DAO.UserDao
-import com.example.labwork.models.Bicycle
-import com.example.labwork.models.User
-import com.example.labwork.ui.theme.LightBluePolitech
-import com.example.labwork.viewmodel.UserViewModel
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.launch
-
-@Composable
-fun RegisteryPage(navController: NavController, userViewModel: UserViewModel, navHostController: NavHostController) {
- var username by remember { mutableStateOf("") }
- var password by remember { mutableStateOf("") }
- var confirmPassword by remember { mutableStateOf("") }
- var email by remember { mutableStateOf("") }
-
- Column(
- modifier = Modifier
- .fillMaxSize()
- .padding(16.dp),
- verticalArrangement = Arrangement.Center,
- horizontalAlignment = Alignment.CenterHorizontally
- ) {
- Image(
- painter = painterResource(R.drawable.logo_ulstu),
- contentDescription = "Logo",
- modifier = Modifier.size(200.dp)
- )
- Spacer(modifier = Modifier.height(16.dp))
-
- TextField(
- value = username,
- onValueChange = { username = it },
- label = { Text("Логин") },
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp),
- textStyle = TextStyle(fontSize = 16.sp),
- colors = TextFieldDefaults.textFieldColors(
- cursorColor = LightBluePolitech,
- backgroundColor = Color.White,
- textColor = LightBluePolitech,
- unfocusedLabelColor = LightBluePolitech,
- focusedIndicatorColor = LightBluePolitech,
- unfocusedIndicatorColor = LightBluePolitech,
- focusedLabelColor = LightBluePolitech
- ),
- singleLine = true
- )
- Spacer(modifier = Modifier.height(8.dp))
-
- TextField(
- value = email,
- onValueChange = { email = it },
- label = { Text("Почта") },
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp),
- textStyle = TextStyle(fontSize = 16.sp),
- colors = TextFieldDefaults.textFieldColors(
- cursorColor = LightBluePolitech,
- backgroundColor = Color.White,
- textColor = LightBluePolitech,
- unfocusedLabelColor = LightBluePolitech,
- focusedIndicatorColor = LightBluePolitech,
- unfocusedIndicatorColor = LightBluePolitech,
- focusedLabelColor = LightBluePolitech
- ),
- singleLine = true
- )
- Spacer(modifier = Modifier.height(8.dp))
-
- TextField(
- value = password,
- onValueChange = { password = it },
- label = { Text("Пароль") },
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp),
- textStyle = TextStyle(fontSize = 16.sp),
- colors = TextFieldDefaults.textFieldColors(
- backgroundColor = Color.White,
- textColor = LightBluePolitech,
- unfocusedLabelColor = LightBluePolitech,
- focusedIndicatorColor = LightBluePolitech,
- unfocusedIndicatorColor = LightBluePolitech,
- focusedLabelColor = LightBluePolitech
- ),
- singleLine = true,
- visualTransformation = PasswordVisualTransformation()
- )
- Spacer(modifier = Modifier.height(8.dp))
-
- TextField(
- value = confirmPassword,
- onValueChange = { confirmPassword = it },
- label = { Text("Подтвердите пароль") },
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp),
- textStyle = TextStyle(fontSize = 16.sp),
- colors = TextFieldDefaults.textFieldColors(
- backgroundColor = Color.White,
- textColor = LightBluePolitech,
- unfocusedLabelColor = LightBluePolitech,
- focusedIndicatorColor = LightBluePolitech,
- unfocusedIndicatorColor = LightBluePolitech,
- focusedLabelColor = LightBluePolitech
- ),
- singleLine = true,
- visualTransformation = PasswordVisualTransformation()
- )
- Spacer(modifier = Modifier.height(8.dp))
-
- Button(
- onClick = {
- if (password == confirmPassword) {
- val newUser = User(null, name = username, email = email, password = password)
- userViewModel.insertUser(newUser)
- navController.navigate("login")
- }
- },
- colors = ButtonDefaults.buttonColors(backgroundColor = LightBluePolitech),
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp)
- ) {
- Text(text = "Зарегистрироваться", color = Color.White)
- }
- Spacer(modifier = Modifier.height(8.dp))
-
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/labwork/repository/BicycleRepository.kt b/app/src/main/java/com/example/labwork/repository/BicycleRepository.kt
deleted file mode 100644
index 81d696f..0000000
--- a/app/src/main/java/com/example/labwork/repository/BicycleRepository.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.example.labwork.repository
-
-import androidx.paging.PagingData
-import com.example.labwork.models.Bicycle
-import kotlinx.coroutines.flow.Flow
-
-interface BicycleRepository {
- suspend fun insertBicycle(bicycle: Bicycle)
- suspend fun updateBicycle(bicycle: Bicycle)
- suspend fun deleteBicycle(bicycle: Bicycle)
- suspend fun getAllBicycles(): List
- fun getBicycles(): Flow>
- suspend fun getBicyclesByUserId(userId: Int): List
- suspend fun getBicycleById(bicycleId: Int?): Bicycle
-}
diff --git a/app/src/main/java/com/example/labwork/repository/OfflineBicycleRepository.kt b/app/src/main/java/com/example/labwork/repository/OfflineBicycleRepository.kt
deleted file mode 100644
index 64a1b51..0000000
--- a/app/src/main/java/com/example/labwork/repository/OfflineBicycleRepository.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.example.labwork.repository
-
-import androidx.paging.Pager
-import androidx.paging.PagingConfig
-import androidx.paging.PagingData
-import com.example.labwork.database.DAO.BicycleDao
-import com.example.labwork.database.Paging.BicyclePagingSource
-import com.example.labwork.models.Bicycle
-import kotlinx.coroutines.flow.Flow
-
-class OfflineBicycleRepository(private val bicycleDao: BicycleDao) : BicycleRepository {
- override suspend fun insertBicycle(bicycle: Bicycle) {
- bicycleDao.insertBicycle(bicycle)
- }
-
- override suspend fun updateBicycle(bicycle: Bicycle) {
- bicycleDao.updateBicycle(bicycle)
- }
-
- override suspend fun deleteBicycle(bicycle: Bicycle) {
- bicycleDao.deleteBicycle(bicycle)
- }
-
- override suspend fun getAllBicycles(): List {
- return bicycleDao.getAllBicycles()
- }
-
- override suspend fun getBicyclesByUserId(userId: Int): List {
- return bicycleDao.getBicyclesByUserId(userId)
- }
-
- override suspend fun getBicycleById(bicycleId: Int?): Bicycle {
- return bicycleDao.getBicycleById(bicycleId)
- }
-
- override fun getBicycles(): Flow> = Pager(config = PagingConfig(pageSize = 3, jumpThreshold = 3, initialLoadSize = 3) )
- {
- BicyclePagingSource(bicycleDao)
- }.flow
-
-}
-
-
-
diff --git a/app/src/main/java/com/example/labwork/repository/OfflineUserRepository.kt b/app/src/main/java/com/example/labwork/repository/OfflineUserRepository.kt
deleted file mode 100644
index fcea650..0000000
--- a/app/src/main/java/com/example/labwork/repository/OfflineUserRepository.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.example.labwork.repository
-
-import com.example.labwork.database.DAO.BicycleDao
-import com.example.labwork.database.DAO.UserDao
-import com.example.labwork.models.Bicycle
-import com.example.labwork.models.User
-
-class OfflineUserRepository(private val userDao: UserDao) : UserRepository {
- override suspend fun getAllUsers(): List {
- return userDao.getAllUsers()
- }
-
- override suspend fun insertUser(user: User) {
- userDao.insertUser(user)
- }
-
- override suspend fun updateUser(user: User) {
- userDao.updateUser(user)
- }
-
- override suspend fun deleteUser(user: User) {
- userDao.deleteUser(user)
- }
-
- override suspend fun getUserById(userId: Int): User {
- return userDao.getUserById(userId)
- }
-
- override suspend fun getUserByEmail(email: String): User {
- return userDao.getUserByEmail(email)
- }
-
- override suspend fun getUserByEmailAndPassword(email: String, password: String): User? {
- return userDao.getUserByEmailAndPassword(email, password)
- }
-
-}
-
diff --git a/app/src/main/java/com/example/labwork/repository/UserRepository.kt b/app/src/main/java/com/example/labwork/repository/UserRepository.kt
deleted file mode 100644
index 919bd1d..0000000
--- a/app/src/main/java/com/example/labwork/repository/UserRepository.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.example.labwork.repository
-
-import com.example.labwork.models.User
-
-interface UserRepository {
- suspend fun getAllUsers(): List
- suspend fun insertUser(user: User)
- suspend fun updateUser(user: User)
- suspend fun deleteUser(user: User)
- suspend fun getUserById(userId: Int): User
- suspend fun getUserByEmail(email: String): User
- suspend fun getUserByEmailAndPassword(email: String, password: String): User?
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/labwork/ui/theme/Color.kt b/app/src/main/java/com/example/labwork/ui/theme/Color.kt
deleted file mode 100644
index 0e7ae4f..0000000
--- a/app/src/main/java/com/example/labwork/ui/theme/Color.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.example.labwork.ui.theme
-
-import androidx.compose.ui.graphics.Color
-
-val Purple80 = Color(0xFFD0BCFF)
-val PurpleGrey80 = Color(0xFFCCC2DC)
-val Pink80 = Color(0xFFEFB8C8)
-
-val Purple40 = Color(0xFF6650a4)
-val PurpleGrey40 = Color(0xFF625b71)
-val Pink40 = Color(0xFF7D5260)
-
-val LightBluePolitech = Color(0xFF0158AF)
-val DarkBluePolitech = Color(0xFF123E6D)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/labwork/viewmodel/AppViewModelProvider.kt b/app/src/main/java/com/example/labwork/viewmodel/AppViewModelProvider.kt
deleted file mode 100644
index b049da9..0000000
--- a/app/src/main/java/com/example/labwork/viewmodel/AppViewModelProvider.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.example.labwork.viewmodel
-
-import androidx.lifecycle.ViewModelProvider
-import androidx.lifecycle.viewmodel.CreationExtras
-import androidx.lifecycle.viewmodel.initializer
-import androidx.lifecycle.viewmodel.viewModelFactory
-import com.example.labwork.BicycleRentApplication
-
-object AppViewModelProvider {
- val Factory = viewModelFactory {
- initializer {
- BicycleScreenViewModel(BicycleRentApplication().container.bicycleRepository)
- }
- initializer {
- BicycleViewModel(BicycleRentApplication().container.bicycleRepository)
- }
- initializer {
- UserViewModel(BicycleRentApplication().container.userRepository)
- }
-
- }
-}
-
-fun CreationExtras.BicycleRentApplication(): BicycleRentApplication =
- (this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as BicycleRentApplication)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/labwork/viewmodel/BicycleScreenViewModel.kt b/app/src/main/java/com/example/labwork/viewmodel/BicycleScreenViewModel.kt
deleted file mode 100644
index 6f55fba..0000000
--- a/app/src/main/java/com/example/labwork/viewmodel/BicycleScreenViewModel.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.example.labwork.viewmodel
-
-import androidx.lifecycle.ViewModel
-import androidx.paging.PagingData
-import com.example.labwork.models.Bicycle
-import com.example.labwork.repository.BicycleRepository
-import kotlinx.coroutines.flow.Flow
-
-class BicycleScreenViewModel(private val bicycleRepository: BicycleRepository) : ViewModel() {
- val bicycles: Flow> = bicycleRepository.getBicycles()
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/labwork/viewmodel/BicycleViewModel.kt b/app/src/main/java/com/example/labwork/viewmodel/BicycleViewModel.kt
deleted file mode 100644
index 4935e00..0000000
--- a/app/src/main/java/com/example/labwork/viewmodel/BicycleViewModel.kt
+++ /dev/null
@@ -1,69 +0,0 @@
-package com.example.labwork.viewmodel
-
-import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
-import com.example.labwork.models.Bicycle
-import com.example.labwork.repository.BicycleRepository
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.launch
-
-class BicycleViewModel(private val bicycleRepository: BicycleRepository) : ViewModel() {
- private val _bicycles = MutableLiveData>()
- val bicycles: StateFlow> = MutableStateFlow(emptyList())
-
- private val _currentPage = MutableStateFlow(1)
- val currentPage: StateFlow = _currentPage
-
- fun setCurrentPage(page: Int) {
- _currentPage.value = page
- }
-
- fun getCurrentPage(): Int {
- return _currentPage.value
- }
-
- fun fetchBicycles() {
- viewModelScope.launch {
- val fetchedBicycles = bicycleRepository.getAllBicycles()
- (bicycles as MutableStateFlow).value = fetchedBicycles
- }
- }
- fun fetchAllBicycles() {
- viewModelScope.launch {
- _bicycles.value = bicycleRepository.getAllBicycles()
- }
- }
- fun getAllBicycles() {
- viewModelScope.launch {
- _bicycles.value = bicycleRepository.getAllBicycles()
- }
- }
-
- fun getBicycleById(bicycleId: Int) {
- viewModelScope.launch {
- val bicycle = bicycleRepository.getBicycleById(bicycleId)
- }
- }
-
- fun insertBicycle(bicycle: Bicycle) {
- viewModelScope.launch {
- bicycleRepository.insertBicycle(bicycle)
- }
- }
-
- fun updateBicycle(bicycle: Bicycle) {
- viewModelScope.launch {
- bicycleRepository.updateBicycle(bicycle)
- }
- }
-
- fun deleteBicycle(bicycle: Bicycle) {
- viewModelScope.launch {
- bicycleRepository.deleteBicycle(bicycle)
- }
- }
-
-
-}
diff --git a/app/src/main/java/com/example/labwork/viewmodel/UserViewModel.kt b/app/src/main/java/com/example/labwork/viewmodel/UserViewModel.kt
deleted file mode 100644
index 1ac9a9f..0000000
--- a/app/src/main/java/com/example/labwork/viewmodel/UserViewModel.kt
+++ /dev/null
@@ -1,57 +0,0 @@
-package com.example.labwork.viewmodel
-
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
-import com.example.labwork.models.User
-import com.example.labwork.repository.UserRepository
-import kotlinx.coroutines.launch
-
-class UserViewModel(private val userRepository: UserRepository) : ViewModel() {
- private val _users = MutableLiveData>()
- val users: LiveData> get() = _users
-
- fun getAllUsers() {
- viewModelScope.launch {
- _users.value = userRepository.getAllUsers()
- }
- }
-
- fun getUserById(userId: Int) {
- viewModelScope.launch {
- val user = userRepository.getUserById(userId)
- }
- }
-
- fun insertUser(user: User) {
- viewModelScope.launch {
- userRepository.insertUser(user)
- }
- }
-
- fun updateUser(user: User) {
- viewModelScope.launch {
- userRepository.updateUser(user)
- }
- }
-
- fun deleteUser(user: User) {
- viewModelScope.launch {
- userRepository.deleteUser(user)
- }
- }
-
- suspend fun login(email: String, password: String): Boolean {
- var isSuccess = false
- val user = userRepository.getUserByEmail(email)
- isSuccess = user != null && user.password == password
- return isSuccess
- }
-
- suspend fun getUserByEmail(email: String): User? {
- return userRepository.getUserByEmail(email)
- }
-
-}
-
diff --git a/app/src/main/java/com/example/myapplication/CinemaApplication.kt b/app/src/main/java/com/example/myapplication/CinemaApplication.kt
new file mode 100644
index 0000000..8d426c7
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/CinemaApplication.kt
@@ -0,0 +1,14 @@
+package com.example.myapplication
+
+import android.app.Application
+import com.example.myapplication.database.AppContainer
+import com.example.myapplication.database.AppDataContainer
+
+class BikeApplication : Application() {
+ lateinit var container: AppContainer
+
+ override fun onCreate() {
+ super.onCreate()
+ container = AppDataContainer(this)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/MainComposeActivity.kt b/app/src/main/java/com/example/myapplication/MainComposeActivity.kt
new file mode 100644
index 0000000..4cf65e7
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/MainComposeActivity.kt
@@ -0,0 +1,41 @@
+package com.example.myapplication
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.ui.Modifier
+import com.example.myapplication.composeui.navigation.MainNavbar
+import com.example.myapplication.datastore.DataStoreManager
+import com.example.myapplication.ui.theme.PmudemoTheme
+
+class MainComposeActivity : ComponentActivity() {
+ private val dataStoreManager = DataStoreManager(this)
+ private val isDarkTheme = mutableStateOf(true)
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ application.deleteDatabase("pmy-db")
+ setContent {
+ PmudemoTheme(darkTheme = isDarkTheme.value) {
+ LaunchedEffect(key1 = true) {
+ dataStoreManager.getSettings().collect { setting ->
+ isDarkTheme.value = setting.isDarkTheme
+ }
+ }
+ Surface(
+ modifier = Modifier.fillMaxSize(),
+ color = MaterialTheme.colorScheme.background
+ ) {
+ MainNavbar(
+ isDarkTheme = isDarkTheme,
+ dataStoreManager = dataStoreManager
+ )
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/api/LocalDateTimeSerializer.kt b/app/src/main/java/com/example/myapplication/api/LocalDateTimeSerializer.kt
new file mode 100644
index 0000000..7b312de
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/api/LocalDateTimeSerializer.kt
@@ -0,0 +1,26 @@
+package com.example.myapplication.api
+
+import androidx.room.TypeConverters
+import com.example.myapplication.database.entities.model.LocalDateTimeConverter
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.Decoder
+import kotlinx.serialization.encoding.Encoder
+import org.threeten.bp.LocalDateTime
+import org.threeten.bp.DateTimeUtils.toLocalDateTime
+import org.threeten.bp.format.DateTimeFormatter
+
+@Serializer(forClass = LocalDateTime::class)
+object LocalDateTimeSerializer: KSerializer {
+ override val descriptor: SerialDescriptor =
+ PrimitiveSerialDescriptor("LocalDateTime", PrimitiveKind.STRING)
+
+ override fun serialize(encoder: Encoder, value: LocalDateTime) {
+ encoder.encodeInt(value.year)
+ }
+
+ override fun deserialize(decoder: Decoder): LocalDateTime {
+ val year = decoder.decodeInt()
+ return LocalDateTime.of(year, 1, 1, 0, 0)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/api/MyServerService.kt b/app/src/main/java/com/example/myapplication/api/MyServerService.kt
new file mode 100644
index 0000000..1558ca5
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/api/MyServerService.kt
@@ -0,0 +1,165 @@
+package com.example.myapplication.api
+
+import com.example.myapplication.api.bike.BikeRemote
+import com.example.myapplication.api.rent.RentRemote
+import com.example.myapplication.api.item.ItemFromBikeRemote
+import com.example.myapplication.api.item.ItemRemote
+import com.example.myapplication.api.item.ItemWithBikeRemote
+import com.example.myapplication.api.user.UserRemote
+import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
+import kotlinx.serialization.json.Json
+import kotlinx.serialization.modules.SerializersModule
+import kotlinx.serialization.modules.contextual
+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 MyServerService {
+ @GET("rents")
+ suspend fun getRents(): List
+ @GET("users/{id}")
+ suspend fun getUser(
+ @Path("id") id: Int,
+ ): UserRemote
+
+ @GET("users")
+ suspend fun getUsers(): List
+
+ @POST("users")
+ suspend fun createUser(
+ @Body user: UserRemote,
+ ): UserRemote
+ @PUT("users/{id}")
+ suspend fun updateUser(
+ @Path("id") id: Int,
+ @Body user: UserRemote,
+ ): UserRemote
+ @DELETE("users/{id}")
+ suspend fun deleteUser(
+ @Path("id") id: Int,
+ ): UserRemote
+
+ @GET("bikes")
+ suspend fun getBikes(
+ @Query("_page") page: Int,
+ @Query("_limit") limit: Int,
+ ): List
+
+ @GET("bikes/{id}")
+ suspend fun getBike(
+ @Path("id") id: Int,
+ ): BikeRemote
+
+ @POST("bikes")
+ suspend fun createBike(
+ @Body bike: BikeRemote,
+ ): BikeRemote
+
+ @PUT("bikes/{id}")
+ suspend fun updateBike(
+ @Path("id") id: Int,
+ @Body bike: BikeRemote,
+ ): BikeRemote
+
+ @DELETE("bikes/{id}")
+ suspend fun deleteBike(
+ @Path("id") id: Int,
+ )
+
+ @GET("bikes/{bikeId}/items")
+ suspend fun getItemsForBike(
+ @Path("bikeId") bikeId: Int
+ ): List
+
+ @GET("items/{id}?_expand=bike")
+ suspend fun getItem(
+ @Path("id") id: Int,
+ ): ItemWithBikeRemote
+
+ @POST("items")
+ suspend fun createItem(
+ @Body item: ItemRemote,
+ ): ItemRemote
+
+ @PUT("items/{id}")
+ suspend fun updateItem(
+ @Path("id") id: Int,
+ @Body item: ItemRemote,
+ ): ItemRemote
+
+ @DELETE("items/{id}")
+ suspend fun deleteItem(
+ @Path("id") id: Int,
+ ): ItemFromBikeRemote
+
+ @GET("users/{id}")
+ suspend fun getUserCart(
+ @Path("id") id: Int,
+ ): UserRemote
+
+ @PUT("users/{id}")
+ suspend fun updateUserCart(
+ @Path("id") id: Int,
+ @Body userRemote: UserRemote,
+ ): UserRemote
+
+ @GET("rents")
+ suspend fun getRents(
+ @Query("_page") page: Int,
+ @Query("_limit") limit: Int,
+ ): List
+
+ @GET("rents/{id}")
+ suspend fun getRent(
+ @Path("id") id: Int,
+ ): RentRemote
+
+ @POST("rents")
+ suspend fun createRent(
+ @Body bike: RentRemote,
+ ): RentRemote
+
+ @DELETE("rents/{id}")
+ suspend fun deleteRent(
+ @Path("id") id: Int
+ )
+
+ @PUT("rents/{id}")
+ suspend fun updateRent(
+ @Path("id") id: Int,
+ @Body rentRemote: RentRemote,
+ ): RentRemote
+
+ companion object {
+ private const val BASE_URL = "http://192.168.0.104:8079/"
+ //private const val BASE_URL = "http://10.0.2.2:8079/"
+
+ @Volatile
+ private var INSTANCE: MyServerService? = null
+
+ fun getInstance(): MyServerService {
+ return INSTANCE ?: synchronized(this) {
+ val logger = HttpLoggingInterceptor()
+ logger.level = HttpLoggingInterceptor.Level.BASIC
+ val client = OkHttpClient.Builder().addInterceptor(logger).build()
+ val json = Json {
+ ignoreUnknownKeys = true
+ serializersModule = SerializersModule {
+ contextual(LocalDateTimeSerializer)
+ }
+ } // Создаем экземпляр Json с ignoreUnknownKeys = true
+ return Retrofit.Builder().baseUrl(BASE_URL).client(client)
+ .addConverterFactory(json.asConverterFactory("application/json".toMediaType())) // Применяем конфигурацию Json
+ .build().create(MyServerService::class.java).also { INSTANCE = it }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/api/bike/BikeRemote.kt b/app/src/main/java/com/example/myapplication/api/bike/BikeRemote.kt
new file mode 100644
index 0000000..b84d0b5
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/api/bike/BikeRemote.kt
@@ -0,0 +1,26 @@
+package com.example.myapplication.api.bike
+
+import com.example.myapplication.database.entities.model.Bike
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class BikeRemote(
+ val id: Int = 0,
+ val name: String = "",
+ val description: String = "",
+ val image: ByteArray? = null
+)
+
+fun BikeRemote.toBike(): Bike = Bike(
+ id,
+ name,
+ description,
+ image
+)
+
+fun Bike.toBikeRemote(): BikeRemote = BikeRemote(
+ uid,
+ name,
+ description,
+ image
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/api/bike/BikeRemoteMediator.kt b/app/src/main/java/com/example/myapplication/api/bike/BikeRemoteMediator.kt
new file mode 100644
index 0000000..f9ba76c
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/api/bike/BikeRemoteMediator.kt
@@ -0,0 +1,121 @@
+package com.example.myapplication.api.bike
+
+import androidx.paging.ExperimentalPagingApi
+import androidx.paging.LoadType
+import androidx.paging.PagingState
+import androidx.paging.RemoteMediator
+import androidx.room.withTransaction
+import com.example.myapplication.api.MyServerService
+import com.example.myapplication.api.item.toItem
+import com.example.myapplication.database.AppDatabase
+import com.example.myapplication.database.entities.model.Bike
+import com.example.myapplication.database.entities.repository.OfflineBikeRepository
+import com.example.myapplication.database.entities.repository.OfflineItemRepository
+import com.example.myapplication.database.remotekeys.model.RemoteKeyType
+import com.example.myapplication.database.remotekeys.model.RemoteKeys
+import com.example.myapplication.database.remotekeys.repository.OfflineRemoteKeyRepository
+import retrofit2.HttpException
+import java.io.IOException
+
+@OptIn(ExperimentalPagingApi::class)
+class BikeRemoteMediator(
+ private val service: MyServerService,
+ private val dbBikeRepository: OfflineBikeRepository,
+ private val dbItemRepository: OfflineItemRepository,
+ 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 bikes = service.getBikes(page, state.config.pageSize).map { it.toBike() }
+ val bikesWithItems = bikes.map { bike ->
+ service.getItemsForBike(bike.uid).map {
+ service.getItem(it.id).toItem()
+ }
+ }
+ val endOfPaginationReached = bikes.isEmpty()
+ database.withTransaction {
+ if (loadType == LoadType.REFRESH) {
+ dbRemoteKeyRepository.deleteRemoteKey(RemoteKeyType.CINEMA)
+ dbItemRepository.clearItems()
+ dbBikeRepository.clearBikes()
+ }
+ val prevKey = if (page == 1) null else page - 1
+ val nextKey = if (endOfPaginationReached) null else page + 1
+ val keys = bikes.map {
+ RemoteKeys(
+ entityId = it.uid,
+ type = RemoteKeyType.CINEMA,
+ prevKey = prevKey,
+ nextKey = nextKey
+ )
+ }
+ dbRemoteKeyRepository.createRemoteKeys(keys)
+ dbBikeRepository.insertBikes(bikes)
+ bikesWithItems.forEach {
+ try {
+ dbItemRepository.insertItems(it)
+ } catch (_: Exception) {
+ }
+ }
+ }
+ 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 { bike ->
+ dbRemoteKeyRepository.getAllRemoteKeys(bike.uid, RemoteKeyType.CINEMA)
+ }
+ }
+
+ private suspend fun getRemoteKeyForFirstItem(state: PagingState): RemoteKeys? {
+ return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
+ ?.let { bike ->
+ dbRemoteKeyRepository.getAllRemoteKeys(bike.uid, RemoteKeyType.CINEMA)
+ }
+ }
+
+ private suspend fun getRemoteKeyClosestToCurrentPosition(
+ state: PagingState
+ ): RemoteKeys? {
+ return state.anchorPosition?.let { position ->
+ state.closestItemToPosition(position)?.uid?.let { bikeUid ->
+ dbRemoteKeyRepository.getAllRemoteKeys(bikeUid, RemoteKeyType.CINEMA)
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/api/bike/RestBikeRepository.kt b/app/src/main/java/com/example/myapplication/api/bike/RestBikeRepository.kt
new file mode 100644
index 0000000..f73e903
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/api/bike/RestBikeRepository.kt
@@ -0,0 +1,88 @@
+package com.example.myapplication.api.bike
+
+import android.util.Log
+import androidx.paging.ExperimentalPagingApi
+import androidx.paging.Pager
+import androidx.paging.PagingConfig
+import androidx.paging.PagingData
+import com.example.myapplication.api.MyServerService
+import com.example.myapplication.database.AppContainer
+import com.example.myapplication.database.AppDatabase
+import com.example.myapplication.database.entities.model.Bike
+import com.example.myapplication.database.entities.model.BikeWithItems
+import com.example.myapplication.database.entities.model.ItemFromBike
+import com.example.myapplication.database.entities.repository.BikeRepository
+import com.example.myapplication.database.entities.repository.OfflineBikeRepository
+import com.example.myapplication.database.entities.repository.OfflineItemRepository
+import com.example.myapplication.database.remotekeys.repository.OfflineRemoteKeyRepository
+import kotlinx.coroutines.flow.Flow
+
+class RestBikeRepository(
+ private val service: MyServerService,
+ private val dbBikeRepository: OfflineBikeRepository,
+ private val dbItemRepository: OfflineItemRepository,
+ private val dbRemoteKeyRepository: OfflineRemoteKeyRepository,
+ private val database: AppDatabase
+) : BikeRepository {
+ override fun getAllBikes(): Flow> {
+ Log.d(RestBikeRepository::class.simpleName, "Get bikes")
+
+ val pagingSourceFactory = { dbBikeRepository.getAllBikesPagingSource() }
+
+ @OptIn(ExperimentalPagingApi::class)
+ return Pager(
+ config = PagingConfig(
+ pageSize = AppContainer.LIMIT,
+ enablePlaceholders = false
+ ),
+ remoteMediator = BikeRemoteMediator(
+ service,
+ dbBikeRepository,
+ dbItemRepository,
+ dbRemoteKeyRepository,
+ database,
+ ),
+ pagingSourceFactory = pagingSourceFactory
+ ).flow
+ }
+
+ override suspend fun getBike(uid: Int): BikeWithItems {
+ val bike = service.getBike(uid).toBike()
+
+ val items = service.getItemsForBike(uid).map { x ->
+ ItemFromBike(
+ x.id,
+ x.dateTime,
+ x.weight,
+ x.maxCount - service.getRents().flatMap { rent ->
+ rent.items.filter { item -> item.id == x.id }
+ }.sumOf { item -> item.count },
+ uid
+ )
+ }
+ return BikeWithItems(bike, items)
+ }
+
+ override suspend fun insertBike(bike: Bike) {
+ service.createBike(bike.toBikeRemote()).toBike()
+ }
+
+ override suspend fun updateBike(bike: Bike) {
+ service.updateBike(bike.uid, bike.toBikeRemote()).toBike()
+ }
+
+ override suspend fun deleteBike(bike: Bike) {
+ val cart = service.getUsers()
+ cart.forEach { userRemote ->
+ userRemote.items = userRemote.items.filter { x -> x.bikeId != bike.uid }
+ userRemote.id?.let { service.updateUserCart(it, userRemote) }
+ }
+ val rents = service.getRents()
+ rents.forEach { rentRemote ->
+ rentRemote.items = rentRemote.items.filter { x -> x.bikeId != bike.uid }
+ service.updateRent(rentRemote.id, rentRemote)
+ }
+ service.deleteBike(bike.uid)
+ dbBikeRepository.deleteBike(bike)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/api/item/BikeWithItemsRemote.kt b/app/src/main/java/com/example/myapplication/api/item/BikeWithItemsRemote.kt
new file mode 100644
index 0000000..b279113
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/api/item/BikeWithItemsRemote.kt
@@ -0,0 +1,57 @@
+package com.example.myapplication.api.item
+
+/*
+@Serializable
+data class BikeWithItemsRemote(
+ val id: Int = 0,
+ val name: String = "",
+ val description: String = "",
+ val image: ByteArray? = null
+ @SerialName("items")
+ val items: List,
+) {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (javaClass != other?.javaClass) return false
+
+ other as BikeWithItemsRemote
+
+ if (id != other.id) return false
+ if (name != other.name) return false
+ if (description != other.description) return false
+ if (image != null) {
+ if (other.image == null) return false
+ if (!image.contentEquals(other.image)) return false
+ } else if (other.image != null) return false
+ if (items != other.items) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = id
+ result = 31 * result + name.hashCode()
+ result = 31 * result + description.hashCode()
+ result = 31 * result + (image?.contentHashCode() ?: 0)
+ result = 31 * result + items.hashCode()
+ return result
+ }
+}
+
+fun BikeWithItemsRemote.toBikeWithItems(): BikeWithItems = BikeWithItems(
+ Bike(
+ id,
+ name,
+ description,
+ image
+ ),
+ items.map { x -> x.toItemFromBike() }
+)
+
+fun Bike.toBikeWithItemsRemote(): BikeWithItemsRemote = BikeWithItemsRemote(
+ uid,
+ name,
+ description,
+ image,
+ items = emptyList()
+)*/
diff --git a/app/src/main/java/com/example/myapplication/api/item/ItemFromBikeRemote.kt b/app/src/main/java/com/example/myapplication/api/item/ItemFromBikeRemote.kt
new file mode 100644
index 0000000..7fb7bdc
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/api/item/ItemFromBikeRemote.kt
@@ -0,0 +1,33 @@
+package com.example.myapplication.api.item
+
+import com.example.myapplication.database.entities.model.ItemFromBike
+import kotlinx.serialization.Contextual
+import kotlinx.serialization.Serializable
+import org.threeten.bp.LocalDateTime
+
+@Serializable
+class ItemFromBikeRemote(
+ val id: Int = 0,
+ @Contextual
+ val dateTime: LocalDateTime = LocalDateTime.MIN,
+ val weight: Double = 0.0,
+ val maxCount: Int = 0,
+ val availableCount: Int = 0,
+ val bikeId: Int = 0,
+)
+
+fun ItemFromBikeRemote.toItemFromBike(): ItemFromBike = ItemFromBike(
+ id,
+ dateTime,
+ weight,
+ availableCount,
+ bikeId
+)
+
+fun ItemFromBike.toItemFromBikeRemote(): ItemFromBikeRemote = ItemFromBikeRemote(
+ uid,
+ dateTime,
+ weight,
+ availableCount,
+ bikeId
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/api/item/ItemFromCartRemote.kt b/app/src/main/java/com/example/myapplication/api/item/ItemFromCartRemote.kt
new file mode 100644
index 0000000..be38e9b
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/api/item/ItemFromCartRemote.kt
@@ -0,0 +1,19 @@
+package com.example.myapplication.api.item
+
+import com.example.myapplication.api.bike.BikeRemote
+import com.example.myapplication.api.bike.toBike
+import com.example.myapplication.database.entities.model.ItemFromCart
+import kotlinx.serialization.Serializable
+import org.threeten.bp.LocalDateTime
+
+@Serializable
+class ItemFromCartRemote(
+ val id: Int = 0,
+ var count: Int = 0,
+ var bikeId: Int = 0,
+)
+
+fun ItemFromCartRemote.toItemFromCart(bike: BikeRemote, dateTime: LocalDateTime, weight: Double, availableCount: Int): ItemFromCart =
+ ItemFromCart(
+ id, dateTime, weight, availableCount, count, bike.id, bike.toBike()
+ )
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/api/item/ItemFromRentRemote.kt b/app/src/main/java/com/example/myapplication/api/item/ItemFromRentRemote.kt
new file mode 100644
index 0000000..c44fd55
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/api/item/ItemFromRentRemote.kt
@@ -0,0 +1,22 @@
+package com.example.myapplication.api.item
+
+import com.example.myapplication.api.bike.BikeRemote
+import com.example.myapplication.api.bike.toBike
+import com.example.myapplication.database.entities.model.ItemFromRent
+import kotlinx.serialization.Contextual
+import kotlinx.serialization.Serializable
+import org.threeten.bp.LocalDateTime
+
+@Serializable
+class ItemFromRentRemote(
+ val id: Int = 0,
+ @Contextual val dateTime: LocalDateTime = LocalDateTime.MIN,
+ val frozenWeight: Double = 0.0,
+ val count: Int = 0,
+ val bikeId: Int = 0,
+)
+
+fun ItemFromRentRemote.toItemFromRent(bike: BikeRemote): ItemFromRent =
+ ItemFromRent(
+ id, dateTime, frozenWeight, count, bikeId, bike.toBike()
+ )
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/api/item/ItemRemote.kt b/app/src/main/java/com/example/myapplication/api/item/ItemRemote.kt
new file mode 100644
index 0000000..7d6d4af
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/api/item/ItemRemote.kt
@@ -0,0 +1,32 @@
+package com.example.myapplication.api.item
+
+import com.example.myapplication.database.entities.model.Item
+import kotlinx.serialization.Contextual
+import kotlinx.serialization.Serializable
+import org.threeten.bp.LocalDateTime
+
+@Serializable
+data class ItemRemote(
+ val id: Int = 0,
+ @Contextual
+ val dateTime: LocalDateTime,
+ val weight: Double,
+ val maxCount: Int,
+ val bikeId: Int = 0
+)
+
+fun ItemRemote.toItem(): Item = Item(
+ id,
+ dateTime,
+ weight,
+ maxCount,
+ bikeId
+)
+
+fun Item.toItemRemote(): ItemRemote = ItemRemote(
+ uid,
+ dateTime,
+ weight,
+ maxCount,
+ bikeId
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/api/item/ItemWithBikeRemote.kt b/app/src/main/java/com/example/myapplication/api/item/ItemWithBikeRemote.kt
new file mode 100644
index 0000000..c963da6
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/api/item/ItemWithBikeRemote.kt
@@ -0,0 +1,26 @@
+package com.example.myapplication.api.item
+
+import com.example.myapplication.api.bike.BikeRemote
+import com.example.myapplication.database.entities.model.Item
+import kotlinx.serialization.Contextual
+import kotlinx.serialization.Serializable
+import org.threeten.bp.LocalDateTime
+
+@Serializable
+data class ItemWithBikeRemote(
+ val id: Int = 0,
+ @Contextual
+ val dateTime: LocalDateTime,
+ val weight: Double,
+ val maxCount: Int,
+ val bikeId: Int = 0,
+ val bike: BikeRemote,
+)
+
+fun ItemWithBikeRemote.toItem(): Item = Item(
+ id,
+ dateTime,
+ weight,
+ maxCount,
+ bikeId
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/api/item/RestItemRepository.kt b/app/src/main/java/com/example/myapplication/api/item/RestItemRepository.kt
new file mode 100644
index 0000000..9b18a9b
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/api/item/RestItemRepository.kt
@@ -0,0 +1,51 @@
+package com.example.myapplication.api.item
+
+import com.example.myapplication.api.MyServerService
+import com.example.myapplication.database.entities.model.Item
+import com.example.myapplication.database.entities.repository.OfflineRentItemRepository
+import com.example.myapplication.database.entities.repository.OfflineItemRepository
+import com.example.myapplication.database.entities.repository.OfflineUserItemRepository
+import com.example.myapplication.database.entities.repository.ItemRepository
+
+class RestItemRepository(
+ private val service: MyServerService,
+ private val dbItemRepository: OfflineItemRepository,
+ private val dbUserItemRepository: OfflineUserItemRepository,
+ private val dbRentItemRepository: OfflineRentItemRepository,
+) : ItemRepository {
+ override suspend fun getItem(uid: Int): Item {
+ return service.getItem(uid).toItem()
+ }
+
+ override suspend fun insertItem(item: Item) {
+ dbItemRepository.insertItem(
+ service.createItem(item.toItemRemote()).toItem()
+ )
+ }
+
+ override suspend fun updateItem(item: Item) {
+ dbItemRepository.updateItem(
+ service.updateItem(
+ item.uid,
+ item.toItemRemote()
+ ).toItem()
+ )
+ }
+
+ override suspend fun deleteItem(item: Item) {
+ val cart = service.getUsers()
+ cart.forEach { userRemote ->
+ userRemote.items = userRemote.items.filter { x -> x.id != item.uid }
+ userRemote.id?.let { service.updateUserCart(it, userRemote) }
+ }
+ val rents = service.getRents()
+ rents.forEach { rentRemote ->
+ rentRemote.items = rentRemote.items.filter { x -> x.id != item.uid }
+ service.updateRent(rentRemote.id, rentRemote)
+ }
+ service.deleteItem(item.uid)
+ dbUserItemRepository.deleteItemsByUid(item.uid)
+ dbRentItemRepository.deleteItemsByUid(item.uid)
+ dbItemRepository.deleteItem(item)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/api/rent/RentRemote.kt b/app/src/main/java/com/example/myapplication/api/rent/RentRemote.kt
new file mode 100644
index 0000000..5a5df76
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/api/rent/RentRemote.kt
@@ -0,0 +1,18 @@
+package com.example.myapplication.api.rent
+
+import com.example.myapplication.api.item.ItemFromRentRemote
+import com.example.myapplication.database.entities.model.Rent
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class RentRemote(
+ val id: Int = 0, val userId: Int = 0, var items: List = emptyList()
+)
+
+fun RentRemote.toRent(): Rent = Rent(
+ id, userId
+)
+
+fun Rent.toRentRemote(): RentRemote = RentRemote(
+ uid, userId!!, items = emptyList()
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/api/rent/RentRemoteMediator.kt b/app/src/main/java/com/example/myapplication/api/rent/RentRemoteMediator.kt
new file mode 100644
index 0000000..5219b7f
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/api/rent/RentRemoteMediator.kt
@@ -0,0 +1,106 @@
+package com.example.myapplication.api.rent
+
+import androidx.paging.ExperimentalPagingApi
+import androidx.paging.LoadType
+import androidx.paging.PagingState
+import androidx.paging.RemoteMediator
+import androidx.room.withTransaction
+import com.example.myapplication.api.MyServerService
+import com.example.myapplication.database.AppDatabase
+import com.example.myapplication.database.entities.model.Rent
+import com.example.myapplication.database.entities.repository.OfflineRentRepository
+import com.example.myapplication.database.remotekeys.model.RemoteKeyType
+import com.example.myapplication.database.remotekeys.model.RemoteKeys
+import com.example.myapplication.database.remotekeys.repository.OfflineRemoteKeyRepository
+import retrofit2.HttpException
+import java.io.IOException
+
+@OptIn(ExperimentalPagingApi::class)
+class RentRemoteMediator(
+ private val service: MyServerService,
+ private val dbRentRepository: OfflineRentRepository,
+ 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 rents = service.getRents(page, state.config.pageSize).map { it.toRent() }
+ val endOfPaginationReached = rents.isEmpty()
+ database.withTransaction {
+ if (loadType == LoadType.REFRESH) {
+ dbRemoteKeyRepository.deleteRemoteKey(RemoteKeyType.ORDER)
+ dbRentRepository.clearRents()
+ }
+ val prevKey = if (page == 1) null else page - 1
+ val nextKey = if (endOfPaginationReached) null else page + 1
+ val keys = rents.map {
+ RemoteKeys(
+ entityId = it.uid,
+ type = RemoteKeyType.ORDER,
+ prevKey = prevKey,
+ nextKey = nextKey
+ )
+ }
+ dbRemoteKeyRepository.createRemoteKeys(keys)
+ dbRentRepository.insertRents(rents)
+ }
+ 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 { rent ->
+ dbRemoteKeyRepository.getAllRemoteKeys(rent.uid, RemoteKeyType.ORDER)
+ }
+ }
+
+ private suspend fun getRemoteKeyForFirstItem(state: PagingState): RemoteKeys? {
+ return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
+ ?.let { rent ->
+ dbRemoteKeyRepository.getAllRemoteKeys(rent.uid, RemoteKeyType.ORDER)
+ }
+ }
+
+ private suspend fun getRemoteKeyClosestToCurrentPosition(
+ state: PagingState
+ ): RemoteKeys? {
+ return state.anchorPosition?.let { position ->
+ state.closestItemToPosition(position)?.uid?.let { rentUid ->
+ dbRemoteKeyRepository.getAllRemoteKeys(rentUid, RemoteKeyType.ORDER)
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/api/rent/RestRentRepository.kt b/app/src/main/java/com/example/myapplication/api/rent/RestRentRepository.kt
new file mode 100644
index 0000000..8c9d2e7
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/api/rent/RestRentRepository.kt
@@ -0,0 +1,77 @@
+package com.example.myapplication.api.rent
+
+import android.util.Log
+import androidx.paging.ExperimentalPagingApi
+import androidx.paging.Pager
+import androidx.paging.PagingConfig
+import androidx.paging.PagingData
+import com.example.myapplication.api.MyServerService
+import com.example.myapplication.api.bike.toBikeRemote
+import com.example.myapplication.api.item.toItemFromRent
+import com.example.myapplication.database.AppContainer
+import com.example.myapplication.database.AppDatabase
+import com.example.myapplication.database.entities.model.Rent
+import com.example.myapplication.database.entities.model.RentItemCrossRef
+import com.example.myapplication.database.entities.model.ItemFromRent
+import com.example.myapplication.database.entities.repository.OfflineBikeRepository
+import com.example.myapplication.database.entities.repository.OfflineRentRepository
+import com.example.myapplication.database.entities.repository.OfflineRentItemRepository
+import com.example.myapplication.database.entities.repository.RentRepository
+import com.example.myapplication.database.remotekeys.repository.OfflineRemoteKeyRepository
+import kotlinx.coroutines.flow.Flow
+
+class RestRentRepository(
+ private val service: MyServerService,
+ private val dbRentRepository: OfflineRentRepository,
+ private val dbBikeRepository: OfflineBikeRepository,
+ private val dbRentItemRepository: OfflineRentItemRepository,
+ private val dbRemoteKeyRepository: OfflineRemoteKeyRepository,
+ private val database: AppDatabase
+) : RentRepository {
+ override fun getAllRents(userId: Int?): Flow> {
+ Log.d(RestRentRepository::class.simpleName, "Get rents")
+
+ val pagingSourceFactory = { dbRentRepository.getAllRentsPagingSource(userId) }
+
+ @OptIn(ExperimentalPagingApi::class)
+ return Pager(
+ config = PagingConfig(
+ pageSize = AppContainer.LIMIT,
+ enablePlaceholders = false
+ ),
+ remoteMediator = RentRemoteMediator(
+ service,
+ dbRentRepository,
+ dbRemoteKeyRepository,
+ database,
+ ),
+ pagingSourceFactory = pagingSourceFactory
+ ).flow
+ }
+
+ override suspend fun getRent(uid: Int): List {
+ val rent = service.getRent(uid)
+
+ dbRentItemRepository.deleteRentItems(uid)
+ rent.items.map {
+ dbRentItemRepository.insertRentItem(
+ RentItemCrossRef(
+ uid,
+ it.id,
+ it.frozenWeight,
+ it.count
+ )
+ )
+ }
+ return rent.items.map { x -> x.toItemFromRent(dbBikeRepository.getBike(x.bikeId).bike.toBikeRemote()) }
+ }
+
+ override suspend fun insertRent(rent: Rent): Long {
+ return dbRentRepository.insertRent(service.createRent(rent.toRentRemote()).toRent())
+ }
+
+ override suspend fun deleteRent(rent: Rent) {
+ service.deleteRent(rent.uid)
+ dbRentRepository.deleteRent(rent)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/api/rentitem/RestRentItemRepository.kt b/app/src/main/java/com/example/myapplication/api/rentitem/RestRentItemRepository.kt
new file mode 100644
index 0000000..c36aa05
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/api/rentitem/RestRentItemRepository.kt
@@ -0,0 +1,39 @@
+package com.example.myapplication.api.rentitem
+
+import com.example.myapplication.api.MyServerService
+import com.example.myapplication.api.item.ItemFromRentRemote
+import com.example.myapplication.api.item.toItem
+import com.example.myapplication.database.entities.model.RentItemCrossRef
+import com.example.myapplication.database.entities.repository.OfflineRentItemRepository
+import com.example.myapplication.database.entities.repository.RentItemRepository
+
+class RestRentItemRepository(
+ private val service: MyServerService,
+ private val dbRentItemRepository: OfflineRentItemRepository
+) : RentItemRepository {
+ override suspend fun insertRentItem(rentItemCrossRef: RentItemCrossRef) {
+ var rentRemote = service.getRent(rentItemCrossRef.rentId)
+ val item = service.getItem(rentItemCrossRef.itemId).toItem()
+
+ val itemFromRent = ItemFromRentRemote(
+ item.uid,
+ item.dateTime,
+ item.weight,
+ rentItemCrossRef.count,
+ item.bikeId
+ )
+
+ val updatedItems = rentRemote.items.toMutableList()
+ updatedItems.add(itemFromRent)
+
+ rentRemote = rentRemote.copy(items = updatedItems)
+ service.updateRent(rentItemCrossRef.rentId, rentRemote)
+ dbRentItemRepository.insertRentItem(rentItemCrossRef)
+ }
+
+ override suspend fun updateRentItem(rentItemCrossRef: RentItemCrossRef) {
+ }
+
+ override suspend fun deleteRentItem(rentItemCrossRef: RentItemCrossRef) {
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/api/user/RestUserRepository.kt b/app/src/main/java/com/example/myapplication/api/user/RestUserRepository.kt
new file mode 100644
index 0000000..0a2aa6f
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/api/user/RestUserRepository.kt
@@ -0,0 +1,76 @@
+package com.example.myapplication.api.user
+
+import android.util.Log
+import com.example.myapplication.api.MyServerService
+import com.example.myapplication.api.item.toItemFromCart
+import com.example.myapplication.database.entities.model.ItemFromCart
+import com.example.myapplication.database.entities.model.User
+import com.example.myapplication.database.entities.model.UserItemCrossRef
+import com.example.myapplication.database.entities.repository.OfflineUserRepository
+import com.example.myapplication.database.entities.repository.OfflineUserItemRepository
+import com.example.myapplication.database.entities.repository.UserRepository
+import kotlinx.coroutines.flow.Flow
+
+class RestUserRepository(
+ private val service: MyServerService,
+ private val dbUserRepository: OfflineUserRepository,
+ private val dbUserItemRepository: OfflineUserItemRepository,
+) : UserRepository {
+ override suspend fun getAllUsers(): List {
+ val existUsers = dbUserRepository.getAllUsers().associateBy { it.uid }.toMutableMap()
+
+ service.getUsers()
+ .map { it.toUser() }
+ .forEach { user ->
+ val existUser = existUsers[user.uid]
+ if (existUser == null) {
+ dbUserRepository.insertUser(user)
+ } else if (existUser != user) {
+ dbUserRepository.updateUser(user)
+ }
+ existUsers[user.uid] = user
+ }
+
+ return existUsers.map { it.value }.sortedBy { it.uid }
+ }
+
+ override suspend fun getCartByUser(userId: Int): List {
+ val cart = service.getUserCart(userId)
+ dbUserItemRepository.deleteUserItems(userId)
+ cart.items.map { itemFromCartRemote ->
+ dbUserItemRepository.insertUserItem(
+ UserItemCrossRef(
+ userId,
+ itemFromCartRemote.id,
+ itemFromCartRemote.count
+ )
+ )
+ }
+
+ return cart.items.map {
+ val item = service.getItem(it.id)
+ it.toItemFromCart(
+ item.bike,
+ item.dateTime,
+ item.weight,
+ item.maxCount - service.getRents().flatMap { rent ->
+ rent.items.filter { item -> item.id == it.id }
+ }.sumOf { item -> item.count })
+ }
+ }
+
+ override suspend fun insertUser(user: User) {
+ service.createUser(user.toUserRemote()).toUser()
+ }
+
+ override suspend fun updateUser(user: User) {
+ user.uid?.let { service.updateUser(it, user.toUserRemote()).toUser() }
+ }
+
+ override suspend fun deleteUser(user: User) {
+ user.uid?.let { service.deleteUser(it).toUser() }
+ }
+
+ override suspend fun getUserById(idUser: Int?): User? =
+ idUser?.let { service.getUser(it).toUser() }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/api/user/UserRemote.kt b/app/src/main/java/com/example/myapplication/api/user/UserRemote.kt
new file mode 100644
index 0000000..22ea36a
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/api/user/UserRemote.kt
@@ -0,0 +1,25 @@
+package com.example.myapplication.api.user
+
+import com.example.myapplication.api.item.ItemFromCartRemote
+import com.example.myapplication.database.entities.model.User
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class UserRemote(
+ val id: Int? = 0,
+ val login: String = "",
+ val password: String = "",
+ var items: List = emptyList()
+)
+
+fun UserRemote.toUser(): User = User(
+ id,
+ login,
+ password
+)
+
+fun User.toUserRemote(): UserRemote = UserRemote(
+ uid,
+ login,
+ password
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/api/useritem/RestUserItemRepository.kt b/app/src/main/java/com/example/myapplication/api/useritem/RestUserItemRepository.kt
new file mode 100644
index 0000000..f33e466
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/api/useritem/RestUserItemRepository.kt
@@ -0,0 +1,62 @@
+package com.example.myapplication.api.useritem
+
+import com.example.myapplication.api.MyServerService
+import com.example.myapplication.api.item.ItemFromCartRemote
+import com.example.myapplication.api.item.toItem
+import com.example.myapplication.database.entities.model.UserItemCrossRef
+import com.example.myapplication.database.entities.repository.OfflineUserItemRepository
+import com.example.myapplication.database.entities.repository.UserItemRepository
+
+class RestUserItemRepository(
+ private val service: MyServerService,
+ private val dbUserItemRepository: OfflineUserItemRepository
+) : UserItemRepository {
+ override suspend fun insertUserItem(userItemCrossRef: UserItemCrossRef) {
+ var cartItems = service.getUserCart(userItemCrossRef.userId)
+ cartItems.items.forEach { item ->
+ if (item.id == userItemCrossRef.itemId)
+ return
+ }
+ val item = service.getItem(userItemCrossRef.itemId).toItem()
+
+ val itemFromCart = ItemFromCartRemote(
+ item.uid,
+ userItemCrossRef.count,
+ item.bikeId,
+ )
+
+ val updatedItems = cartItems.items.toMutableList()
+ updatedItems.add(itemFromCart)
+
+ cartItems = cartItems.copy(items = updatedItems)
+ service.updateUserCart(userItemCrossRef.userId, cartItems)
+ dbUserItemRepository.insertUserItem(userItemCrossRef)
+ }
+
+ override suspend fun updateUserItem(userItemCrossRef: UserItemCrossRef) {
+ val userRemote = service.getUserCart(userItemCrossRef.userId)
+ if (userItemCrossRef.count <= 0) {
+ userRemote.items =
+ userRemote.items.filter { x -> x.id != userItemCrossRef.itemId }
+ } else
+ userRemote.items.forEach {
+ if (it.id == userItemCrossRef.itemId) {
+ it.count = userItemCrossRef.count
+ }
+ }
+ service.updateUserCart(userItemCrossRef.userId, userRemote)
+ dbUserItemRepository.updateUserItem(userItemCrossRef)
+ }
+
+ override suspend fun deleteUserItem(userItemCrossRef: UserItemCrossRef) {
+ updateUserItem(userItemCrossRef)
+ dbUserItemRepository.deleteUserItem(userItemCrossRef)
+ }
+
+ override suspend fun deleteUserItems(userId: Int) {
+ val userRemote = service.getUserCart(userId)
+ userRemote.items = emptyList()
+ service.updateUserCart(userId, userRemote)
+ dbUserItemRepository.deleteUserItems(userId)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/composeui/Cart.kt b/app/src/main/java/com/example/myapplication/composeui/Cart.kt
new file mode 100644
index 0000000..261cb4e
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/composeui/Cart.kt
@@ -0,0 +1,318 @@
+package com.example.myapplication.composeui
+
+import android.content.res.Configuration
+import android.graphics.BitmapFactory
+import androidx.compose.animation.animateColorAsState
+import androidx.compose.animation.core.animateFloatAsState
+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.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.material.icons.outlined.Delete
+import androidx.compose.material3.Button
+import androidx.compose.material3.DismissDirection
+import androidx.compose.material3.DismissState
+import androidx.compose.material3.DismissValue
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.material3.SwipeToDismiss
+import androidx.compose.material3.Text
+import androidx.compose.material3.rememberDismissState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.draw.scale
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.asImageBitmap
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.res.vectorResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.example.myapplication.R
+import com.example.myapplication.database.entities.composeui.AppViewModelProvider
+import com.example.myapplication.database.entities.composeui.CartUiState
+import com.example.myapplication.database.entities.composeui.CartViewModel
+import com.example.myapplication.database.entities.model.Item
+import com.example.myapplication.database.entities.model.ItemFromCart
+import com.example.myapplication.ui.theme.PmudemoTheme
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import org.threeten.bp.format.DateTimeFormatter
+
+@Composable
+fun Cart(
+ viewModel: CartViewModel = viewModel(factory = AppViewModelProvider.Factory)
+) {
+ val coroutineScope = rememberCoroutineScope()
+ val cartUiState = viewModel.cartUiState
+
+ LaunchedEffect(Unit) {
+ viewModel.refreshState()
+ }
+
+ Cart(
+ cartUiState = cartUiState,
+ modifier = Modifier
+ .padding(all = 10.dp),
+ onSwipe = { item: ItemFromCart, user: Int ->
+ coroutineScope.launch {
+ viewModel.removeFromCart(
+ item = Item(
+ uid = item.uid,
+ dateTime = item.dateTime,
+ weight = item.weight,
+ maxCount = 0,
+ bikeId = item.bikeId
+ ), user = user
+ )
+ }
+ },
+ onChangeCount = { item: ItemFromCart, user: Int, count: Int ->
+ coroutineScope.launch {
+ viewModel.updateFromCart(
+ item = Item(
+ uid = item.uid,
+ dateTime = item.dateTime,
+ weight = item.weight,
+ maxCount = 0,
+ bikeId = item.bikeId
+ ), userId = user, count = count, availableCount = item.availableCount
+ )
+ }
+ },
+ onAddToRent = { items: List, user: Int ->
+ coroutineScope.launch {
+ viewModel.addToRent(items = items, userId = user)
+ }
+ }
+ )
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+private fun Cart(
+ cartUiState: CartUiState,
+ modifier: Modifier,
+ onSwipe: (ItemFromCart, Int) -> Unit,
+ onChangeCount: (ItemFromCart, Int, Int) -> Unit,
+ onAddToRent: (List, Int) -> Unit
+) {
+ LazyColumn(
+ modifier = modifier
+ ) {
+ items(cartUiState.itemList, key = { it.uid.toString() }) { item ->
+ val dismissState: DismissState = rememberDismissState(
+ positionalThreshold = { 200.dp.toPx() }
+ )
+
+ if (dismissState.isDismissed(direction = DismissDirection.EndToStart)) {
+ onSwipe(item, 1)
+ }
+
+ SwipeToDelete(
+ dismissState = dismissState,
+ item = item,
+ onChangeCount = onChangeCount
+ )
+ }
+ }
+ Column {
+ Spacer(modifier = Modifier.weight(1f))
+
+ Button(
+ onClick = { onAddToRent(cartUiState.itemList, 1) },
+ modifier = Modifier
+ .padding(16.dp)
+ .fillMaxWidth()
+ ) { Text("Арендовать") }
+ }
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+private fun SwipeToDelete(
+ dismissState: DismissState,
+ item: ItemFromCart,
+ onChangeCount: (ItemFromCart, Int, Int) -> Unit,
+) {
+ SwipeToDismiss(
+ state = dismissState,
+ directions = setOf(
+ DismissDirection.EndToStart
+ ),
+ background = {
+ val backgroundColor by animateColorAsState(
+ when (dismissState.targetValue) {
+ DismissValue.DismissedToStart -> Color.Red.copy(alpha = 0.8f)
+ else -> MaterialTheme.colorScheme.background
+ },
+ label = ""
+ )
+ val iconScale by animateFloatAsState(
+ targetValue = if (dismissState.targetValue == DismissValue.DismissedToStart) 1.3f else 0.5f,
+ label = ""
+ )
+ Box(
+ Modifier
+ .fillMaxSize()
+ .background(color = backgroundColor)
+ .padding(end = 16.dp),
+ contentAlignment = Alignment.CenterEnd
+ ) {
+ Icon(
+ modifier = Modifier.scale(iconScale),
+ imageVector = Icons.Outlined.Delete,
+ contentDescription = "Delete",
+ tint = Color.White
+ )
+ }
+ },
+ dismissContent = {
+ ItemListItem(
+ item = item,
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(10.dp)
+ .clip(RoundedCornerShape(16.dp))
+ .background(MaterialTheme.colorScheme.secondary),
+ onChangeCount = onChangeCount
+ )
+ }
+ )
+}
+
+@Composable
+private fun ItemListItem(
+ item: ItemFromCart,
+ modifier: Modifier = Modifier,
+ onChangeCount: (ItemFromCart, Int, Int) -> Unit,
+) {
+ var currentCount by remember { mutableStateOf(item.count) }
+
+ val dateFormatter = DateTimeFormatter.ofPattern("yyyy")
+ val formattedDate = dateFormatter.format(item.dateTime)
+ Column {
+/* Text(
+ text = formattedDate,
+ color = MaterialTheme.colorScheme.onBackground,
+ )*/
+ Box(
+ modifier = modifier
+ ) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(8.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.SpaceBetween
+ ) {
+ if (item.bike.image != null)
+ Image(
+ bitmap = BitmapFactory.decodeByteArray(
+ item.bike.image,
+ 0,
+ item.bike.image.size
+ ).asImageBitmap(),
+ contentDescription = null,
+ modifier = Modifier
+ .size(90.dp)
+ .padding(4.dp)
+ )
+
+ Column(
+ modifier = Modifier.weight(1f),
+ verticalArrangement = Arrangement.spacedBy(4.dp)
+ ) {
+ Text(
+ text = "${item.bike.name}\n" +
+ "Вес: ${item.weight}\n" +
+ "${currentCount}/${item.availableCount}\n" +
+ "Год: ${formattedDate}",
+ color = MaterialTheme.colorScheme.onSecondary
+ )
+ }
+
+ Box(
+ modifier = Modifier
+ .background(
+ color = MaterialTheme.colorScheme.onBackground,
+ shape = RoundedCornerShape(10.dp)
+ ) // Задаем фон для кнопок
+ ) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ IconButton(
+ onClick = { onChangeCount(item, 1, --currentCount) }
+ ) {
+ Icon(
+ imageVector = ImageVector.vectorResource(id = R.drawable.minus),
+ contentDescription = "Уменьшить",
+ tint = MaterialTheme.colorScheme.background,
+ modifier = Modifier.size(10.dp)
+ )
+ }
+
+ Text(
+ text = "$currentCount",
+ color = MaterialTheme.colorScheme.background
+ )
+
+ IconButton(
+ onClick = {
+ onChangeCount(
+ item,
+ 1,
+ if (currentCount != item.availableCount) ++currentCount else currentCount
+ )
+ }
+ ) {
+ Icon(
+ imageVector = Icons.Default.Add,
+ contentDescription = "Увеличить",
+ tint = MaterialTheme.colorScheme.background,
+ modifier = Modifier.size(10.dp)
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
+@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+fun CartPreview() {
+ PmudemoTheme {
+ Surface(
+ color = MaterialTheme.colorScheme.background
+ ) {
+ Cart()
+ }
+ }
+}
diff --git a/app/src/main/java/com/example/myapplication/composeui/navigation/MainNavbar.kt b/app/src/main/java/com/example/myapplication/composeui/navigation/MainNavbar.kt
new file mode 100644
index 0000000..99fd961
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/composeui/navigation/MainNavbar.kt
@@ -0,0 +1,243 @@
+package com.example.myapplication.composeui.navigation
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+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
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.foundation.text.KeyboardActions
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.ArrowBack
+import androidx.compose.material.icons.filled.Person
+import androidx.compose.material.icons.filled.Search
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.NavigationBar
+import androidx.compose.material3.NavigationBarItem
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.viewmodel.compose.viewModel
+import androidx.navigation.NavDestination
+import androidx.navigation.NavDestination.Companion.hierarchy
+import androidx.navigation.NavGraph.Companion.findStartDestination
+import androidx.navigation.NavHostController
+import androidx.navigation.NavType
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.composable
+import androidx.navigation.compose.currentBackStackEntryAsState
+import androidx.navigation.compose.rememberNavController
+import androidx.navigation.navArgument
+import com.example.myapplication.composeui.Cart
+import com.example.myapplication.database.entities.composeui.AppViewModelProvider
+import com.example.myapplication.database.entities.composeui.BikeList
+import com.example.myapplication.database.entities.composeui.BikeView
+import com.example.myapplication.database.entities.composeui.CurrentUserViewModel
+import com.example.myapplication.database.entities.composeui.RentList
+import com.example.myapplication.database.entities.composeui.RentView
+import com.example.myapplication.database.entities.composeui.UserProfile
+import com.example.myapplication.database.entities.composeui.edit.BikeEdit
+import com.example.myapplication.database.entities.composeui.edit.ItemEdit
+import com.example.myapplication.datastore.DataStoreManager
+
+@Composable
+fun Topbar(
+ navController: NavHostController,
+ currentScreen: Screen?
+) {
+ var searchQuery by remember { mutableStateOf("") }
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(color = MaterialTheme.colorScheme.primary)
+ ) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(10.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ if (
+ navController.previousBackStackEntry != null
+ && (currentScreen == null || !currentScreen.showInBottomBar)
+ ) {
+ Icon(
+ imageVector = Icons.Filled.ArrowBack,
+ contentDescription = null,
+ modifier = Modifier
+ .size(30.dp)
+ .clickable { navController.navigateUp() },
+ tint = MaterialTheme.colorScheme.secondary
+ )
+ } else
+ Icon(
+ imageVector = Icons.Default.Person,
+ contentDescription = null,
+ modifier = Modifier
+ .size(30.dp)
+ .clickable { navController.navigate(Screen.UserProfile.route) },
+ tint = MaterialTheme.colorScheme.secondary
+ )
+
+ Spacer(modifier = Modifier.width(16.dp))
+
+ BasicTextField(
+ value = searchQuery,
+ onValueChange = { newValue -> searchQuery = newValue },
+ modifier = Modifier
+ .weight(1f)
+ .height(36.dp)
+ .background(
+ color = MaterialTheme.colorScheme.onPrimary,
+ RoundedCornerShape(18.dp)
+ )
+ .padding(start = 13.dp, top = 8.dp),
+ keyboardOptions = KeyboardOptions.Default.copy(
+ imeAction = androidx.compose.ui.text.input.ImeAction.Search
+ ),
+ keyboardActions = KeyboardActions(
+ onSearch = { }
+ )
+ )
+
+ Spacer(modifier = Modifier.width(16.dp))
+
+ Icon(
+ imageVector = Icons.Default.Search,
+ contentDescription = null,
+ modifier = Modifier
+ .size(30.dp)
+ .clickable { },
+ tint = MaterialTheme.colorScheme.secondary
+ )
+ }
+ }
+}
+
+@Composable
+fun Navbar(
+ navController: NavHostController,
+ currentDestination: NavDestination?,
+ modifier: Modifier = Modifier
+) {
+ NavigationBar(modifier = modifier, containerColor = MaterialTheme.colorScheme.primary) {
+ Screen.bottomBarItems.forEach { screen ->
+ NavigationBarItem(
+ icon = {
+ Icon(
+ screen.icon,
+ contentDescription = null,
+ tint = if (currentDestination?.hierarchy?.any { it.route == screen.route } == true)
+ MaterialTheme.colorScheme.primary
+ else
+ MaterialTheme.colorScheme.secondary
+ )
+ },
+ label = { Text(
+ stringResource(screen.resourceId),
+ color = MaterialTheme.colorScheme.secondary
+ ) },
+ selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,
+ onClick = {
+ navController.navigate(screen.route) {
+ popUpTo(navController.graph.findStartDestination().id) {
+ saveState = true
+ }
+ launchSingleTop = true
+ restoreState = true
+ }
+ }
+ )
+ }
+ }
+}
+
+@Composable
+fun Navhost(
+ navController: NavHostController,
+ innerPadding: PaddingValues,
+ isDarkTheme: MutableState,
+ dataStore: DataStoreManager,
+ modifier: Modifier = Modifier,
+ currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)
+) {
+ NavHost(
+ navController,
+ startDestination = Screen.BikeList.route,
+ modifier.padding(innerPadding)
+ ) {
+ composable(Screen.BikeList.route) { BikeList(navController) }
+ composable(Screen.RentList.route) { RentList(navController, 1) }
+ composable(Screen.Cart.route) { Cart() }
+ composable(Screen.UserProfile.route) { UserProfile(isDarkTheme, dataStore, currentUserViewModel = currentUserViewModel) }
+ composable(
+ Screen.BikeEdit.route,
+ arguments = listOf(navArgument("id") { type = NavType.IntType })
+ ) {
+ BikeEdit(navController)
+ }
+ composable(
+ Screen.ItemEdit.route,
+ arguments = listOf(navArgument("id") { type = NavType.IntType },
+ navArgument("bikeId") { type = NavType.IntType })
+ ) {
+ ItemEdit(navController)
+ }
+ composable(
+ Screen.BikeView.route,
+ arguments = listOf(navArgument("id") { type = NavType.IntType })
+ ) { backStackEntry ->
+ backStackEntry.arguments?.let { BikeView(navController, currentUserViewModel = currentUserViewModel) }
+ }
+ composable(
+ Screen.RentView.route,
+ arguments = listOf(navArgument("id") { type = NavType.IntType })
+ ) { backStackEntry ->
+ backStackEntry.arguments?.let { RentView(it.getInt("id")) }
+ }
+ }
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun MainNavbar(
+ isDarkTheme: MutableState,
+ dataStoreManager: DataStoreManager
+) {
+ val navController = rememberNavController()
+ val navBackStackEntry by navController.currentBackStackEntryAsState()
+ val currentDestination = navBackStackEntry?.destination
+ val currentScreen = currentDestination?.route?.let { Screen.getItem(it) }
+
+ Scaffold(
+ topBar = {
+ Topbar(navController, currentScreen)
+ },
+ bottomBar = {
+ if (currentScreen == null || currentScreen.showInBottomBar) {
+ Navbar(navController, currentDestination)
+ }
+ }
+ ) { innerPadding ->
+ Navhost(navController, innerPadding, isDarkTheme, dataStoreManager)
+ }
+}
diff --git a/app/src/main/java/com/example/myapplication/composeui/navigation/Screen.kt b/app/src/main/java/com/example/myapplication/composeui/navigation/Screen.kt
new file mode 100644
index 0000000..c8d3c89
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/composeui/navigation/Screen.kt
@@ -0,0 +1,58 @@
+package com.example.myapplication.composeui.navigation
+
+import androidx.annotation.StringRes
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Favorite
+import androidx.compose.material.icons.filled.Home
+import androidx.compose.material.icons.filled.List
+import androidx.compose.material.icons.filled.ShoppingCart
+import androidx.compose.ui.graphics.vector.ImageVector
+import com.example.myapplication.R
+
+enum class Screen(
+ val route: String,
+ @StringRes val resourceId: Int,
+ val icon: ImageVector = Icons.Filled.Favorite,
+ val showInBottomBar: Boolean = true
+) {
+ BikeList(
+ "Bike-list", R.string.Bike_main_title, Icons.Filled.Home
+ ),
+ BikeEdit(
+ "Bike-edit/{id}", R.string.Bike_view_title, showInBottomBar = false
+ ),
+ ItemEdit(
+ "Item-edit/{id}/{bikeId}", R.string.Item_view_title, showInBottomBar = false
+ ),
+ BikeView(
+ "Bike-view/{id}", R.string.Bike_view_title, showInBottomBar = false
+ ),
+ ItemList(
+ "Item-list", R.string.Items_title, showInBottomBar = false
+ ),
+ Cart(
+ "cart", R.string.Cart_title, Icons.Filled.ShoppingCart
+ ),
+ RentList(
+ "Rent-list", R.string.Rent_title, Icons.Filled.List
+ ),
+ RentView(
+ "Rent-view/{id}", R.string.Rent_view_title, showInBottomBar = false
+ ),
+ UserProfile(
+ "User-profile", R.string.Profile_title, showInBottomBar = false
+ );
+
+ companion object {
+ val bottomBarItems = listOf(
+ BikeList,
+ Cart,
+ RentList
+ )
+
+ fun getItem(route: String): Screen? {
+ val findRoute = route.split("/").first()
+ return values().find { value -> value.route.startsWith(findRoute) }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/database/AppContainer.kt b/app/src/main/java/com/example/myapplication/database/AppContainer.kt
new file mode 100644
index 0000000..6d6306a
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/database/AppContainer.kt
@@ -0,0 +1,101 @@
+package com.example.myapplication.database
+
+import android.content.Context
+import com.example.myapplication.api.MyServerService
+import com.example.myapplication.api.bike.RestBikeRepository
+import com.example.myapplication.api.rent.RestRentRepository
+import com.example.myapplication.api.rentitem.RestRentItemRepository
+import com.example.myapplication.api.item.RestItemRepository
+import com.example.myapplication.api.user.RestUserRepository
+import com.example.myapplication.api.useritem.RestUserItemRepository
+import com.example.myapplication.database.entities.repository.OfflineBikeRepository
+import com.example.myapplication.database.entities.repository.OfflineRentRepository
+import com.example.myapplication.database.entities.repository.OfflineRentItemRepository
+import com.example.myapplication.database.entities.repository.OfflineItemRepository
+import com.example.myapplication.database.entities.repository.OfflineUserRepository
+import com.example.myapplication.database.entities.repository.OfflineUserItemRepository
+import com.example.myapplication.database.remotekeys.repository.OfflineRemoteKeyRepository
+
+interface AppContainer {
+ val bikeRestRepository: RestBikeRepository
+ val itemRestRepository: RestItemRepository
+ val userRestRepository: RestUserRepository
+ val rentRestRepository: RestRentRepository
+ val rentItemRestRepository: RestRentItemRepository
+ val userItemRestRepository: RestUserItemRepository
+
+ companion object {
+ const val TIMEOUT = 5000L
+ const val LIMIT = 10
+ }
+}
+
+class AppDataContainer(private val context: Context) : AppContainer {
+ private val bikeRepository: OfflineBikeRepository by lazy {
+ OfflineBikeRepository(AppDatabase.getInstance(context).bikeDao())
+ }
+ private val rentRepository: OfflineRentRepository by lazy {
+ OfflineRentRepository(AppDatabase.getInstance(context).rentDao())
+ }
+ private val rentItemRepository: OfflineRentItemRepository by lazy {
+ OfflineRentItemRepository(AppDatabase.getInstance(context).rentItemCrossRefDao())
+ }
+ private val itemRepository: OfflineItemRepository by lazy {
+ OfflineItemRepository(AppDatabase.getInstance(context).itemDao())
+ }
+ private val userRepository: OfflineUserRepository by lazy {
+ OfflineUserRepository(AppDatabase.getInstance(context).userDao())
+ }
+ private val userItemRepository: OfflineUserItemRepository by lazy {
+ OfflineUserItemRepository(AppDatabase.getInstance(context).userItemCrossRefDao())
+ }
+ private val remoteKeyRepository: OfflineRemoteKeyRepository by lazy {
+ OfflineRemoteKeyRepository(AppDatabase.getInstance(context).remoteKeysDao())
+ }
+ override val bikeRestRepository: RestBikeRepository by lazy {
+ RestBikeRepository(
+ MyServerService.getInstance(),
+ bikeRepository,
+ itemRepository,
+ remoteKeyRepository,
+ AppDatabase.getInstance(context)
+ )
+ }
+ override val itemRestRepository: RestItemRepository by lazy {
+ RestItemRepository(
+ MyServerService.getInstance(),
+ itemRepository,
+ userItemRepository,
+ rentItemRepository,
+ )
+ }
+ override val userRestRepository: RestUserRepository by lazy {
+ RestUserRepository(
+ MyServerService.getInstance(),
+ userRepository,
+ userItemRepository,
+ )
+ }
+ override val rentRestRepository: RestRentRepository by lazy {
+ RestRentRepository(
+ MyServerService.getInstance(),
+ rentRepository,
+ bikeRepository,
+ rentItemRepository,
+ remoteKeyRepository,
+ AppDatabase.getInstance(context)
+ )
+ }
+ override val userItemRestRepository: RestUserItemRepository by lazy {
+ RestUserItemRepository(
+ MyServerService.getInstance(),
+ userItemRepository,
+ )
+ }
+ override val rentItemRestRepository: RestRentItemRepository by lazy {
+ RestRentItemRepository(
+ MyServerService.getInstance(),
+ rentItemRepository,
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/database/AppDatabase.kt b/app/src/main/java/com/example/myapplication/database/AppDatabase.kt
new file mode 100644
index 0000000..60d39cb
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/database/AppDatabase.kt
@@ -0,0 +1,194 @@
+package com.example.myapplication.database
+
+import android.content.Context
+import android.graphics.Bitmap
+import androidx.room.Database
+import androidx.room.Room
+import androidx.room.RoomDatabase
+import androidx.room.TypeConverters
+import androidx.sqlite.db.SupportSQLiteDatabase
+import com.example.myapplication.database.entities.dao.BikeDao
+import com.example.myapplication.database.entities.dao.RentDao
+import com.example.myapplication.database.entities.dao.RentItemCrossRefDao
+import com.example.myapplication.database.entities.dao.ItemDao
+import com.example.myapplication.database.entities.dao.UserDao
+import com.example.myapplication.database.entities.dao.UserItemCrossRefDao
+import com.example.myapplication.database.entities.model.Bike
+import com.example.myapplication.database.entities.model.LocalDateTimeConverter
+import com.example.myapplication.database.entities.model.Rent
+import com.example.myapplication.database.entities.model.RentItemCrossRef
+import com.example.myapplication.database.entities.model.Item
+import com.example.myapplication.database.entities.model.User
+import com.example.myapplication.database.entities.model.UserItemCrossRef
+import com.example.myapplication.database.remotekeys.dao.RemoteKeysDao
+import com.example.myapplication.database.remotekeys.model.RemoteKeys
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import java.io.ByteArrayOutputStream
+
+@Database(
+ entities = [
+ Bike::class,
+ Item::class,
+ Rent::class,
+ RentItemCrossRef::class,
+ User::class,
+ UserItemCrossRef::class,
+ RemoteKeys::class
+ ],
+ version = 1,
+ exportSchema = false
+)
+@TypeConverters(LocalDateTimeConverter::class)
+abstract class AppDatabase : RoomDatabase() {
+ abstract fun bikeDao(): BikeDao
+ abstract fun itemDao(): ItemDao
+ abstract fun rentDao(): RentDao
+ abstract fun rentItemCrossRefDao(): RentItemCrossRefDao
+ abstract fun userDao(): UserDao
+ abstract fun userItemCrossRefDao(): UserItemCrossRefDao
+ abstract fun remoteKeysDao(): RemoteKeysDao
+
+ companion object {
+ private const val DB_NAME: String = "pmy-db"
+
+ @Volatile
+ private var INSTANCE: AppDatabase? = null
+
+ private suspend fun populateDatabase() {
+ INSTANCE?.let { database ->
+ // Users
+ val userDao = database.userDao()
+ val user1 = User(1, "login", "password")
+ userDao.insert(user1)
+ /*// Bikes
+ val bikeDao = database.bikeDao()
+ val bike1 =
+ Bike(1, "a", "Desc1", createColoredImage(android.graphics.Color.BLUE), 2023)
+ val bike2 =
+ Bike(2, "b", "Desc2", createColoredImage(android.graphics.Color.GREEN), 2023)
+ val bike3 =
+ Bike(3, "c", "Desc3", createColoredImage(android.graphics.Color.RED), 2023)
+ val bike4 =
+ Bike(4, "d", "Desc4", createColoredImage(android.graphics.Color.CYAN), 2023)
+ bikeDao.insert(bike1)
+ bikeDao.insert(bike2)
+ bikeDao.insert(bike3)
+ bikeDao.insert(bike4)
+
+ for (i in 5..20) {
+ val bike = Bike(
+ uid = i,
+ name = generateBikeName(i),
+ description = "Description $i",
+ image = createColoredImage(getRandomColorInt()),
+ )
+ bikeDao.insert(bike)
+ }
+
+ // Rents
+ val rentDao = database.rentDao()
+ val rent1 = Rent(1, 1)
+ val rent2 = Rent(2, 1)
+ val rent3 = Rent(3, 1)
+ val rent4 = Rent(4, 1)
+ rentDao.insert(rent1)
+ rentDao.insert(rent2)
+ rentDao.insert(rent3)
+ rentDao.insert(rent4)
+ // Items
+ val itemDao = database.itemDao()
+ val item1 = Item(1, LocalDateTime.now(), 150.0, 120, bike1.uid)
+ val item2 = Item(2, LocalDateTime.now(), 200.0, 110, bike2.uid)
+ val item3 = Item(3, LocalDateTime.now(), 300.0, 100, bike3.uid)
+ val item4 = Item(4, LocalDateTime.now(), 450.0, 200, bike1.uid)
+ itemDao.insert(item1)
+ itemDao.insert(item2)
+ itemDao.insert(item3)
+ itemDao.insert(item4)
+ // RentItemCrossRef для связи арендаов с сеансами
+ val rentItemCrossRefDao = database.rentItemCrossRefDao()
+ if (item1.uid != null && item2.uid != null && item3.uid != null) {
+ val rentItemCrossRef1 =
+ RentItemCrossRef(rent1.uid, item3.uid, 150.0, 5)
+ val rentItemCrossRef2 =
+ RentItemCrossRef(rent1.uid, item2.uid, 300.0, 10)
+ val rentItemCrossRef3 =
+ RentItemCrossRef(rent2.uid, item2.uid, 350.0, 6)
+ val rentItemCrossRef4 =
+ RentItemCrossRef(rent3.uid, item1.uid, 250.0, 10)
+ val rentItemCrossRef5 =
+ RentItemCrossRef(rent3.uid, item3.uid, 150.0, 16)
+ val rentItemCrossRef6 =
+ RentItemCrossRef(rent4.uid, item3.uid, 150.0, 2)
+ rentItemCrossRefDao.insert(rentItemCrossRef1)
+ rentItemCrossRefDao.insert(rentItemCrossRef2)
+ rentItemCrossRefDao.insert(rentItemCrossRef3)
+ rentItemCrossRefDao.insert(rentItemCrossRef4)
+ rentItemCrossRefDao.insert(rentItemCrossRef5)
+ rentItemCrossRefDao.insert(rentItemCrossRef6)
+ }
+ // UserItems
+ val userItemCrossRefDao = database.userItemCrossRefDao()
+ val userItemCrossRef1 = UserItemCrossRef(1, 1, 5)
+ val userItemCrossRef2 = UserItemCrossRef(1, 3, 15)
+ userItemCrossRefDao.insert(userItemCrossRef1)
+ userItemCrossRefDao.insert(userItemCrossRef2)*/
+ }
+ }
+
+ fun getInstance(appContext: Context): AppDatabase {
+ return INSTANCE ?: synchronized(this) {
+ Room.databaseBuilder(
+ appContext,
+ AppDatabase::class.java,
+ DB_NAME
+ )
+ .addCallback(object : Callback() {
+ override fun onCreate(db: SupportSQLiteDatabase) {
+ super.onCreate(db)
+ CoroutineScope(Dispatchers.IO).launch {
+ populateDatabase()
+ }
+ }
+ })
+ .build()
+ .also { INSTANCE = it }
+ }
+ }
+
+ private fun createColoredImage(color: Int): ByteArray {
+ val width = 100
+ val height = 100
+
+ val bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
+ bmp.eraseColor(color)
+
+ val stream = ByteArrayOutputStream()
+ bmp.compress(Bitmap.CompressFormat.PNG, 100, stream)
+
+ return stream.toByteArray()
+ }
+
+ private fun getRandomColorInt(): Int {
+ val red = (0..255).random()
+ val green = (0..255).random()
+ val blue = (0..255).random()
+ return (0xFF shl 24) or (red shl 16) or (green shl 8) or blue
+ }
+
+ private fun generateBikeName(index: Int): String {
+ val base = 'a'.code
+ val alphabetSize = 26
+ val sb = StringBuilder()
+ var remainder = index
+ do {
+ val letter = (remainder % alphabetSize + base).toChar()
+ sb.insert(0, letter)
+ remainder /= alphabetSize
+ } while (remainder > 0)
+ return sb.toString()
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/database/entities/composeui/AppViewModelProvider.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/AppViewModelProvider.kt
new file mode 100644
index 0000000..01d96c5
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/AppViewModelProvider.kt
@@ -0,0 +1,73 @@
+package com.example.myapplication.database.entities.composeui
+
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.createSavedStateHandle
+import androidx.lifecycle.viewmodel.CreationExtras
+import androidx.lifecycle.viewmodel.initializer
+import androidx.lifecycle.viewmodel.viewModelFactory
+import com.example.myapplication.BikeApplication
+import com.example.myapplication.database.entities.composeui.edit.BikeEditViewModel
+import com.example.myapplication.database.entities.composeui.edit.ItemEditViewModel
+
+object AppViewModelProvider {
+ val Factory = viewModelFactory {
+ initializer {
+ CurrentUserViewModel(bikeApplication().container.userRestRepository)
+ }
+ initializer {
+ EntryUserViewModel(bikeApplication().container.userRestRepository)
+ }
+ initializer {
+ RegisterUserViewModel(bikeApplication().container.userRestRepository)
+ }
+ initializer {
+ BikeListViewModel(bikeApplication().container.bikeRestRepository)
+ }
+ initializer {
+ BikeEditViewModel(
+ this.createSavedStateHandle(),
+ bikeApplication().container.bikeRestRepository
+ )
+ }
+ initializer {
+ BikeViewModel(
+ this.createSavedStateHandle(),
+ bikeApplication().container.bikeRestRepository,
+ )
+ }
+ initializer {
+ ItemListViewModel(
+ bikeApplication().container.itemRestRepository,
+ bikeApplication().container.userItemRestRepository,
+ )
+ }
+ initializer {
+ ItemEditViewModel(
+ this.createSavedStateHandle(),
+ bikeApplication().container.itemRestRepository,
+ )
+ }
+ initializer {
+ CartViewModel(
+ bikeApplication().container.userItemRestRepository,
+ bikeApplication().container.rentRestRepository,
+ bikeApplication().container.rentItemRestRepository,
+ bikeApplication().container.userRestRepository,
+ )
+ }
+ initializer {
+ RentListViewModel(
+ bikeApplication().container.rentRestRepository,
+ )
+ }
+ initializer {
+ RentViewModel(
+ this.createSavedStateHandle(),
+ bikeApplication().container.rentRestRepository,
+ )
+ }
+ }
+}
+
+fun CreationExtras.bikeApplication(): BikeApplication =
+ (this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as BikeApplication)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/database/entities/composeui/BikeList.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/BikeList.kt
new file mode 100644
index 0000000..c5bace7
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/BikeList.kt
@@ -0,0 +1,234 @@
+package com.example.myapplication.database.entities.composeui
+
+import android.graphics.BitmapFactory
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
+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.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.material.icons.filled.Delete
+import androidx.compose.material.icons.filled.Edit
+import androidx.compose.material.icons.filled.Info
+import androidx.compose.material3.FloatingActionButton
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.draw.shadow
+import androidx.compose.ui.graphics.asImageBitmap
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.viewmodel.compose.viewModel
+import androidx.navigation.NavController
+import androidx.paging.compose.LazyPagingItems
+import androidx.paging.compose.collectAsLazyPagingItems
+import com.example.myapplication.composeui.navigation.Screen
+import com.example.myapplication.database.entities.model.Bike
+import kotlinx.coroutines.launch
+
+@Composable
+fun BikeList(
+ navController: NavController,
+ viewModel: BikeListViewModel = viewModel(factory = AppViewModelProvider.Factory)
+) {
+ val coroutineScope = rememberCoroutineScope()
+ val bikePagingItems = viewModel.bikeListUiState.collectAsLazyPagingItems()
+
+ Scaffold(
+ topBar = {},
+ floatingActionButton = {
+ Box(
+ //modifier = Modifier.fillMaxWidth(),
+ contentAlignment = Alignment.Center // Center align the FloatingActionButton
+ ) {
+ FloatingActionButton(
+ onClick = {
+ val route = Screen.BikeEdit.route.replace("{id}", 0.toString())
+ navController.navigate(route)
+ },
+ modifier = Modifier.fillMaxWidth(0.92f).align(Alignment.BottomCenter),
+ containerColor = MaterialTheme.colorScheme.primary
+ ) {
+ Icon(
+ Icons.Filled.Add,
+ "Добавить",
+ tint = MaterialTheme.colorScheme.onPrimary
+ )
+ }
+ }
+ }
+ ) { innerPadding ->
+ BikeList(
+ modifier = Modifier
+ .padding(innerPadding)
+ .fillMaxSize(),
+ pagingBike = bikePagingItems,
+ onClick = { uid: Int ->
+ val route = Screen.BikeView.route.replace("{id}", uid.toString())
+ navController.navigate(route)
+ },
+ onDeleteClick = { bike: Bike ->
+ coroutineScope.launch {
+ viewModel.deleteBike(bike)
+ }
+ },
+ onEditClick = { uid: Int ->
+ val route = Screen.BikeEdit.route.replace("{id}", uid.toString())
+ navController.navigate(route)
+ },
+ )
+ }
+}
+@Composable
+private fun BikeList(
+ modifier: Modifier = Modifier,
+ pagingBike: LazyPagingItems,
+ onClick: (uid: Int) -> Unit,
+ onDeleteClick: (bike: Bike) -> Unit,
+ onEditClick: (bike: Int) -> Unit
+) {
+ Column(
+ modifier = modifier
+ ) {
+ LazyColumn(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(all = 10.dp)
+ .padding(bottom = 72.dp) // Добавим отступ внизу
+ ) {
+ items(pagingBike.itemCount) { index ->
+ val bike = pagingBike[index]
+ if (bike != null) {
+ BikeListItem(
+ bike = bike,
+ modifier = Modifier
+ .padding(vertical = 7.dp)
+ //.clickable { onClick(bike.uid) }
+ .background(
+ color = MaterialTheme.colorScheme.primary,
+ shape = RoundedCornerShape(16.dp)
+ ),
+ onDeleteClick = onDeleteClick,
+ onEditClick = onEditClick,
+ onClick = onClick
+ )
+ }
+ }
+ }
+ }
+}
+
+
+@Composable
+private fun BikeListItem(
+ bike: Bike,
+ modifier: Modifier = Modifier,
+ onDeleteClick: (bike: Bike) -> Unit,
+ onClick: (uid: Int) -> Unit,
+ onEditClick: (bike: Int) -> Unit
+) {
+ var isExpanded by remember { mutableStateOf(false) } // Состояние для определения, раскрыта ли дополнительная информация о велосипеде
+
+ Box(
+ modifier = modifier
+ .clip(RoundedCornerShape(8.dp))
+ //.background(Color.White)
+ .shadow(4.dp)
+ .padding(8.dp)
+ .clickable { isExpanded = !isExpanded } // Добавляем обработчик нажатия для раскрытия/скрытия дополнительной информации
+ ) {
+ Column(Modifier.padding(8.dp)) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+ if (bike.image != null)
+ Box(
+ modifier = Modifier
+ .size(90.dp)
+ .padding(4.dp)
+ .border(width = 1.dp, color = MaterialTheme.colorScheme.secondary) // Добавляем рамку
+ ) {
+ Image(
+ bitmap = BitmapFactory.decodeByteArray(
+ bike.image,
+ 0,
+ bike.image.size
+ ).asImageBitmap(),
+ contentDescription = null,
+ modifier = Modifier.fillMaxSize()
+ )
+ }
+
+ Text(
+ "${bike.name}",
+ color = MaterialTheme.colorScheme.secondary,
+ //style = MaterialTheme.typography.h6
+ )
+
+ // Добавляем пустое пространство для разделения текста и кнопок
+ Spacer(modifier = Modifier.weight(1f))
+ IconButton(
+ onClick = { onEditClick(bike.uid) },
+ modifier = Modifier.size(24.dp)
+ ) {
+ Icon(
+ imageVector = Icons.Default.Edit,
+ contentDescription = "Редактировать",
+ tint = MaterialTheme.colorScheme.secondary,
+ )
+ }
+ IconButton(
+ onClick = { onClick(bike.uid) },
+ modifier = Modifier.size(24.dp)
+ ) {
+ Icon(
+ imageVector = Icons.Default.Info,
+ contentDescription = "Информация",
+ tint = MaterialTheme.colorScheme.secondary,
+ )
+ }
+ IconButton(
+ onClick = { onDeleteClick(bike) },
+ modifier = Modifier.size(24.dp)
+ ) {
+ Icon(
+ imageVector = Icons.Default.Delete,
+ contentDescription = "Удалить",
+ tint = MaterialTheme.colorScheme.secondary,
+ )
+ }
+ }
+ AnimatedVisibility(visible = isExpanded) { // Анимированная видимость для дополнительной информации
+ Text(
+ text = bike.description,
+ color = MaterialTheme.colorScheme.secondary,
+ //style = MaterialTheme.typography.body2,
+ modifier = Modifier.padding(top = 8.dp)
+ )
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/example/myapplication/database/entities/composeui/BikeListViewModel.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/BikeListViewModel.kt
new file mode 100644
index 0000000..b370abd
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/BikeListViewModel.kt
@@ -0,0 +1,17 @@
+package com.example.myapplication.database.entities.composeui
+
+import androidx.lifecycle.ViewModel
+import androidx.paging.PagingData
+import com.example.myapplication.database.entities.model.Bike
+import com.example.myapplication.database.entities.repository.BikeRepository
+import kotlinx.coroutines.flow.Flow
+
+class BikeListViewModel(
+ private val bikeRepository: BikeRepository
+) : ViewModel() {
+ val bikeListUiState: Flow> = bikeRepository.getAllBikes()
+
+ suspend fun deleteBike(bike: Bike) {
+ bikeRepository.deleteBike(bike)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/database/entities/composeui/BikeView.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/BikeView.kt
new file mode 100644
index 0000000..114aec3
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/BikeView.kt
@@ -0,0 +1,143 @@
+package com.example.myapplication.database.entities.composeui
+
+import android.graphics.BitmapFactory
+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.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.asImageBitmap
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.lifecycle.viewmodel.compose.viewModel
+import androidx.navigation.NavController
+import com.example.myapplication.composeui.navigation.Screen
+import com.example.myapplication.database.entities.model.Bike
+
+@Composable
+fun BikeView(
+ navController: NavController,
+ viewModel: BikeViewModel = viewModel(factory = AppViewModelProvider.Factory),
+ currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)
+) {
+ val bikeUiState = viewModel.bikeUiState
+
+ LaunchedEffect(Unit) {
+ viewModel.refreshState()
+ }
+
+ Column(
+ modifier = Modifier
+ .padding(16.dp)
+ .fillMaxSize(),
+ ) {
+ val bike: Bike? = bikeUiState.bikeWithItems?.bike
+ if (bike != null) {
+ Box(
+ modifier = Modifier
+ .background(
+ color = MaterialTheme.colorScheme.secondary,
+ shape = RoundedCornerShape(16.dp)
+ )
+ ) {
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(16.dp)
+ .background(color = MaterialTheme.colorScheme.secondary),
+ verticalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ ) {
+ Text(
+ text = "${bike.name}",
+ style = TextStyle(
+ fontWeight = FontWeight.Bold,
+ fontSize = 18.sp,
+ color = MaterialTheme.colorScheme.onSecondary
+ ),
+ textAlign = TextAlign.Center,
+ modifier = Modifier
+ .padding(bottom = 8.dp)
+ )
+ }
+
+ if (bike.image != null)
+ Image(
+ bitmap = BitmapFactory.decodeByteArray(
+ bike.image,
+ 0,
+ bike.image.size
+ ).asImageBitmap(),
+ contentDescription = null,
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(200.dp)
+ .padding(4.dp)
+ )
+
+ Text(
+ text = bike.description,
+ color = MaterialTheme.colorScheme.onSecondary
+ )
+ }
+ }
+ }
+
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ text = "Доступные",
+ style = TextStyle(
+ fontWeight = FontWeight.Bold,
+ fontSize = 18.sp,
+ color = MaterialTheme.colorScheme.onBackground
+ ),
+ modifier = Modifier
+ .weight(1f) // Занимает доступное пространство
+ .padding(top = 8.dp, bottom = 8.dp)
+ )
+
+ IconButton(
+ onClick = {
+ val route = Screen.ItemEdit.route.replace("{id}", 0.toString())
+ .replace(
+ "{bikeId}",
+ bikeUiState.bikeWithItems?.bike?.uid.toString()
+ )
+ navController.navigate(route)
+ }
+ ) {
+ Icon(
+ imageVector = Icons.Filled.Add,
+ contentDescription = "Добавить сеанс",
+ )
+ }
+ }
+ if (bikeUiState.bikeWithItems != null) {
+ ItemList(viewModel, navController, currentUserViewModel = currentUserViewModel)
+ }
+ }
+}
diff --git a/app/src/main/java/com/example/myapplication/database/entities/composeui/BikeViewModel.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/BikeViewModel.kt
new file mode 100644
index 0000000..35a6527
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/BikeViewModel.kt
@@ -0,0 +1,45 @@
+package com.example.myapplication.database.entities.composeui
+
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.ViewModel
+import com.example.myapplication.database.entities.model.BikeWithItems
+import com.example.myapplication.database.entities.repository.BikeRepository
+
+class BikeViewModel(
+ savedStateHandle: SavedStateHandle, private val bikeRepository: BikeRepository
+) : ViewModel() {
+ private val bikeUid: Int = checkNotNull(savedStateHandle["id"])
+
+ var bikeUiState by mutableStateOf(BikeUiState())
+ private set
+
+ suspend fun refreshState() {
+ if (bikeUid > 0) {
+ bikeUiState = BikeUiState(bikeRepository.getBike(bikeUid))
+ }
+ }
+
+// init {
+// viewModelScope.launch {
+// if (bikeUid > 0) {
+// bikeUiState = BikeUiState(bikeRepository.getBike(bikeUid))
+// }
+// }
+// }
+
+// val bikeUiState: mutableStateOf(BikeUiState()) = bikeRepository.getBike(
+// bikeUid
+// ).map
+// {
+// BikeUiState(it)
+// }.stateIn(
+// scope = viewModelScope,
+// started = SharingStarted.WhileSubscribed(stopTimeoutMillis = AppDataContainer.TIMEOUT),
+// initialValue = BikeUiState()
+// )
+}
+
+data class BikeUiState(val bikeWithItems: BikeWithItems? = null)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/database/entities/composeui/CartViewModel.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/CartViewModel.kt
new file mode 100644
index 0000000..40e3627
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/CartViewModel.kt
@@ -0,0 +1,70 @@
+package com.example.myapplication.database.entities.composeui
+
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.lifecycle.ViewModel
+import com.example.myapplication.database.entities.model.Rent
+import com.example.myapplication.database.entities.model.RentItemCrossRef
+import com.example.myapplication.database.entities.model.Item
+import com.example.myapplication.database.entities.model.ItemFromCart
+import com.example.myapplication.database.entities.model.UserItemCrossRef
+import com.example.myapplication.database.entities.repository.RentRepository
+import com.example.myapplication.database.entities.repository.RentItemRepository
+import com.example.myapplication.database.entities.repository.UserRepository
+import com.example.myapplication.database.entities.repository.UserItemRepository
+import kotlinx.coroutines.delay
+
+class CartViewModel(
+ private val userItemRepository: UserItemRepository,
+ private val rentRepository: RentRepository,
+ private val rentItemRepository: RentItemRepository,
+ private val userRepository: UserRepository,
+) : ViewModel() {
+ private val userUid: Int = 1
+ var cartUiState by mutableStateOf(CartUiState())
+ private set
+
+ suspend fun refreshState() {
+ val cart = userRepository.getCartByUser(userUid)
+ cartUiState = CartUiState(cart)
+ }
+
+ suspend fun addToRent(userId: Int, items: List) {
+ if (items.isEmpty())
+ return
+ val rentId = rentRepository.insertRent(Rent(0, userId))
+ items.forEach { item ->
+ rentItemRepository.insertRentItem(
+ RentItemCrossRef(
+ rentId.toInt(),
+ item.uid,
+ item.weight,
+ item.count
+ )
+ )
+ }
+ userItemRepository.deleteUserItems(userId)
+ refreshState()
+ }
+
+ suspend fun removeFromCart(user: Int, item: Item, count: Int = 1) {
+ userItemRepository.deleteUserItem(UserItemCrossRef(user, item.uid, count))
+ refreshState()
+ }
+
+ suspend fun updateFromCart(userId: Int, item: Item, count: Int, availableCount: Int)
+ : Boolean {
+ if (count == 0) {
+ removeFromCart(userId, item, count)
+ return false
+ }
+ if (count > availableCount)
+ return false
+ userItemRepository.updateUserItem(UserItemCrossRef(userId, item.uid, count))
+ refreshState()
+ return true
+ }
+}
+
+data class CartUiState(val itemList: List = listOf())
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/database/entities/composeui/CurrentUserViewModel.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/CurrentUserViewModel.kt
new file mode 100644
index 0000000..2c098cc
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/CurrentUserViewModel.kt
@@ -0,0 +1,24 @@
+package com.example.myapplication.database.entities.composeui
+
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.example.myapplication.database.entities.model.User
+import com.example.myapplication.database.entities.repository.UserRepository
+import kotlinx.coroutines.launch
+
+class CurrentUserViewModel(private val userRepository: UserRepository) : ViewModel(){
+ val argument = mutableStateOf(null)
+ private val userid = mutableStateOf(null)
+ var user by mutableStateOf(null)
+
+ fun setArgument(arg: String) {
+ argument.value = arg
+ userid.value = arg.toInt()
+ viewModelScope.launch {
+ user = userRepository.getUserById(userid.value)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/database/entities/composeui/EntryUserViewModel.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/EntryUserViewModel.kt
new file mode 100644
index 0000000..c54f060
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/EntryUserViewModel.kt
@@ -0,0 +1,20 @@
+package com.example.myapplication.database.entities.composeui
+
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.example.myapplication.database.entities.model.User
+import com.example.myapplication.database.entities.repository.UserRepository
+import kotlinx.coroutines.launch
+
+class EntryUserViewModel(private val userRepository: UserRepository) : ViewModel() {
+
+ var userList by mutableStateOf>(emptyList())
+ fun setUserList() {
+ viewModelScope.launch {
+ userList=userRepository.getAllUsers()
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/database/entities/composeui/ItemList.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/ItemList.kt
new file mode 100644
index 0000000..2a556eb
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/ItemList.kt
@@ -0,0 +1,155 @@
+package com.example.myapplication.database.entities.composeui
+
+import android.graphics.BitmapFactory
+import android.util.Log
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+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.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.material.icons.filled.Delete
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.asImageBitmap
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.viewmodel.compose.viewModel
+import androidx.navigation.NavController
+import com.example.myapplication.R
+import com.example.myapplication.composeui.navigation.Screen
+import kotlinx.coroutines.launch
+import org.threeten.bp.format.DateTimeFormatter
+
+@Composable
+fun ItemList(
+ bikeWithItemsViewModel: BikeViewModel,
+ navController: NavController,
+ viewModel: ItemListViewModel = viewModel(factory = AppViewModelProvider.Factory),
+ currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)
+) {
+ val coroutineScope = rememberCoroutineScope()
+ val bikeWithItems = bikeWithItemsViewModel.bikeUiState.bikeWithItems!!
+
+ var getUser by remember { mutableStateOf(currentUserViewModel.user) }
+ Log.d("Удачи","${getUser?.uid}")
+
+ LazyColumn {
+ if (bikeWithItems.items.isEmpty()) {
+ item {
+ Text(
+ text = stringResource(R.string.Item_empty_description),
+ textAlign = TextAlign.Center,
+ style = MaterialTheme.typography.titleLarge
+ )
+ }
+ } else {
+ items(bikeWithItems.items, key = { it.uid }) { item ->
+ val route = Screen.ItemEdit.route.replace(
+ "{id}", item.uid.toString()
+ ).replace(
+ "{bikeId}", bikeWithItems.bike.uid.toString()
+ )
+ val dateFormatter = DateTimeFormatter.ofPattern("yyyy")
+ val formattedDate = dateFormatter.format(item.dateTime)
+ Column {
+ /* Text(
+ text = formattedDate,
+ color = MaterialTheme.colorScheme.onBackground,
+ )*/
+ Box(modifier = Modifier
+ .padding(vertical = 7.dp)
+ .clickable {
+ navController.navigate(route)
+ }
+ .background(
+ color = MaterialTheme.colorScheme.primary,
+ shape = RoundedCornerShape(16.dp)
+ )) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(8.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ if (bikeWithItems.bike.image != null) Image(
+ bitmap = BitmapFactory.decodeByteArray(
+ bikeWithItems.bike.image,
+ 0,
+ bikeWithItems.bike.image.size
+ ).asImageBitmap(),
+ contentDescription = null,
+ modifier = Modifier
+ .size(90.dp)
+ .padding(4.dp)
+ )
+
+ Column(
+ modifier = Modifier.weight(1f),
+ verticalArrangement = Arrangement.spacedBy(4.dp)
+ ) {
+ Text(
+ text = "Вес: ${item.weight}\n" + "Количество: ${item.availableCount}\n" + "Год: ${formattedDate}",
+ color = MaterialTheme.colorScheme.onPrimary
+ )
+ }
+
+ IconButton(
+ onClick = {
+ coroutineScope.launch {
+ if (item.availableCount != 0) {
+ viewModel.addItemInCart(itemId = item.uid)
+ }
+ }
+ },
+ ) {
+ Icon(
+ imageVector = Icons.Filled.Add,
+ contentDescription = null,
+ modifier = Modifier.size(24.dp),
+ tint = MaterialTheme.colorScheme.onPrimary
+ )
+ }
+ IconButton(
+ onClick = {
+ coroutineScope.launch {
+ viewModel.deleteItem(item = item)
+ bikeWithItemsViewModel.refreshState()
+ }
+ },
+ ) {
+ Icon(
+ imageVector = Icons.Default.Delete,
+ contentDescription = null,
+ modifier = Modifier.size(24.dp),
+ tint = MaterialTheme.colorScheme.onPrimary
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
diff --git a/app/src/main/java/com/example/myapplication/database/entities/composeui/ItemListViewModel.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/ItemListViewModel.kt
new file mode 100644
index 0000000..a50d95b
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/ItemListViewModel.kt
@@ -0,0 +1,33 @@
+package com.example.myapplication.database.entities.composeui
+
+import androidx.lifecycle.ViewModel
+import com.example.myapplication.database.entities.model.Item
+import com.example.myapplication.database.entities.model.ItemFromBike
+import com.example.myapplication.database.entities.model.UserItemCrossRef
+import com.example.myapplication.database.entities.repository.ItemRepository
+import com.example.myapplication.database.entities.repository.UserItemRepository
+
+class ItemListViewModel(
+ private val itemRepository: ItemRepository,
+ private val userItemRepository: UserItemRepository
+) : ViewModel() {
+ suspend fun deleteItem(item: ItemFromBike) {
+ itemRepository.deleteItem(
+ Item(
+ uid = item.uid,
+ dateTime = item.dateTime,
+ weight = item.weight,
+ maxCount = 0,
+ bikeId = 0
+ )
+ )
+ }
+
+ suspend fun addItemInCart(itemId: Int, count: Int = 1) {
+ try {
+ userItemRepository.insertUserItem(UserItemCrossRef(1, itemId, count))
+ } catch (_: Exception) {
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/database/entities/composeui/RegisterUserViewModel.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/RegisterUserViewModel.kt
new file mode 100644
index 0000000..b5f47a9
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/RegisterUserViewModel.kt
@@ -0,0 +1,26 @@
+package com.example.myapplication.database.entities.composeui
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.example.myapplication.database.entities.model.User
+import com.example.myapplication.database.entities.repository.UserRepository
+import kotlinx.coroutines.launch
+
+class RegisterUserViewModel(private val userRepository: UserRepository) : ViewModel() {
+
+ private val _users = MutableLiveData>()
+ val users: LiveData> get() = _users
+
+ fun setUserList() {
+ viewModelScope.launch {
+ _users.value = userRepository.getAllUsers()
+ }
+ }
+
+ suspend fun insertUser(user: User) {
+ userRepository.insertUser(user)
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/database/entities/composeui/RentList.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/RentList.kt
new file mode 100644
index 0000000..c1daab0
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/RentList.kt
@@ -0,0 +1,104 @@
+package com.example.myapplication.database.entities.composeui
+
+import android.content.res.Configuration
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Delete
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.viewmodel.compose.viewModel
+import androidx.navigation.NavController
+import androidx.paging.compose.collectAsLazyPagingItems
+import androidx.paging.compose.itemContentType
+import androidx.paging.compose.itemKey
+import com.example.myapplication.composeui.navigation.Screen
+import com.example.myapplication.ui.theme.PmudemoTheme
+import kotlinx.coroutines.launch
+
+@Composable
+fun RentList(
+ navController: NavController?,
+ userId: Int?,
+ viewModel: RentListViewModel = viewModel(factory = AppViewModelProvider.Factory)
+) {
+ val coroutineScope = rememberCoroutineScope()
+ val rentsUiState = viewModel.rentListUiState.collectAsLazyPagingItems()
+ LazyColumn(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(all = 10.dp)
+ ) {
+ items(
+ count = rentsUiState.itemCount,
+ key = rentsUiState.itemKey(),
+ contentType = rentsUiState.itemContentType()
+ ) { index ->
+ val rent = rentsUiState[index]
+ val rentId = Screen.RentView.route.replace("{id}", rent!!.uid.toString())
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(all = 10.dp)
+ .clickable { navController?.navigate(rentId) }
+ .background(
+ color = MaterialTheme.colorScheme.primary,
+ shape = RoundedCornerShape(16.dp)
+ )
+ ) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(8.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.SpaceBetween
+ ) {
+ Text("Аренда №${rent.uid}", color = MaterialTheme.colorScheme.onPrimary)
+ IconButton(
+ onClick = {
+ coroutineScope.launch {
+ viewModel.deleteRent(rent!!)
+ }
+ }
+ ) {
+ Icon(
+ Icons.Default.Delete,
+ tint = MaterialTheme.colorScheme.onPrimary,
+ contentDescription = "Удалить"
+ )
+ }
+ }
+ }
+ }
+ }
+}
+
+@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
+@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+fun RentListPreview() {
+ PmudemoTheme {
+ Surface(
+ color = MaterialTheme.colorScheme.background
+ ) {
+ RentList(navController = null, 1)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/database/entities/composeui/RentListViewModel.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/RentListViewModel.kt
new file mode 100644
index 0000000..58976cd
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/RentListViewModel.kt
@@ -0,0 +1,19 @@
+package com.example.myapplication.database.entities.composeui
+
+import androidx.lifecycle.ViewModel
+import androidx.paging.PagingData
+import com.example.myapplication.database.entities.model.Rent
+import com.example.myapplication.database.entities.repository.RentRepository
+import kotlinx.coroutines.flow.Flow
+
+class RentListViewModel(
+ private val rentRepository: RentRepository
+) : ViewModel() {
+ val rentListUiState: Flow> = rentRepository.getAllRents(1)
+
+ suspend fun deleteRent(rent: Rent) {
+ rentRepository.deleteRent(rent)
+ }
+}
+
+data class RentListUiState(val rentList: List = listOf())
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/database/entities/composeui/RentView.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/RentView.kt
new file mode 100644
index 0000000..fb3da55
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/RentView.kt
@@ -0,0 +1,132 @@
+package com.example.myapplication.database.entities.composeui
+
+import android.content.res.Configuration
+import android.graphics.BitmapFactory
+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.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.asImageBitmap
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.example.myapplication.ui.theme.PmudemoTheme
+import org.threeten.bp.format.DateTimeFormatter
+
+@Composable
+fun RentView(
+ id: Int,
+ viewModel: RentViewModel = viewModel(factory = AppViewModelProvider.Factory)
+) {
+ val coroutineScope = rememberCoroutineScope()
+ val rentUiState by viewModel.rentUiState.collectAsState()
+ LazyColumn(
+ modifier = Modifier
+ .padding(10.dp)
+ ) {
+ items(rentUiState.itemList) { item ->
+ val count = remember { mutableStateOf(item.count) }
+ val dateFormatter = DateTimeFormatter.ofPattern("yyyy")
+ val formattedDate = dateFormatter.format(item.dateTime)
+
+/* Text(
+ text = formattedDate,
+ color = MaterialTheme.colorScheme.onBackground,
+ )*/
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(10.dp)
+ .clip(RoundedCornerShape(16.dp))
+ .background(MaterialTheme.colorScheme.primary)
+ ) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(8.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.SpaceBetween
+ ) {
+ if (item.bike.image != null)
+ Image(
+ bitmap = BitmapFactory.decodeByteArray(
+ item.bike.image,
+ 0,
+ item.bike.image.size
+ ).asImageBitmap(),
+ contentDescription = null,
+ modifier = Modifier
+ .size(90.dp)
+ .padding(4.dp)
+ )
+
+ Column(
+ modifier = Modifier
+ .weight(1f)
+ .padding(start = 8.dp),
+ verticalArrangement = Arrangement.spacedBy(4.dp)
+ ) {
+ Text(
+ text = "${item.bike.name}\n" +
+ "Вес: ${item.frozenWeight}\n" +
+ "Количество: ${count.value}\n" +
+ "Дата: ${formattedDate}",
+ color = MaterialTheme.colorScheme.onPrimary
+ )
+/* Row(verticalAlignment = Alignment.CenterVertically) {
+ // Кнопка "Удалить"
+ Button(
+ onClick = {
+ coroutineScope.launch {
+ //viewModel.removeItemFromRent(item)
+ }
+ },
+ modifier = Modifier.padding(end = 8.dp)
+ ) {
+ Icon(
+ imageVector = Icons.Default.Delete,
+ contentDescription = "Удалить"
+ )
+ Text(text = "Удалить")
+ }
+ }*/
+ }
+ }
+ }
+ }
+ }
+}
+
+
+@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
+@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+fun RentViewPreview() {
+ PmudemoTheme {
+ Surface(
+ color = MaterialTheme.colorScheme.background
+ ) {
+ RentView(id = 1)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/database/entities/composeui/RentViewModel.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/RentViewModel.kt
new file mode 100644
index 0000000..11335e7
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/RentViewModel.kt
@@ -0,0 +1,30 @@
+package com.example.myapplication.database.entities.composeui
+
+import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.example.myapplication.database.AppContainer
+import com.example.myapplication.database.entities.model.ItemFromRent
+import com.example.myapplication.database.entities.repository.RentRepository
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+class RentViewModel(
+ savedStateHandle: SavedStateHandle,
+ private val rentRepository: RentRepository
+) : ViewModel() {
+ private val rentUid: Int = checkNotNull(savedStateHandle["id"])
+
+ val rentUiState: StateFlow = flow{emit(rentRepository.getRent(rentUid))} .map {
+ RentUiState(it)
+ }.stateIn(
+ scope = viewModelScope,
+ started = SharingStarted.WhileSubscribed(stopTimeoutMillis = AppContainer.TIMEOUT),
+ initialValue = RentUiState()
+ )
+}
+
+data class RentUiState(val itemList: List = listOf())
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/database/entities/composeui/UserProfile.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/UserProfile.kt
new file mode 100644
index 0000000..a6ff373
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/UserProfile.kt
@@ -0,0 +1,230 @@
+package com.example.myapplication.database.entities.composeui
+
+import android.annotation.SuppressLint
+import android.util.Log
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.material3.Button
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Switch
+import androidx.compose.material3.SwitchDefaults
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.input.PasswordVisualTransformation
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.example.myapplication.database.entities.model.User
+import com.example.myapplication.datastore.DataStoreManager
+import com.example.myapplication.datastore.SettingData
+import kotlinx.coroutines.async
+import kotlinx.coroutines.launch
+@SuppressLint("UnrememberedMutableState")
+@Composable
+fun UserProfile(
+ isDarkTheme: MutableState,
+ dataStoreManager: DataStoreManager,
+ registerUserViewModel: RegisterUserViewModel = viewModel(factory = AppViewModelProvider.Factory),
+ entryUserViewModel: EntryUserViewModel = viewModel(factory = AppViewModelProvider.Factory),
+ currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)
+) {
+ var username by remember { mutableStateOf("") }
+ var password by remember { mutableStateOf("") }
+ var isRegistration by remember { mutableStateOf(false) }
+
+ val coroutineScope = rememberCoroutineScope()
+
+ registerUserViewModel.setUserList()
+ var users = mutableStateOf>(emptyList())
+ registerUserViewModel.users.observeForever { userList ->
+ users.value = userList
+ }
+
+ entryUserViewModel.setUserList()
+ val users_entry = mutableStateOf>(entryUserViewModel.userList)
+
+ LazyColumn {
+ item {
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(16.dp),
+ verticalArrangement = Arrangement.spacedBy(16.dp)
+ ) {
+ Text(
+ text = "Логин",
+ modifier = Modifier.align(Alignment.CenterHorizontally)
+ )
+ BasicTextField(
+ value = username,
+ onValueChange = { newValue -> username = newValue },
+ modifier = Modifier
+ .fillMaxWidth()
+ .size(36.dp)
+ .background(MaterialTheme.colorScheme.secondary, RoundedCornerShape(18.dp))
+ .padding(start = 13.dp, top = 8.dp)
+ )
+
+ Text(
+ text = "Пароль",
+ modifier = Modifier.align(Alignment.CenterHorizontally)
+ )
+ BasicTextField(
+ value = password,
+ onValueChange = { newValue -> password = newValue },
+ modifier = Modifier
+ .fillMaxWidth()
+ .size(36.dp)
+ .background(MaterialTheme.colorScheme.secondary, RoundedCornerShape(18.dp))
+ .padding(start = 13.dp, top = 8.dp),
+ visualTransformation = PasswordVisualTransformation()
+ )
+
+ if (isRegistration) {
+ Button(
+ onClick = {
+ var isExist = false;
+ if (password.isNotEmpty() && username.isNotEmpty()) {
+ users.value.forEach { user ->
+ if (user.login == username) {
+ Log.d("User already exist. User id: ", user.uid.toString())
+ isExist = true
+ }
+ }
+ if (!isExist) {
+ val newUser = User(null, username, password)
+ coroutineScope.launch {
+ val insertResult = async {
+ registerUserViewModel.insertUser(newUser)
+ }
+
+ insertResult.await()
+
+ registerUserViewModel.setUserList()
+ registerUserViewModel.users.observeForever { userList ->
+ users.value = userList
+ Log.println(Log.ASSERT, "UsersList", users.value.toString())
+ users.value?.forEach { user ->
+ if (user.password == password) {
+ currentUserViewModel.setArgument(user.uid.toString())
+ //navController.navigate(route = Graph.passUserId(user.id.toString()))
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(8.dp)
+ ) {
+ Text("Регистрация")
+ }
+ Text(
+ text = "Уже есть аккаунт? Войти",
+ modifier = Modifier
+ .clickable {
+ isRegistration = false
+ }
+ .align(Alignment.CenterHorizontally),
+ color = MaterialTheme.colorScheme.onBackground
+ )
+ } else {
+ Button(
+ onClick = {
+ Log.d("Кнопка","Тык")
+ if (password.isNotEmpty()) {
+ users_entry.value.forEach { user ->
+ if (user.password == password) {
+ currentUserViewModel.setArgument(user.uid.toString())
+ Log.d("Авторизация","Успешно")
+ //navController.navigate(route = Graph.passUserId(user.id.toString())) {
+ }else{
+ Log.d("Авторизация","Пароль не совпадает")
+ }
+
+ }
+ }
+ },
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(8.dp)
+ ) {
+ Text("Вход")
+ }
+ Text(
+ text = "Нет аккаунта? Зарегистрироваться",
+ modifier = Modifier
+ .clickable {
+ isRegistration = true
+ }
+ .align(Alignment.CenterHorizontally),
+ color = MaterialTheme.colorScheme.onBackground
+ )
+ }
+ val switchColors = SwitchDefaults.colors(
+ checkedThumbColor = MaterialTheme.colorScheme.primary, // Change the color when the switch is checked
+ checkedTrackColor = MaterialTheme.colorScheme.secondary, // Change the color of the track when the switch is checked
+ uncheckedThumbColor = MaterialTheme.colorScheme.primary, // Change the color when the switch is unchecked
+ uncheckedTrackColor = MaterialTheme.colorScheme.onPrimary // Change the color of the track when the switch is unchecked
+ )
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(16.dp),
+ horizontalArrangement = Arrangement.End
+ ) {
+ Text(
+ "Темная тема", modifier = Modifier
+ .align(Alignment.CenterVertically)
+ .padding(5.dp)
+ )
+
+ val coroutine = rememberCoroutineScope()
+
+ Switch(
+ checked = isDarkTheme.value,
+ onCheckedChange = {
+ isDarkTheme.value = !isDarkTheme.value
+ coroutine.launch {
+ dataStoreManager.saveSettings(SettingData(isDarkTheme = isDarkTheme.value))
+ }
+ },
+ colors = switchColors
+ )
+ }
+ }
+ }
+ }
+}
+
+/*@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
+@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+fun UserProfilePreview() {
+ PmudemoTheme {
+ Surface(
+ color = MaterialTheme.colorScheme.background
+ ) {
+ UserProfile(navController = null, isDarkTheme = remember { mutableStateOf(true) })
+ }
+ }
+}*/
diff --git a/app/src/main/java/com/example/myapplication/database/entities/composeui/edit/BikeEdit.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/edit/BikeEdit.kt
new file mode 100644
index 0000000..c5bf3bc
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/edit/BikeEdit.kt
@@ -0,0 +1,119 @@
+package com.example.myapplication.database.entities.composeui.edit
+
+import android.content.res.Configuration
+import android.graphics.BitmapFactory
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Button
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.viewmodel.compose.viewModel
+import androidx.navigation.NavController
+import com.example.myapplication.R
+import com.example.myapplication.database.entities.composeui.AppViewModelProvider
+import com.example.myapplication.ui.theme.PmudemoTheme
+import kotlinx.coroutines.launch
+
+@Composable
+fun BikeEdit(
+ navController: NavController,
+ viewModel: BikeEditViewModel = viewModel(factory = AppViewModelProvider.Factory),
+) {
+ val coroutineScope = rememberCoroutineScope()
+
+ BikeEdit(
+ bikeUiState = viewModel.bikeUiState,
+ onClick = {
+ coroutineScope.launch {
+ viewModel.saveBike()
+ navController.popBackStack()
+ }
+ },
+ onUpdate = viewModel::updateUiState,
+ )
+}
+
+@Composable
+private fun BikeEdit(
+ bikeUiState: BikeUiState,
+ onClick: () -> Unit,
+ onUpdate: (BikeDetails) -> Unit,
+) {
+ Column(
+ Modifier
+ .fillMaxWidth()
+ .padding(all = 10.dp)
+ ) {
+ OutlinedTextField(
+ modifier = Modifier.fillMaxWidth(),
+ value = bikeUiState.bikeDetails.name,
+ onValueChange = { onUpdate(bikeUiState.bikeDetails.copy(name = it)) },
+ label = { Text(stringResource(id = R.string.Bike_name)) },
+ singleLine = true,
+ textStyle = MaterialTheme.typography.bodyLarge.copy(
+ color = MaterialTheme.colorScheme.onSecondary
+ )
+
+ )
+ OutlinedTextField(
+ modifier = Modifier.fillMaxWidth(),
+ value = bikeUiState.bikeDetails.description,
+ onValueChange = { onUpdate(bikeUiState.bikeDetails.copy(description = it)) },
+ label = { Text(stringResource(id = R.string.Bike_description)) },
+ singleLine = true,
+ textStyle = MaterialTheme.typography.bodyLarge.copy(
+ color = MaterialTheme.colorScheme.onSecondary
+ )
+ )
+ if (bikeUiState.bikeDetails.image != null)
+ ImageUploader(
+ bitmap = BitmapFactory.decodeByteArray(
+ bikeUiState.bikeDetails.image,
+ 0,
+ bikeUiState.bikeDetails.image.size
+ ),
+ onResult = { byteArray: ByteArray? ->
+ onUpdate(
+ bikeUiState.bikeDetails.copy(
+ image = byteArray
+ )
+ )
+ }
+ )
+ Button(
+ onClick = onClick,
+ enabled = bikeUiState.isEntryValid,
+ shape = MaterialTheme.shapes.small,
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Text(text = stringResource(R.string.Save_button))
+ }
+ }
+}
+
+
+@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
+@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+fun BikeEditPreview() {
+ PmudemoTheme {
+ Surface(
+ color = MaterialTheme.colorScheme.background
+ ) {
+ BikeEdit(
+ bikeUiState = BikeUiState(),
+ onClick = {},
+ onUpdate = {},
+ )
+ }
+ }
+}
diff --git a/app/src/main/java/com/example/myapplication/database/entities/composeui/edit/BikeEditViewModel.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/edit/BikeEditViewModel.kt
new file mode 100644
index 0000000..c311fff
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/edit/BikeEditViewModel.kt
@@ -0,0 +1,92 @@
+package com.example.myapplication.database.entities.composeui.edit
+
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.example.myapplication.database.entities.model.Bike
+import com.example.myapplication.database.entities.model.BikeWithItems
+import com.example.myapplication.database.entities.model.ItemFromBike
+import com.example.myapplication.database.entities.repository.BikeRepository
+import kotlinx.coroutines.launch
+
+class BikeEditViewModel(
+ savedStateHandle: SavedStateHandle,
+ private val bikeRepository: BikeRepository
+) : ViewModel() {
+ var bikeUiState by mutableStateOf(BikeUiState())
+ private set
+
+ private val bikeUid: Int = checkNotNull(savedStateHandle["id"])
+
+ init {
+ viewModelScope.launch {
+ if (bikeUid > 0) {
+ bikeUiState = bikeRepository.getBike(bikeUid)
+ .toUiState(true)
+ }
+ }
+ }
+
+ fun updateUiState(bikeDetails: BikeDetails) {
+ bikeUiState = BikeUiState(
+ bikeDetails = bikeDetails,
+ isEntryValid = validateInput(bikeDetails)
+ )
+ }
+
+ suspend fun saveBike() {
+ if (validateInput()) {
+ if (bikeUid > 0) {
+ bikeRepository.updateBike(bikeUiState.bikeDetails.toBike(bikeUid))
+ } else {
+ bikeRepository.insertBike(bikeUiState.bikeDetails.toBike())
+ }
+ }
+ }
+
+ private fun validateInput(uiState: BikeDetails = bikeUiState.bikeDetails): Boolean {
+ return with(uiState) {
+ name.isNotBlank()
+ && description.isNotBlank()
+ }
+ }
+}
+
+data class BikeUiState(
+ val bikeDetails: BikeDetails = BikeDetails(),
+ val isEntryValid: Boolean = false
+)
+
+data class BikeDetails(
+ val name: String = "",
+ val description: String = "",
+ val image: ByteArray? = byteArrayOf(),
+ val items: List = emptyList()
+)
+
+fun BikeDetails.toBike(uid: Int = 0): Bike = Bike(
+ uid = uid,
+ name = name,
+ description = description,
+ image = image
+)
+
+fun BikeWithItems.toDetails(): BikeDetails {
+ val bike = this.bike
+ val items = this.items
+
+ return BikeDetails(
+ name = bike.name,
+ description = bike.description,
+ image = bike.image,
+ items = items
+ )
+}
+
+fun BikeWithItems.toUiState(isEntryValid: Boolean = false): BikeUiState = BikeUiState(
+ bikeDetails = this.toDetails(),
+ isEntryValid = isEntryValid
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/database/entities/composeui/edit/ImageUploader.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/edit/ImageUploader.kt
new file mode 100644
index 0000000..47838ba
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/edit/ImageUploader.kt
@@ -0,0 +1,121 @@
+package com.example.myapplication.database.entities.composeui.edit
+
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.net.Uri
+import androidx.activity.compose.rememberLauncherForActivityResult
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.asImageBitmap
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import com.example.myapplication.R
+import java.io.ByteArrayOutputStream
+
+@Composable
+fun ImageUploader(
+ bitmap: Bitmap?,
+ onResult: (ByteArray) -> Unit
+) {
+ val context = LocalContext.current
+ val title: String = if (bitmap == null) {
+ stringResource(R.string.not_uploaded)
+ } else {
+ stringResource(R.string.size, bitmap.width, bitmap.height)
+ }
+
+ val launcher = rememberLauncherForActivityResult(
+ contract = ActivityResultContracts.GetContent(),
+ onResult = { uri: Uri? ->
+ uri?.let {
+ val inputStream = context.contentResolver.openInputStream(uri)
+ val newBitmap: Bitmap = BitmapFactory.decodeStream(inputStream)
+ val scaledBitmap = resizeBitmapWithAspectRatio(newBitmap, 200)
+ val stream = ByteArrayOutputStream()
+ scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream)
+ onResult(stream.toByteArray())
+ }
+ }
+ )
+
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(16.dp)
+ ) {
+ Box(
+ contentAlignment = Alignment.Center,
+ modifier = Modifier
+ .background(
+ color = MaterialTheme.colorScheme.surface,
+ shape = RoundedCornerShape(10.dp)
+ )
+ .aspectRatio(1f)
+ .border(
+ width = 1.dp,
+ color = MaterialTheme.colorScheme.primary,
+ shape = RoundedCornerShape(10.dp)
+ )
+ .clickable { launcher.launch("image/*") }
+ .padding(16.dp)
+ ) {
+ if (bitmap != null) {
+ Image(
+ bitmap = bitmap.asImageBitmap(),
+ contentDescription = null,
+ contentScale = ContentScale.Crop,
+ modifier = Modifier
+ .fillMaxSize()
+ .clip(shape = RoundedCornerShape(10.dp))
+ )
+ } else {
+ Image(
+ painter = painterResource(id = R.drawable.photo),
+ contentDescription = null,
+ contentScale = ContentScale.Crop,
+ modifier = Modifier
+ .fillMaxSize()
+ .clip(shape = RoundedCornerShape(10.dp))
+ )
+ }
+ }
+ Spacer(modifier = Modifier.height(8.dp))
+ Text(
+ text = title,
+ color = MaterialTheme.colorScheme.onBackground
+ )
+ }
+}
+
+fun resizeBitmapWithAspectRatio(bitmap: Bitmap, maxHeight: Int): Bitmap {
+ if (bitmap.height <= maxHeight) {
+ return bitmap
+ }
+
+ val aspectRatio = bitmap.width.toFloat() / bitmap.height
+ val newWidth = (maxHeight * aspectRatio).toInt()
+
+ return Bitmap.createScaledBitmap(bitmap, newWidth, maxHeight, true)
+}
+
diff --git a/app/src/main/java/com/example/myapplication/database/entities/composeui/edit/ItemEdit.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/edit/ItemEdit.kt
new file mode 100644
index 0000000..31f5c41
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/edit/ItemEdit.kt
@@ -0,0 +1,153 @@
+package com.example.myapplication.database.entities.composeui.edit
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material3.Button
+import androidx.compose.material3.DatePicker
+import androidx.compose.material3.DatePickerDefaults
+import androidx.compose.material3.DisplayMode
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Text
+import androidx.compose.material3.rememberDatePickerState
+import androidx.compose.runtime.*
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.viewmodel.compose.viewModel
+import androidx.navigation.NavController
+import com.example.myapplication.R
+import com.example.myapplication.database.entities.composeui.AppViewModelProvider
+import kotlinx.coroutines.launch
+import org.threeten.bp.Instant
+import org.threeten.bp.LocalDateTime
+import org.threeten.bp.LocalTime
+import org.threeten.bp.ZoneId
+import org.threeten.bp.ZoneOffset
+
+
+@Composable
+fun ItemEdit(
+ navController: NavController,
+ viewModel: ItemEditViewModel = viewModel(factory = AppViewModelProvider.Factory),
+) {
+ val coroutineScope = rememberCoroutineScope()
+ ItemEdit(
+ itemUiState = viewModel.itemUiState,
+ onClick = {
+ coroutineScope.launch {
+ viewModel.saveItem()
+ navController.popBackStack()
+ }
+ },
+ onUpdate = viewModel::updateUiState
+ )
+}
+
+fun Long.toLocalDate(): org.threeten.bp.LocalDate {
+ val instant = Instant.ofEpochMilli(this)
+ return instant.atZone(ZoneId.systemDefault()).toLocalDate()
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+private fun ItemEdit(
+ itemUiState: ItemUiState,
+ onClick: () -> Unit,
+ onUpdate: (ItemDetails) -> Unit,
+) {
+ LazyColumn(
+ Modifier
+ .fillMaxWidth()
+ .padding(all = 10.dp)
+ ) {
+ item {
+ if (itemUiState.itemDetails.dateTime != LocalDateTime.MIN) {
+ val selectedDateMillis =
+ itemUiState.itemDetails.dateTime.toInstant(ZoneOffset.UTC).toEpochMilli()
+
+ val dateState = rememberDatePickerState(
+ initialDisplayMode = DisplayMode.Input,
+ initialSelectedDateMillis = selectedDateMillis
+ )
+ /* Surface(
+ tonalElevation = 6.dp,
+ color = MaterialTheme.colorScheme.primary
+ ) {*/
+ DatePicker(
+ state = dateState,
+ modifier = Modifier.padding(16.dp),
+ colors = DatePickerDefaults.colors(
+ dayContentColor = MaterialTheme.colorScheme.primary,
+ )
+ )
+ // }
+ val selectedDate = dateState.selectedDateMillis?.toLocalDate()
+ if (selectedDate != null) {
+ val resultDateTime = LocalDateTime.of(selectedDate, LocalTime.MIDNIGHT)
+ onUpdate(itemUiState.itemDetails.copy(dateTime = resultDateTime))
+ }
+ } else {
+ val dateState = rememberDatePickerState(
+ initialDisplayMode = DisplayMode.Input
+ )
+ /* Surface(
+ tonalElevation = 6.dp,
+ color = MaterialTheme.colorScheme.primary
+ ) {*/
+ DatePicker(
+ state = dateState,
+ modifier = Modifier.padding(16.dp),
+ colors = DatePickerDefaults.colors(
+ dayContentColor = MaterialTheme.colorScheme.primary,
+ )
+ )
+ // }
+ val selectedDate = dateState.selectedDateMillis?.toLocalDate()
+ if (selectedDate != null) {
+ val resultDateTime = LocalDateTime.of(selectedDate, LocalTime.MIDNIGHT)
+ onUpdate(itemUiState.itemDetails.copy(dateTime = resultDateTime))
+ }
+
+ }
+
+ OutlinedTextField(
+ modifier = Modifier.fillMaxWidth(),
+ value = itemUiState.itemDetails.weight,
+ label = { Text(text = "Вес") },
+ onValueChange = {
+ onUpdate(itemUiState.itemDetails.copy(weight = it))
+ },
+ keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number),
+ textStyle = MaterialTheme.typography.bodyLarge.copy(
+ color = MaterialTheme.colorScheme.onSecondary
+ )
+ )
+ OutlinedTextField(
+ modifier = Modifier.fillMaxWidth(),
+ value = itemUiState.itemDetails.maxCount.toString(),
+ onValueChange = { newValue ->
+ val parsedMaxCount = newValue.toIntOrNull() ?: 0 // Преобразование в Int
+ onUpdate(itemUiState.itemDetails.copy(maxCount = parsedMaxCount))
+ },
+ label = { Text(text = "Количество") },
+ keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
+ textStyle = MaterialTheme.typography.bodyLarge.copy(
+ color = MaterialTheme.colorScheme.onSecondary
+ )
+ )
+ Button(
+ onClick = onClick,
+ enabled = itemUiState.isEntryValid,
+ shape = MaterialTheme.shapes.small,
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Text(text = stringResource(R.string.Save_button))
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/database/entities/composeui/edit/ItemEditViewModel.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/edit/ItemEditViewModel.kt
new file mode 100644
index 0000000..4f972c8
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/edit/ItemEditViewModel.kt
@@ -0,0 +1,106 @@
+package com.example.myapplication.database.entities.composeui.edit
+
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.example.myapplication.database.entities.model.Item
+import com.example.myapplication.database.entities.repository.ItemRepository
+import kotlinx.coroutines.launch
+import org.threeten.bp.LocalDateTime
+
+class ItemEditViewModel(
+ savedStateHandle: SavedStateHandle,
+ private val itemRepository: ItemRepository
+) : ViewModel() {
+ var itemUiState by mutableStateOf(ItemUiState())
+ private set
+
+ private val itemUid: Int = checkNotNull(savedStateHandle["id"])
+ private val bikeUid: Int = checkNotNull(savedStateHandle["bikeId"])
+
+ init {
+ viewModelScope.launch {
+ if (itemUid > 0) {
+ itemUiState = itemRepository.getItem(itemUid)
+ .toUiState(true)
+ }
+ }
+ }
+
+ fun updateUiState(itemDetails: ItemDetails) {
+ itemUiState = ItemUiState(
+ itemDetails = itemDetails,
+ isEntryValid = validateInput(itemDetails)
+ )
+ }
+
+ suspend fun saveItem() {
+ if (validateInput()) {
+ if (bikeUid > 0)
+ if (itemUid > 0) {
+ itemRepository.updateItem(
+ itemUiState.itemDetails
+ .toItem(uid = itemUid, bikeUid = bikeUid)
+ )
+ } else {
+ itemRepository.insertItem(
+ itemUiState.itemDetails.toItem(
+ bikeUid = bikeUid
+ )
+ )
+ }
+ }
+ }
+
+ private fun validateInput(uiState: ItemDetails = itemUiState.itemDetails): Boolean {
+ return with(uiState) {
+ dateTime != LocalDateTime.MIN
+ && isValidDouble(weight)
+ && maxCount > 0
+ && bikeUid > 0
+ }
+ }
+}
+
+val regex = """^-?\d+(.\d+)?+(,\d+)?$""".toRegex()
+
+fun isValidDouble(input: String): Boolean {
+ return regex.matches(input)
+}
+
+data class ItemUiState(
+ val itemDetails: ItemDetails = ItemDetails(),
+ val isEntryValid: Boolean = false
+)
+
+data class ItemDetails(
+ val uid: Int = 0,
+ val dateTime: LocalDateTime = LocalDateTime.MIN,
+ val weight: String = "0",
+ val maxCount: Int = 0,
+ val bikeId: Int = 0
+)
+
+fun ItemDetails.toItem(uid: Int = 0, bikeUid: Int = 0): Item = Item(
+ uid = uid,
+ dateTime = dateTime,
+ weight = weight.toDoubleOrNull() ?: 0.0,
+ maxCount = maxCount,
+ bikeId = bikeUid
+)
+
+fun Item.toDetails(): ItemDetails = ItemDetails(
+ uid = uid,
+ dateTime = dateTime,
+ weight = weight.toString(),
+ maxCount = maxCount,
+ bikeId = bikeId
+)
+
+fun Item.toUiState(isEntryValid: Boolean = false): ItemUiState = ItemUiState(
+ itemDetails = this.toDetails(),
+ isEntryValid = isEntryValid
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/database/entities/dao/BikeDao.kt b/app/src/main/java/com/example/myapplication/database/entities/dao/BikeDao.kt
new file mode 100644
index 0000000..088b7f8
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/database/entities/dao/BikeDao.kt
@@ -0,0 +1,39 @@
+package com.example.myapplication.database.entities.dao
+
+import androidx.paging.PagingSource
+import androidx.room.Dao
+import androidx.room.Delete
+import androidx.room.Insert
+import androidx.room.Query
+import androidx.room.Update
+import com.example.myapplication.database.entities.model.Bike
+import com.example.myapplication.database.entities.model.ItemFromBike
+import kotlinx.coroutines.flow.Flow
+
+@Dao
+interface BikeDao {
+ @Query("select * from bikes order by name")
+ fun getAll(): PagingSource
+
+ @Query(
+ "SELECT c.*, s.uid as item_uid, s.date_time, s.weight, s.max_count-IFNULL(SUM(os.count), 0) as available_count, c.uid as bike_id " +
+ "FROM bikes AS c " +
+ "LEFT JOIN items AS s ON s.bike_id = c.uid " +
+ "LEFT JOIN rents_items AS os ON os.item_id = s.uid " +
+ "WHERE c.uid = :bikeId " +
+ "GROUP BY item_uid"
+ )
+ fun getByUid(bikeId: Int?): Flow