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 @@ + + + + + + + + + + \ 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>> + + @Insert + suspend fun insert(vararg bike: Bike) + + @Update + suspend fun update(bike: Bike) + + @Delete + suspend fun delete(bike: Bike) + + @Query("DELETE FROM bikes") + suspend fun deleteAll() +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/dao/ItemDao.kt b/app/src/main/java/com/example/myapplication/database/entities/dao/ItemDao.kt new file mode 100644 index 0000000..65b7f87 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/dao/ItemDao.kt @@ -0,0 +1,35 @@ +package com.example.myapplication.database.entities.dao + +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.Item + +@Dao +interface ItemDao { + @Query("select * from items where items.uid = :uid") + suspend fun getByUid(uid: Int): Item + + @Insert + suspend fun insert(vararg item: Item) + + @Update + suspend fun update(item: Item) + + @Delete + suspend fun delete(item: Item) + + @Query("DELETE FROM items") + suspend fun deleteAll() + + @Query( + "SELECT s.max_count-IFNULL(SUM(os.count), 0) as available_count " + + "FROM items AS s " + + "LEFT JOIN rents_items AS os ON os.item_id = s.uid " + + "WHERE s.uid = :itemId " + + "GROUP BY s.uid" + ) + suspend fun getAvailableCountOfItem(itemId: Int): Int +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/dao/RentDao.kt b/app/src/main/java/com/example/myapplication/database/entities/dao/RentDao.kt new file mode 100644 index 0000000..297ba91 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/dao/RentDao.kt @@ -0,0 +1,37 @@ +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.Rent +import com.example.myapplication.database.entities.model.ItemFromRent + +@Dao +interface RentDao { + @Query("select * from rents where user_id = :userId") + fun getAll(userId: Int?): PagingSource + + @Query( + "SELECT o.*, s.*, os.count, os.frozen_weight " + + "FROM rents AS o " + + "JOIN rents_items AS os ON os.rent_id = o.uid " + + "JOIN items AS s ON s.uid = os.item_id " + + "WHERE o.uid = :rentId" + ) + fun getByUid(rentId: Int?): List + + @Insert + suspend fun insert(vararg rent: Rent): List + + @Update + suspend fun update(rent: Rent) + + @Delete + suspend fun delete(rent: Rent) + + @Query("DELETE FROM rents") + suspend fun deleteAll() +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/dao/RentItemCrossRefDao.kt b/app/src/main/java/com/example/myapplication/database/entities/dao/RentItemCrossRefDao.kt new file mode 100644 index 0000000..c82b433 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/dao/RentItemCrossRefDao.kt @@ -0,0 +1,27 @@ +package com.example.myapplication.database.entities.dao + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import androidx.room.Update +import com.example.myapplication.database.entities.model.RentItemCrossRef + +@Dao +interface RentItemCrossRefDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insert(rentItemCrossRef: RentItemCrossRef) + + @Update + suspend fun update(rentItemCrossRef: RentItemCrossRef) + + @Delete + suspend fun delete(rentItemCrossRef: RentItemCrossRef) + + @Query("DELETE FROM rents_items where rents_items.rent_id = :rentId") + suspend fun deleteByRentUid(rentId: Int) + + @Query("DELETE FROM rents_items where rents_items.item_id = :itemId") + suspend fun deleteByItemUid(itemId: Int) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/dao/UserDao.kt b/app/src/main/java/com/example/myapplication/database/entities/dao/UserDao.kt new file mode 100644 index 0000000..39f9226 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/dao/UserDao.kt @@ -0,0 +1,42 @@ +package com.example.myapplication.database.entities.dao + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import androidx.room.Update +import com.example.myapplication.database.entities.model.ItemFromCart +import com.example.myapplication.database.entities.model.User +import kotlinx.coroutines.flow.Flow + +@Dao +interface UserDao { + @Query("select * from users") + suspend fun getAll(): List + + @Query("select * from users where users.uid = :idUser") + fun getUserById(idUser: Int?): Flow + + @Query( + "SELECT items.*, items.max_count-IFNULL(SUM(rents_items.count), 0) as available_count, " + + "users_items.count FROM items " + + "join users_items on items.uid = users_items.item_id " + + "left join rents_items on items.uid = rents_items.item_id " + + "where users_items.user_id = :userId " + + "GROUP BY users_items.item_id " + ) + suspend fun getCartByUid(userId: Int): List + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insert(vararg user: User) + + @Update + suspend fun update(user: User) + + @Delete + suspend fun delete(user: User) + + @Query("DELETE FROM users") + suspend fun deleteAll() +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/dao/UserItemCrossRefDao.kt b/app/src/main/java/com/example/myapplication/database/entities/dao/UserItemCrossRefDao.kt new file mode 100644 index 0000000..44436fb --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/dao/UserItemCrossRefDao.kt @@ -0,0 +1,27 @@ +package com.example.myapplication.database.entities.dao + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import androidx.room.Update +import com.example.myapplication.database.entities.model.UserItemCrossRef + +@Dao +interface UserItemCrossRefDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insert(userItemCrossRef: UserItemCrossRef) + + @Update + suspend fun update(userItemCrossRef: UserItemCrossRef) + + @Delete + suspend fun delete(userItemCrossRef: UserItemCrossRef) + + @Query("DELETE FROM users_items where users_items.user_id = :userId") + suspend fun deleteByUserUid(userId: Int) + + @Query("DELETE FROM users_items where users_items.item_id = :itemId") + suspend fun deleteByItemUid(itemId: Int) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/model/Bike.kt b/app/src/main/java/com/example/myapplication/database/entities/model/Bike.kt new file mode 100644 index 0000000..ecfc832 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/model/Bike.kt @@ -0,0 +1,52 @@ +package com.example.myapplication.database.entities.model + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.Ignore +import androidx.room.PrimaryKey + +@Entity(tableName = "bikes") +data class Bike( + @PrimaryKey(autoGenerate = true) + val uid: Int = 0, + val name: String, + val description: String, + @ColumnInfo(typeAffinity = ColumnInfo.BLOB) + val image: ByteArray? +) { + @Ignore + constructor( + name: String, + description: String, + image: ByteArray? + ) : this(0, name, description, image) + + companion object { + fun getBike(index: Int = 0): Bike { + return Bike( + index, + "name", + "desc", + byteArrayOf() + ) + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + other as Bike + if (uid != other.uid) return false + if (name != other.name) return false + if (description != other.description) return false + return image.contentEquals(other.image) + } + + override fun hashCode(): Int { + var result = uid + result = 31 * result + name.hashCode() + result = 31 * result + description.hashCode() + return result + } +} + diff --git a/app/src/main/java/com/example/myapplication/database/entities/model/BikeWithItems.kt b/app/src/main/java/com/example/myapplication/database/entities/model/BikeWithItems.kt new file mode 100644 index 0000000..f0954cf --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/model/BikeWithItems.kt @@ -0,0 +1,24 @@ +package com.example.myapplication.database.entities.model + +data class BikeWithItems( + val bike: Bike, + val items: List +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as BikeWithItems + + if (bike != other.bike) return false + if (items != other.items) return false + + return true + } + + override fun hashCode(): Int { + var result = bike.hashCode() + result = 31 * result + items.hashCode() + return result + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/model/Item.kt b/app/src/main/java/com/example/myapplication/database/entities/model/Item.kt new file mode 100644 index 0000000..9754468 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/model/Item.kt @@ -0,0 +1,73 @@ +package com.example.myapplication.database.entities.model + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.ForeignKey +import androidx.room.Ignore +import androidx.room.PrimaryKey +import org.threeten.bp.LocalDateTime + +@Entity( + tableName = "items", foreignKeys = [ + ForeignKey( + entity = Bike::class, + parentColumns = ["uid"], + childColumns = ["bike_id"], + onDelete = ForeignKey.CASCADE, + onUpdate = ForeignKey.RESTRICT + ) + ] +) +data class Item( + @PrimaryKey(autoGenerate = true) + val uid: Int = 0, + @ColumnInfo(name = "date_time") + val dateTime: LocalDateTime, + val weight: Double, + @ColumnInfo(name = "max_count") + val maxCount: Int, + @ColumnInfo(name = "bike_id", index = true) + val bikeId: Int = 0, +) { + @Ignore + constructor( + dateTime: LocalDateTime, + weight: Double, + maxCount: Int, + bike: Bike, + ) : this(0, dateTime, weight, maxCount, bike.uid) + + companion object { + fun getItem(index: Int = 0): Item { + return Item( + index, + LocalDateTime.MIN, + 0.0, + 0, + 0 + ) + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + other as Item + if (uid != other.uid) return false + if (dateTime != other.dateTime) return false + if (weight != other.weight) return false + if (maxCount != other.maxCount) return false + if (bikeId != other.bikeId) return false + return true + } + + override fun hashCode(): Int { + var result = uid + result = 31 * result + dateTime.hashCode() + result = 31 * result + weight.hashCode() + result = 31 * result + maxCount.hashCode() + result = 31 * result + bikeId.hashCode() + return result + } +} + diff --git a/app/src/main/java/com/example/myapplication/database/entities/model/ItemFromBike.kt b/app/src/main/java/com/example/myapplication/database/entities/model/ItemFromBike.kt new file mode 100644 index 0000000..3553367 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/model/ItemFromBike.kt @@ -0,0 +1,47 @@ +package com.example.myapplication.database.entities.model + +import androidx.room.ColumnInfo +import org.threeten.bp.LocalDateTime +import org.threeten.bp.format.DateTimeFormatter + +data class ItemFromBike( + @ColumnInfo(name = "item_uid") + val uid: Int, + @ColumnInfo(name = "date_time") + val dateTime: LocalDateTime, + val weight: Double, + @ColumnInfo(name = "available_count") + var availableCount: Int, + @ColumnInfo(name = "bike_id") + val bikeId: Int, +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + other as ItemFromBike + if (uid != other.uid) return false + val dateFormatter = DateTimeFormatter.ofPattern("yyyy") + if (dateFormatter.format(dateTime) != dateFormatter.format(other.dateTime)) return false + if (weight != other.weight) return false + if (availableCount != other.availableCount) return false + if (bikeId != other.bikeId) return false + return true + } + + override fun hashCode(): Int { + var result = uid + result = 31 * result + dateTime.hashCode() + result = 31 * result + weight.hashCode() + result = 31 * result + availableCount.hashCode() + result = 31 * result + bikeId.hashCode() + return result + } +} + +fun ItemFromBike.toItem(): Item = Item ( + uid, + dateTime, + weight, + availableCount, + bikeId +) \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/model/ItemFromCart.kt b/app/src/main/java/com/example/myapplication/database/entities/model/ItemFromCart.kt new file mode 100644 index 0000000..02d4a0d --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/model/ItemFromCart.kt @@ -0,0 +1,23 @@ +package com.example.myapplication.database.entities.model + +import androidx.room.ColumnInfo +import androidx.room.Relation +import org.threeten.bp.LocalDateTime + +data class ItemFromCart( + @ColumnInfo(name = "uid") + val uid: Int = 0, + @ColumnInfo(name = "date_time") + val dateTime: LocalDateTime, + val weight: Double, + @ColumnInfo(name = "available_count") + val availableCount: Int, + val count: Int, + @ColumnInfo(name = "bike_id") + val bikeId: Int = 0, + @Relation( + parentColumn = "bike_id", + entity = Bike::class, + entityColumn = "uid" + ) val bike: Bike +) \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/model/ItemFromRent.kt b/app/src/main/java/com/example/myapplication/database/entities/model/ItemFromRent.kt new file mode 100644 index 0000000..e5a02b5 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/model/ItemFromRent.kt @@ -0,0 +1,23 @@ +package com.example.myapplication.database.entities.model + +import androidx.room.ColumnInfo +import androidx.room.Relation +import org.threeten.bp.LocalDateTime + +data class ItemFromRent( + @ColumnInfo(name = "uid") + val uid: Int = 0, + @ColumnInfo(name = "date_time") + val dateTime: LocalDateTime, + @ColumnInfo(name = "frozen_weight") + val frozenWeight: Double, + val count: Int, + @ColumnInfo(name = "bike_id") + val bikeId: Int = 0, + @Relation( + parentColumn = "bike_id", + entity = Bike::class, + entityColumn = "uid" + ) + val bike: Bike +) \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/model/LocalDateTimeConverter.kt b/app/src/main/java/com/example/myapplication/database/entities/model/LocalDateTimeConverter.kt new file mode 100644 index 0000000..884748b --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/model/LocalDateTimeConverter.kt @@ -0,0 +1,16 @@ +package com.example.myapplication.database.entities.model + +import androidx.room.TypeConverter +import org.threeten.bp.LocalDateTime + +class LocalDateTimeConverter { + @TypeConverter + fun fromLocalDateTime(value: LocalDateTime?): String? { + return value?.toString() + } + + @TypeConverter + fun toLocalDateTime(value: String?): LocalDateTime? { + return value?.let { LocalDateTime.parse(it) } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/model/Rent.kt b/app/src/main/java/com/example/myapplication/database/entities/model/Rent.kt new file mode 100644 index 0000000..15bcc8d --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/model/Rent.kt @@ -0,0 +1,37 @@ +package com.example.myapplication.database.entities.model + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.ForeignKey +import androidx.room.PrimaryKey + +@Entity( + tableName = "rents", foreignKeys = [ + ForeignKey( + entity = User::class, + parentColumns = ["uid"], + childColumns = ["user_id"], + onDelete = ForeignKey.RESTRICT, + onUpdate = ForeignKey.RESTRICT + ) + ] +) +data class Rent( + @PrimaryKey(autoGenerate = true) + val uid: Int, + @ColumnInfo(name = "user_id", index = true) + val userId: Int?, +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + other as Rent + if (uid != other.uid) return false + return true + } + + override fun hashCode(): Int { + return uid + } +} + diff --git a/app/src/main/java/com/example/myapplication/database/entities/model/RentItemCrossRef.kt b/app/src/main/java/com/example/myapplication/database/entities/model/RentItemCrossRef.kt new file mode 100644 index 0000000..2a82575 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/model/RentItemCrossRef.kt @@ -0,0 +1,37 @@ +package com.example.myapplication.database.entities.model + +import androidx.room.ColumnInfo +import androidx.room.Entity +import java.util.Objects + +@Entity( + tableName = "rents_items", + primaryKeys = ["rent_id", "item_id"] +) +data class RentItemCrossRef( + @ColumnInfo(name = "rent_id", index = true) + val rentId: Int, + @ColumnInfo(name = "item_id", index = true) + val itemId: Int, + @ColumnInfo(name = "frozen_weight") + val frozenWeight: Double, + val count: Int +) { + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + if (javaClass != other?.javaClass) { + return false + } + other as RentItemCrossRef + if (rentId == other.rentId && itemId == other.itemId) { + return true + } + return false + } + + override fun hashCode(): Int { + return Objects.hash(rentId, itemId) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/model/User.kt b/app/src/main/java/com/example/myapplication/database/entities/model/User.kt new file mode 100644 index 0000000..72a0ec3 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/model/User.kt @@ -0,0 +1,24 @@ +package com.example.myapplication.database.entities.model + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "users") +data class User( + @PrimaryKey(autoGenerate = true) + val uid: Int? = 0, + val login: String, + val password: String +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + other as User + if (uid != other.uid) return false + return true + } + + override fun hashCode(): Int { + return uid ?: -1 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/model/UserItemCrossRef.kt b/app/src/main/java/com/example/myapplication/database/entities/model/UserItemCrossRef.kt new file mode 100644 index 0000000..c071d23 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/model/UserItemCrossRef.kt @@ -0,0 +1,36 @@ +package com.example.myapplication.database.entities.model + +import androidx.room.ColumnInfo +import androidx.room.Entity +import java.util.Objects.hash + +@Entity( + tableName = "users_items", + primaryKeys = ["user_id", "item_id"] +) +data class UserItemCrossRef( + @ColumnInfo(name = "user_id", index = true) + val userId: Int, + @ColumnInfo(name = "item_id", index = true) + val itemId: Int, + @ColumnInfo(name = "count") + val count: Int, +) { + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + if (javaClass != other?.javaClass) { + return false + } + other as UserItemCrossRef + if (userId == other.userId && itemId == other.itemId) { + return true + } + return false + } + + override fun hashCode(): Int { + return hash(userId, itemId) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/repository/BikeRepository.kt b/app/src/main/java/com/example/myapplication/database/entities/repository/BikeRepository.kt new file mode 100644 index 0000000..771247d --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/repository/BikeRepository.kt @@ -0,0 +1,14 @@ +package com.example.myapplication.database.entities.repository + +import androidx.paging.PagingData +import com.example.myapplication.database.entities.model.Bike +import com.example.myapplication.database.entities.model.BikeWithItems +import kotlinx.coroutines.flow.Flow + +interface BikeRepository { + fun getAllBikes(): Flow> + suspend fun getBike(uid: Int): BikeWithItems + suspend fun insertBike(bike: Bike) + suspend fun updateBike(bike: Bike) + suspend fun deleteBike(bike: Bike) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/repository/ItemRepository.kt b/app/src/main/java/com/example/myapplication/database/entities/repository/ItemRepository.kt new file mode 100644 index 0000000..33719ba --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/repository/ItemRepository.kt @@ -0,0 +1,10 @@ +package com.example.myapplication.database.entities.repository + +import com.example.myapplication.database.entities.model.Item + +interface ItemRepository { + suspend fun getItem(uid: Int): Item + suspend fun insertItem(item: Item) + suspend fun updateItem(item: Item) + suspend fun deleteItem(item: Item) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineBikeRepository.kt b/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineBikeRepository.kt new file mode 100644 index 0000000..158805c --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineBikeRepository.kt @@ -0,0 +1,50 @@ +package com.example.myapplication.database.entities.repository + +import androidx.paging.Pager +import androidx.paging.PagingConfig +import androidx.paging.PagingData +import androidx.paging.PagingSource +import com.example.myapplication.database.AppContainer +import com.example.myapplication.database.entities.dao.BikeDao +import com.example.myapplication.database.entities.model.Bike +import com.example.myapplication.database.entities.model.BikeWithItems +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map + +class OfflineBikeRepository(private val bikeDao: BikeDao) : BikeRepository { + override fun getAllBikes(): Flow> = Pager( + config = PagingConfig( + pageSize = AppContainer.LIMIT, + enablePlaceholders = false + ), + pagingSourceFactory = bikeDao::getAll + ).flow + + override suspend fun getBike(uid: Int): BikeWithItems { + val item = bikeDao.getByUid(uid) + .map { map -> + map.firstNotNullOf { + BikeWithItems( + bike = it.key, + items = it.value + ) + } + } + .first() + return item + } + + override suspend fun insertBike(bike: Bike) = bikeDao.insert(bike) + + override suspend fun updateBike(bike: Bike) = bikeDao.update(bike) + + override suspend fun deleteBike(bike: Bike) = bikeDao.delete(bike) + + fun getAllBikesPagingSource(): PagingSource = bikeDao.getAll() + + suspend fun insertBikes(bikes: List) = + bikeDao.insert(*bikes.toTypedArray()) + + suspend fun clearBikes() = bikeDao.deleteAll() +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineItemRepository.kt b/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineItemRepository.kt new file mode 100644 index 0000000..d0c5e2e --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineItemRepository.kt @@ -0,0 +1,19 @@ +package com.example.myapplication.database.entities.repository + +import com.example.myapplication.database.entities.dao.ItemDao +import com.example.myapplication.database.entities.model.Item + +class OfflineItemRepository(private val itemDao: ItemDao) : ItemRepository { + override suspend fun getItem(uid: Int): Item = itemDao.getByUid(uid) + + override suspend fun insertItem(item: Item) = itemDao.insert(item) + + override suspend fun updateItem(item: Item) = itemDao.update(item) + + override suspend fun deleteItem(item: Item) = itemDao.delete(item) + + suspend fun insertItems(items: List) = + itemDao.insert(*items.toTypedArray()) + + suspend fun clearItems() = itemDao.deleteAll() +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineRentItemRepository.kt b/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineRentItemRepository.kt new file mode 100644 index 0000000..dcef834 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineRentItemRepository.kt @@ -0,0 +1,20 @@ +package com.example.myapplication.database.entities.repository + +import com.example.myapplication.database.entities.dao.RentItemCrossRefDao +import com.example.myapplication.database.entities.model.RentItemCrossRef + +class OfflineRentItemRepository(private val rentItemDao: RentItemCrossRefDao) : + RentItemRepository { + override suspend fun insertRentItem(rentItemCrossRef: RentItemCrossRef) = + rentItemDao.insert(rentItemCrossRef) + + override suspend fun updateRentItem(rentItemCrossRef: RentItemCrossRef) = + rentItemDao.update(rentItemCrossRef) + + override suspend fun deleteRentItem(rentItemCrossRef: RentItemCrossRef) = + rentItemDao.delete(rentItemCrossRef) + + suspend fun deleteRentItems(userId: Int) = rentItemDao.deleteByRentUid(userId) + + suspend fun deleteItemsByUid(itemId: Int) = rentItemDao.deleteByItemUid(itemId) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineRentRepository.kt b/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineRentRepository.kt new file mode 100644 index 0000000..a759ede --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineRentRepository.kt @@ -0,0 +1,35 @@ +package com.example.myapplication.database.entities.repository + +import androidx.paging.Pager +import androidx.paging.PagingConfig +import androidx.paging.PagingData +import androidx.paging.PagingSource +import com.example.myapplication.database.AppContainer +import com.example.myapplication.database.entities.dao.RentDao +import com.example.myapplication.database.entities.model.Rent +import com.example.myapplication.database.entities.model.ItemFromRent +import kotlinx.coroutines.flow.Flow + +class OfflineRentRepository(private val rentDao: RentDao) : RentRepository { + override fun getAllRents(userId: Int?): Flow> = Pager( + config = PagingConfig( + pageSize = AppContainer.LIMIT, + enablePlaceholders = false + ), + pagingSourceFactory = { rentDao.getAll(userId) } + ).flow + + override suspend fun getRent(uid: Int): List = rentDao.getByUid(uid) + + override suspend fun insertRent(rent: Rent): Long = rentDao.insert(rent).first() + + fun getAllRentsPagingSource(userId: Int?): PagingSource = rentDao.getAll(userId) + + suspend fun clearRents() = rentDao.deleteAll() + + suspend fun insertRents(rents: List) = rentDao.insert(*rents.toTypedArray()) + + override suspend fun deleteRent(rent: Rent) { + rentDao.delete(rent) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineUserItemRepository.kt b/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineUserItemRepository.kt new file mode 100644 index 0000000..b227b44 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineUserItemRepository.kt @@ -0,0 +1,20 @@ +package com.example.myapplication.database.entities.repository + +import com.example.myapplication.database.entities.dao.UserItemCrossRefDao +import com.example.myapplication.database.entities.model.UserItemCrossRef + +class OfflineUserItemRepository(private val userItemDao: UserItemCrossRefDao) : + UserItemRepository { + override suspend fun insertUserItem(userItemCrossRef: UserItemCrossRef) = + userItemDao.insert(userItemCrossRef) + + override suspend fun updateUserItem(userItemCrossRef: UserItemCrossRef) = + userItemDao.update(userItemCrossRef) + + override suspend fun deleteUserItem(userItemCrossRef: UserItemCrossRef) = + userItemDao.delete(userItemCrossRef) + + override suspend fun deleteUserItems(userId: Int) = userItemDao.deleteByUserUid(userId) + + suspend fun deleteItemsByUid(itemId: Int) = userItemDao.deleteByItemUid(itemId) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineUserRepository.kt b/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineUserRepository.kt new file mode 100644 index 0000000..df88252 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineUserRepository.kt @@ -0,0 +1,27 @@ +package com.example.myapplication.database.entities.repository + +import com.example.myapplication.database.entities.dao.UserDao +import com.example.myapplication.database.entities.model.ItemFromCart +import com.example.myapplication.database.entities.model.User +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.first + +class OfflineUserRepository(private val userDao: UserDao) : UserRepository { + override suspend fun getAllUsers(): List = userDao.getAll() + override suspend fun getUserById(idUser: Int?): User? = userDao.getUserById(idUser).first() + + + override suspend fun getCartByUser(userId: Int): List = + userDao.getCartByUid(userId) + + override suspend fun insertUser(user: User) = userDao.insert(user) + + override suspend fun updateUser(user: User) = userDao.update(user) + + override suspend fun deleteUser(user: User) = userDao.delete(user) + + suspend fun insertUsers(users: List) = + userDao.insert(*users.toTypedArray()) + + suspend fun clearUsers() = userDao.deleteAll() +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/repository/RentItemRepository.kt b/app/src/main/java/com/example/myapplication/database/entities/repository/RentItemRepository.kt new file mode 100644 index 0000000..d3de8f4 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/repository/RentItemRepository.kt @@ -0,0 +1,9 @@ +package com.example.myapplication.database.entities.repository + +import com.example.myapplication.database.entities.model.RentItemCrossRef + +interface RentItemRepository { + suspend fun insertRentItem(rentItemCrossRef: RentItemCrossRef) + suspend fun updateRentItem(rentItemCrossRef: RentItemCrossRef) + suspend fun deleteRentItem(rentItemCrossRef: RentItemCrossRef) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/repository/RentRepository.kt b/app/src/main/java/com/example/myapplication/database/entities/repository/RentRepository.kt new file mode 100644 index 0000000..49159f1 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/repository/RentRepository.kt @@ -0,0 +1,13 @@ +package com.example.myapplication.database.entities.repository + +import androidx.paging.PagingData +import com.example.myapplication.database.entities.model.Rent +import com.example.myapplication.database.entities.model.ItemFromRent +import kotlinx.coroutines.flow.Flow + +interface RentRepository { + fun getAllRents(userId: Int?): Flow> + suspend fun getRent(uid: Int): List + suspend fun insertRent(rent: Rent): Long + suspend fun deleteRent(rent: Rent) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/repository/UserItemRepository.kt b/app/src/main/java/com/example/myapplication/database/entities/repository/UserItemRepository.kt new file mode 100644 index 0000000..7ed9668 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/repository/UserItemRepository.kt @@ -0,0 +1,10 @@ +package com.example.myapplication.database.entities.repository + +import com.example.myapplication.database.entities.model.UserItemCrossRef + +interface UserItemRepository { + suspend fun insertUserItem(userItemCrossRef: UserItemCrossRef) + suspend fun updateUserItem(userItemCrossRef: UserItemCrossRef) + suspend fun deleteUserItem(userItemCrossRef: UserItemCrossRef) + suspend fun deleteUserItems(userId: Int) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/repository/UserRepository.kt b/app/src/main/java/com/example/myapplication/database/entities/repository/UserRepository.kt new file mode 100644 index 0000000..0948b1e --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/repository/UserRepository.kt @@ -0,0 +1,14 @@ +package com.example.myapplication.database.entities.repository + +import com.example.myapplication.database.entities.model.ItemFromCart +import com.example.myapplication.database.entities.model.User +import kotlinx.coroutines.flow.Flow + +interface UserRepository { + suspend fun getAllUsers(): List + suspend fun getUserById(idUser: Int?): User? + suspend fun getCartByUser(userId: Int): List + suspend fun insertUser(user: User) + suspend fun updateUser(user: User) + suspend fun deleteUser(user: User) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/remotekeys/dao/RemoteKeysDao.kt b/app/src/main/java/com/example/myapplication/database/remotekeys/dao/RemoteKeysDao.kt new file mode 100644 index 0000000..ff93909 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/remotekeys/dao/RemoteKeysDao.kt @@ -0,0 +1,20 @@ +package com.example.myapplication.database.remotekeys.dao + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import com.example.myapplication.database.remotekeys.model.RemoteKeyType +import com.example.myapplication.database.remotekeys.model.RemoteKeys + +@Dao +interface RemoteKeysDao { + @Query("SELECT * FROM remote_keys WHERE entityId = :entityId AND type = :type") + suspend fun getRemoteKeys(entityId: Int, type: RemoteKeyType): RemoteKeys? + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertAll(remoteKey: List) + + @Query("DELETE FROM remote_keys WHERE type = :type") + suspend fun clearRemoteKeys(type: RemoteKeyType) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/remotekeys/model/RemoteKeys.kt b/app/src/main/java/com/example/myapplication/database/remotekeys/model/RemoteKeys.kt new file mode 100644 index 0000000..3e94e79 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/remotekeys/model/RemoteKeys.kt @@ -0,0 +1,30 @@ +package com.example.myapplication.database.remotekeys.model + +import androidx.room.Entity +import androidx.room.PrimaryKey +import androidx.room.TypeConverter +import androidx.room.TypeConverters +import com.example.myapplication.database.entities.model.Bike +import com.example.myapplication.database.entities.model.Rent +import com.example.myapplication.database.entities.model.Item + +enum class RemoteKeyType(private val type: String) { + CINEMA(Bike::class.simpleName ?: "Bike"), + ORDER(Rent::class.simpleName ?: "Rent"), + SESSION(Item::class.simpleName ?: "Item"); + + @TypeConverter + fun toRemoteKeyType(value: String) = RemoteKeyType.values().first { it.type == value } + + @TypeConverter + fun fromRemoteKeyType(value: RemoteKeyType) = value.type +} + +@Entity(tableName = "remote_keys") +data class RemoteKeys( + @PrimaryKey val entityId: Int, + @TypeConverters(RemoteKeyType::class) + val type: RemoteKeyType, + val prevKey: Int?, + val nextKey: Int? +) diff --git a/app/src/main/java/com/example/myapplication/database/remotekeys/repository/OfflineRemoteKeyRepository.kt b/app/src/main/java/com/example/myapplication/database/remotekeys/repository/OfflineRemoteKeyRepository.kt new file mode 100644 index 0000000..66387df --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/remotekeys/repository/OfflineRemoteKeyRepository.kt @@ -0,0 +1,16 @@ +package com.example.myapplication.database.remotekeys.repository + +import com.example.myapplication.database.remotekeys.dao.RemoteKeysDao +import com.example.myapplication.database.remotekeys.model.RemoteKeyType +import com.example.myapplication.database.remotekeys.model.RemoteKeys + +class OfflineRemoteKeyRepository(private val remoteKeysDao: RemoteKeysDao) : RemoteKeyRepository { + override suspend fun getAllRemoteKeys(id: Int, type: RemoteKeyType) = + remoteKeysDao.getRemoteKeys(id, type) + + override suspend fun createRemoteKeys(remoteKeys: List) = + remoteKeysDao.insertAll(remoteKeys) + + override suspend fun deleteRemoteKey(type: RemoteKeyType) = + remoteKeysDao.clearRemoteKeys(type) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/remotekeys/repository/RemoteKeyRepository.kt b/app/src/main/java/com/example/myapplication/database/remotekeys/repository/RemoteKeyRepository.kt new file mode 100644 index 0000000..ea47662 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/remotekeys/repository/RemoteKeyRepository.kt @@ -0,0 +1,10 @@ +package com.example.myapplication.database.remotekeys.repository + +import com.example.myapplication.database.remotekeys.model.RemoteKeyType +import com.example.myapplication.database.remotekeys.model.RemoteKeys + +interface RemoteKeyRepository { + suspend fun getAllRemoteKeys(id: Int, type: RemoteKeyType): RemoteKeys? + suspend fun createRemoteKeys(remoteKeys: List) + suspend fun deleteRemoteKey(type: RemoteKeyType) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/datastore/DataStoreManager.kt b/app/src/main/java/com/example/myapplication/datastore/DataStoreManager.kt new file mode 100644 index 0000000..ff9803b --- /dev/null +++ b/app/src/main/java/com/example/myapplication/datastore/DataStoreManager.kt @@ -0,0 +1,25 @@ +package com.example.myapplication.datastore + +import android.content.Context +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.booleanPreferencesKey +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.preferencesDataStore +import kotlinx.coroutines.flow.map + +private val Context.dataStore: DataStore by preferencesDataStore("data_store") + +class DataStoreManager(private val context: Context) { + suspend fun saveSettings(settingData: SettingData) { + context.dataStore.edit { pref -> + pref[booleanPreferencesKey("isDarkTheme")] = settingData.isDarkTheme + } + } + + fun getSettings() = context.dataStore.data.map { pref -> + return@map SettingData( + pref[booleanPreferencesKey("isDarkTheme")] ?: true + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/datastore/SettingData.kt b/app/src/main/java/com/example/myapplication/datastore/SettingData.kt new file mode 100644 index 0000000..f74f0cb --- /dev/null +++ b/app/src/main/java/com/example/myapplication/datastore/SettingData.kt @@ -0,0 +1,5 @@ +package com.example.myapplication.datastore + +data class SettingData( + val isDarkTheme: Boolean +) \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/ui.theme/Color.kt b/app/src/main/java/com/example/myapplication/ui.theme/Color.kt new file mode 100644 index 0000000..4ccb0ab --- /dev/null +++ b/app/src/main/java/com/example/myapplication/ui.theme/Color.kt @@ -0,0 +1,14 @@ +package com.example.myapplication.ui.theme + +import androidx.compose.ui.graphics.Color + +val LightGray = Color(0xFFB2CCD6) +val LightBlueGray = Color(0xFF70A3B2) +val LightBgGray = Color(0xFFCED6DC) + +val Gray = Color(0xFFD6D6D6) +val DarkGray = Color(0xFF191A1F) +val BgGray = Color(0xFF2A2D32) + +val LightBluePolitech = Color(0xFF0158AF) +val DarkBluePolitech = Color(0xFF123E6D) \ No newline at end of file diff --git a/app/src/main/java/com/example/labwork/ui/theme/Theme.kt b/app/src/main/java/com/example/myapplication/ui.theme/Theme.kt similarity index 67% rename from app/src/main/java/com/example/labwork/ui/theme/Theme.kt rename to app/src/main/java/com/example/myapplication/ui.theme/Theme.kt index 4d25afd..791d12e 100644 --- a/app/src/main/java/com/example/labwork/ui/theme/Theme.kt +++ b/app/src/main/java/com/example/myapplication/ui.theme/Theme.kt @@ -1,4 +1,4 @@ -package com.example.labwork.ui.theme +package com.example.myapplication.ui.theme import android.app.Activity import android.os.Build @@ -10,38 +10,42 @@ import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable import androidx.compose.runtime.SideEffect +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalView import androidx.core.view.WindowCompat private val DarkColorScheme = darkColorScheme( - primary = Purple80, - secondary = PurpleGrey80, - tertiary = Pink80 + primary = DarkGray, + onPrimary = Color.White, + + secondary = Gray, + onSecondary = Color.Black, + + background = BgGray, + onBackground = Color.White, ) private val LightColorScheme = lightColorScheme( - primary = DarkBluePolitech, - secondary = PurpleGrey40, - tertiary = LightBluePolitech - - /* Other default colors to override - background = Color(0xFFFFFBFE), - surface = Color(0xFFFFFBFE), + primary = LightBluePolitech, onPrimary = Color.White, - onSecondary = Color.White, - onTertiary = Color.White, - onBackground = Color(0xFF1C1B1F), - onSurface = Color(0xFF1C1B1F), - */ + + secondary = Color.White, + onSecondary = DarkBluePolitech, + + background = Color.White, + onBackground = LightBluePolitech, + + surface = Color.White, // Цвет поверхности (например, для карт или панелей) на светлой схеме цветов. + onSurface = DarkBluePolitech, // Цвет текста или иконок, отображаемых на поверхности на светлой схеме цветов. ) @Composable -fun LabWorkTheme( +fun PmudemoTheme( darkTheme: Boolean = isSystemInDarkTheme(), // Dynamic color is available on Android 12+ - dynamicColor: Boolean = true, + dynamicColor: Boolean = false, content: @Composable () -> Unit ) { val colorScheme = when { diff --git a/app/src/main/java/com/example/labwork/ui/theme/Type.kt b/app/src/main/java/com/example/myapplication/ui.theme/Type.kt similarity index 95% rename from app/src/main/java/com/example/labwork/ui/theme/Type.kt rename to app/src/main/java/com/example/myapplication/ui.theme/Type.kt index ec3e332..64846a0 100644 --- a/app/src/main/java/com/example/labwork/ui/theme/Type.kt +++ b/app/src/main/java/com/example/myapplication/ui.theme/Type.kt @@ -1,4 +1,4 @@ -package com.example.labwork.ui.theme +package com.example.myapplication.ui.theme import androidx.compose.material3.Typography import androidx.compose.ui.text.TextStyle diff --git a/app/src/main/res/drawable-nodpi/kashinmaksim.jpg b/app/src/main/res/drawable-nodpi/kashinmaksim.jpg deleted file mode 100644 index 0d189a7..0000000 Binary files a/app/src/main/res/drawable-nodpi/kashinmaksim.jpg and /dev/null differ diff --git a/app/src/main/res/drawable-nodpi/logo_ulstu.png b/app/src/main/res/drawable-nodpi/logo_ulstu.png deleted file mode 100644 index 1adaa27..0000000 Binary files a/app/src/main/res/drawable-nodpi/logo_ulstu.png and /dev/null differ diff --git a/app/src/main/res/drawable/baseline_account_circle.xml b/app/src/main/res/drawable/baseline_account_circle.xml deleted file mode 100644 index 93ce250..0000000 --- a/app/src/main/res/drawable/baseline_account_circle.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/baseline_directions_bike_24.xml b/app/src/main/res/drawable/baseline_directions_bike_24.xml deleted file mode 100644 index c8848a4..0000000 --- a/app/src/main/res/drawable/baseline_directions_bike_24.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/baseline_info.xml b/app/src/main/res/drawable/baseline_info.xml deleted file mode 100644 index e0ecb40..0000000 --- a/app/src/main/res/drawable/baseline_info.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/baseline_manage_search.xml b/app/src/main/res/drawable/baseline_manage_search.xml deleted file mode 100644 index 98b332c..0000000 --- a/app/src/main/res/drawable/baseline_manage_search.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/baseline_menu_book.xml b/app/src/main/res/drawable/baseline_menu_book.xml deleted file mode 100644 index 21e9852..0000000 --- a/app/src/main/res/drawable/baseline_menu_book.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - diff --git a/app/src/main/res/drawable/baseline_visibility.xml b/app/src/main/res/drawable/baseline_visibility.xml deleted file mode 100644 index b923c39..0000000 --- a/app/src/main/res/drawable/baseline_visibility.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/baseline_visibility_off.xml b/app/src/main/res/drawable/baseline_visibility_off.xml deleted file mode 100644 index 00c8a20..0000000 --- a/app/src/main/res/drawable/baseline_visibility_off.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/minus.xml b/app/src/main/res/drawable/minus.xml new file mode 100644 index 0000000..fdbe783 --- /dev/null +++ b/app/src/main/res/drawable/minus.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/photo.jpg b/app/src/main/res/drawable/photo.jpg new file mode 100644 index 0000000..9f39ccf Binary files /dev/null and b/app/src/main/res/drawable/photo.jpg differ diff --git a/app/src/main/res/drawable/resource__polytech_back.png b/app/src/main/res/drawable/resource__polytech_back.png deleted file mode 100644 index a5d5fa6..0000000 Binary files a/app/src/main/res/drawable/resource__polytech_back.png and /dev/null differ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 22e9cef..0a2844b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,3 +1,21 @@ - LabWork + pmu-demo + Велосипеды + Велосипед + Сеанс + Аренда + Название + Описание + Изображение + Подтверждение + Аренда + Профиль + Сеансы + Время + Сохранить + Записи о велосипедах отсутствуют + Записи о товарах отсутствуют + Велосипед не указан + Размер загруженного изображения: %1$dx%2$d + Загрузите изображение \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index b98170f..bf6a6ee 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,5 +1,5 @@ -