From e4bc55da4ed46d630c064ebf3542134b4b2abbf3 Mon Sep 17 00:00:00 2001 From: dasha Date: Tue, 5 Dec 2023 15:29:15 +0400 Subject: [PATCH] lw4 --- app/build.gradle.kts | 15 +- .../myapplication/ExampleInstrumentedTest.kt | 6 +- app/src/images/photo.jpg | Bin 31567 -> 0 bytes app/src/main/AndroidManifest.xml | 1 + .../myapplication/CinemaApplication.kt | 14 + .../myapplication/MainComposeActivity.kt | 5 +- .../example/myapplication/composeui/Cart.kt | 344 ++++++++++++------ .../composeui/navigation/MainNavbar.kt | 29 +- .../composeui/navigation/Screen.kt | 12 +- .../myapplication/database/AppContainer.kt | 49 +++ .../myapplication/database/AppDatabase.kt | 95 +++-- .../composeui/AppViewModelProvider.kt | 64 ++++ .../entities/composeui/CartViewModel.kt | 70 ++++ .../database/entities/composeui/CinemaList.kt | 198 ++++++++++ .../entities/composeui/CinemaListViewModel.kt | 43 +++ .../database/entities/composeui/CinemaView.kt | 137 +++++++ .../entities/composeui/CinemaViewModel.kt | 31 ++ .../entities/composeui/OrderList.kt | 34 +- .../entities/composeui/OrderListViewModel.kt | 25 ++ .../entities/composeui/OrderView.kt | 33 +- .../entities/composeui/OrderViewModel.kt | 28 ++ .../entities/composeui/SessionList.kt | 141 +++++++ .../composeui/SessionListViewModel.kt | 33 ++ .../entities/composeui/UserProfile.kt | 3 +- .../entities/composeui/edit/CinemaEdit.kt | 127 +++++++ .../composeui/edit/CinemaEditViewModel.kt | 101 +++++ .../entities/composeui/edit/ImageUploader.kt | 121 ++++++ .../entities/composeui/edit/SessionEdit.kt | 150 ++++++++ .../composeui/edit/SessionEditViewModel.kt | 110 ++++++ .../database/entities/dao/CinemaDao.kt | 39 ++ .../database/entities/dao/OrderDao.kt | 34 ++ .../entities/dao/OrderSessionCrossRefDao.kt | 5 +- .../{ => database}/entities/dao/SessionDao.kt | 9 +- .../{ => database}/entities/dao/UserDao.kt | 12 +- .../entities/dao/UserSessionCrossRefDao.kt | 9 +- .../{ => database}/entities/model/Cinema.kt | 27 +- .../entities/model/CinemaWithSessions.kt | 24 ++ .../entities/model/LocalDateTimeConverter.kt | 2 +- .../{ => database}/entities/model/Order.kt | 2 +- .../entities/model/OrderSessionCrossRef.kt | 2 +- .../{ => database}/entities/model/Session.kt | 33 +- .../entities/model/SessionFromCart.kt | 8 +- .../entities/model/SessionFromCinema.kt | 35 ++ .../entities/model/SessionFromOrder.kt | 2 +- .../{ => database}/entities/model/User.kt | 4 +- .../entities/model/UserSessionCrossRef.kt | 36 ++ .../entities/repository/CinemaRepository.kt | 15 + .../repository/OfflineCinemaRepository.kt | 33 ++ .../repository/OfflineOrderRepository.kt | 18 + .../OfflineOrderSessionRepository.kt | 16 + .../repository/OfflineSessionRepository.kt | 15 + .../repository/OfflineUserRepository.kt | 19 + .../OfflineUserSessionRepository.kt | 18 + .../entities/repository/OrderRepository.kt | 13 + .../repository/OrderSessionRepository.kt | 9 + .../entities/repository/SessionRepository.kt | 11 + .../entities/repository/UserRepository.kt | 13 + .../repository/UserSessionRepository.kt | 10 + .../entities/composeui/CinemaList.kt | 109 ------ .../entities/composeui/CinemaView.kt | 209 ----------- .../myapplication/entities/dao/CinemaDao.kt | 33 -- .../myapplication/entities/dao/OrderDao.kt | 32 -- .../entities/model/SessionFromCinema.kt | 14 - .../entities/model/UserSessionCrossRef.kt | 17 - app/src/main/res/drawable/minus.xml | 5 +- app/src/main/res/drawable/photo.jpg | Bin 31567 -> 62928 bytes app/src/main/res/drawable/ticket.xml | 25 -- app/src/main/res/values/strings.xml | 8 + .../example/myapplication/ExampleUnitTest.kt | 3 +- 69 files changed, 2265 insertions(+), 682 deletions(-) delete mode 100644 app/src/images/photo.jpg create mode 100644 app/src/main/java/com/example/myapplication/CinemaApplication.kt create mode 100644 app/src/main/java/com/example/myapplication/database/AppContainer.kt create mode 100644 app/src/main/java/com/example/myapplication/database/entities/composeui/AppViewModelProvider.kt create mode 100644 app/src/main/java/com/example/myapplication/database/entities/composeui/CartViewModel.kt create mode 100644 app/src/main/java/com/example/myapplication/database/entities/composeui/CinemaList.kt create mode 100644 app/src/main/java/com/example/myapplication/database/entities/composeui/CinemaListViewModel.kt create mode 100644 app/src/main/java/com/example/myapplication/database/entities/composeui/CinemaView.kt create mode 100644 app/src/main/java/com/example/myapplication/database/entities/composeui/CinemaViewModel.kt rename app/src/main/java/com/example/myapplication/{ => database}/entities/composeui/OrderList.kt (74%) create mode 100644 app/src/main/java/com/example/myapplication/database/entities/composeui/OrderListViewModel.kt rename app/src/main/java/com/example/myapplication/{ => database}/entities/composeui/OrderView.kt (83%) create mode 100644 app/src/main/java/com/example/myapplication/database/entities/composeui/OrderViewModel.kt create mode 100644 app/src/main/java/com/example/myapplication/database/entities/composeui/SessionList.kt create mode 100644 app/src/main/java/com/example/myapplication/database/entities/composeui/SessionListViewModel.kt rename app/src/main/java/com/example/myapplication/{ => database}/entities/composeui/UserProfile.kt (98%) create mode 100644 app/src/main/java/com/example/myapplication/database/entities/composeui/edit/CinemaEdit.kt create mode 100644 app/src/main/java/com/example/myapplication/database/entities/composeui/edit/CinemaEditViewModel.kt create mode 100644 app/src/main/java/com/example/myapplication/database/entities/composeui/edit/ImageUploader.kt create mode 100644 app/src/main/java/com/example/myapplication/database/entities/composeui/edit/SessionEdit.kt create mode 100644 app/src/main/java/com/example/myapplication/database/entities/composeui/edit/SessionEditViewModel.kt create mode 100644 app/src/main/java/com/example/myapplication/database/entities/dao/CinemaDao.kt create mode 100644 app/src/main/java/com/example/myapplication/database/entities/dao/OrderDao.kt rename app/src/main/java/com/example/myapplication/{ => database}/entities/dao/OrderSessionCrossRefDao.kt (73%) rename app/src/main/java/com/example/myapplication/{ => database}/entities/dao/SessionDao.kt (50%) rename app/src/main/java/com/example/myapplication/{ => database}/entities/dao/UserDao.kt (61%) rename app/src/main/java/com/example/myapplication/{ => database}/entities/dao/UserSessionCrossRefDao.kt (61%) rename app/src/main/java/com/example/myapplication/{ => database}/entities/model/Cinema.kt (51%) create mode 100644 app/src/main/java/com/example/myapplication/database/entities/model/CinemaWithSessions.kt rename app/src/main/java/com/example/myapplication/{ => database}/entities/model/LocalDateTimeConverter.kt (85%) rename app/src/main/java/com/example/myapplication/{ => database}/entities/model/Order.kt (93%) rename app/src/main/java/com/example/myapplication/{ => database}/entities/model/OrderSessionCrossRef.kt (93%) rename app/src/main/java/com/example/myapplication/{ => database}/entities/model/Session.kt (55%) rename app/src/main/java/com/example/myapplication/{ => database}/entities/model/SessionFromCart.kt (78%) create mode 100644 app/src/main/java/com/example/myapplication/database/entities/model/SessionFromCinema.kt rename app/src/main/java/com/example/myapplication/{ => database}/entities/model/SessionFromOrder.kt (90%) rename app/src/main/java/com/example/myapplication/{ => database}/entities/model/User.kt (86%) create mode 100644 app/src/main/java/com/example/myapplication/database/entities/model/UserSessionCrossRef.kt create mode 100644 app/src/main/java/com/example/myapplication/database/entities/repository/CinemaRepository.kt create mode 100644 app/src/main/java/com/example/myapplication/database/entities/repository/OfflineCinemaRepository.kt create mode 100644 app/src/main/java/com/example/myapplication/database/entities/repository/OfflineOrderRepository.kt create mode 100644 app/src/main/java/com/example/myapplication/database/entities/repository/OfflineOrderSessionRepository.kt create mode 100644 app/src/main/java/com/example/myapplication/database/entities/repository/OfflineSessionRepository.kt create mode 100644 app/src/main/java/com/example/myapplication/database/entities/repository/OfflineUserRepository.kt create mode 100644 app/src/main/java/com/example/myapplication/database/entities/repository/OfflineUserSessionRepository.kt create mode 100644 app/src/main/java/com/example/myapplication/database/entities/repository/OrderRepository.kt create mode 100644 app/src/main/java/com/example/myapplication/database/entities/repository/OrderSessionRepository.kt create mode 100644 app/src/main/java/com/example/myapplication/database/entities/repository/SessionRepository.kt create mode 100644 app/src/main/java/com/example/myapplication/database/entities/repository/UserRepository.kt create mode 100644 app/src/main/java/com/example/myapplication/database/entities/repository/UserSessionRepository.kt delete mode 100644 app/src/main/java/com/example/myapplication/entities/composeui/CinemaList.kt delete mode 100644 app/src/main/java/com/example/myapplication/entities/composeui/CinemaView.kt delete mode 100644 app/src/main/java/com/example/myapplication/entities/dao/CinemaDao.kt delete mode 100644 app/src/main/java/com/example/myapplication/entities/dao/OrderDao.kt delete mode 100644 app/src/main/java/com/example/myapplication/entities/model/SessionFromCinema.kt delete mode 100644 app/src/main/java/com/example/myapplication/entities/model/UserSessionCrossRef.kt delete mode 100644 app/src/main/res/drawable/ticket.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f41a4b8..45a4f09 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -10,7 +10,7 @@ android { defaultConfig { applicationId = "com.example.myapplication" - minSdk = 24 + minSdk = 26 targetSdk = 33 versionCode = 1 versionName = "1.0" @@ -31,6 +31,7 @@ android { } } compileOptions { + isCoreLibraryDesugaringEnabled = true sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } @@ -51,8 +52,16 @@ android { } dependencies { - implementation("com.jakewharton.threetenabp:threetenabp:1.2.1") + 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") + + // Pagination + val paging_version = "3.2.0-rc01" + implementation("androidx.paging:paging-runtime:$paging_version") + implementation("androidx.paging:paging-compose:$paging_version") + // Core implementation("androidx.core:core-ktx:1.9.0") implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2") @@ -64,7 +73,7 @@ dependencies { 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") // Room val room_version = "2.5.2" diff --git a/app/src/androidTest/java/com/example/myapplication/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/example/myapplication/ExampleInstrumentedTest.kt index e9283cf..e9489e1 100644 --- a/app/src/androidTest/java/com/example/myapplication/ExampleInstrumentedTest.kt +++ b/app/src/androidTest/java/com/example/myapplication/ExampleInstrumentedTest.kt @@ -1,13 +1,11 @@ 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. * diff --git a/app/src/images/photo.jpg b/app/src/images/photo.jpg deleted file mode 100644 index 9fb88cf01ba39e610afd4378d070e7263d9097e2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31567 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 DtcxF{ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2900ac3..084d36d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ xmlns:tools="http://schemas.android.com/tools"> () } - LaunchedEffect(Unit) { - withContext(Dispatchers.IO) { - AppDatabase.getInstance(context).userDao().getCartByUid(id).collect { data -> - sessions.clear() - sessions.addAll(data) +fun Cart( + viewModel: CartViewModel = viewModel(factory = AppViewModelProvider.Factory) +) { + val coroutineScope = rememberCoroutineScope() + val cartUiState by viewModel.cartUiState.collectAsState() + + Cart( + cartUiState = cartUiState, + modifier = Modifier + .padding(all = 10.dp), + onSwipe = { session: SessionFromCart, user: Int -> + coroutineScope.launch { + viewModel.removeFromCart( + session = Session( + uid = session.uid, + dateTime = session.dateTime, + price = session.price, + maxCount = 0, + cinemaId = session.cinemaId + ), user = user + ) + } + }, + onChangeCount = { session: SessionFromCart, user: Int, count: Int -> + coroutineScope.launch { + viewModel.updateFromCart( + session = Session( + uid = session.uid, + dateTime = session.dateTime, + price = session.price, + maxCount = 0, + cinemaId = session.cinemaId + ), userId = user, count = count, availableCount = session.availableCount + ) + } + }, + onAddToOrder = { sessions: List, user: Int -> + coroutineScope.launch { + viewModel.addToOrder(sessions = sessions, userId = user) } } - } + ) +} +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun Cart( + cartUiState: CartUiState, + modifier: Modifier, + onSwipe: (SessionFromCart, Int) -> Unit, + onChangeCount: (SessionFromCart, Int, Int) -> Unit, + onAddToOrder: (List, Int) -> Unit +) { LazyColumn( - modifier = Modifier - .padding(all = 10.dp) + modifier = modifier ) { - items(sessions) { session -> - var currentCount by remember { mutableStateOf(session.count) } - - val dateFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm") - val formattedDate = dateFormatter.format(session.dateTime) - - Text( - text = formattedDate, - color = MaterialTheme.colorScheme.onBackground, + items(cartUiState.sessionList, key = { it.uid.toString() }) { session -> + val dismissState: DismissState = rememberDismissState( + positionalThreshold = { 200.dp.toPx() } ) - Box( - modifier = Modifier - .fillMaxWidth() - .padding(10.dp) - .clip(RoundedCornerShape(16.dp)) - .background(MaterialTheme.colorScheme.secondary) - ) { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(8.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween - ) { - if (session.cinema.image != null) - Image( - bitmap = BitmapFactory.decodeByteArray( - session.cinema.image, - 0, - session.cinema.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 = "${session.cinema.name}, ${session.cinema.year}\n" + - "Цена: ${session.price}\n" + - "${currentCount}/${session.availableCount}", - color = MaterialTheme.colorScheme.onSecondary - ) - } - - Box( - modifier = Modifier - .background( - color = MaterialTheme.colorScheme.background, - shape = RoundedCornerShape(10.dp) - ) // Задаем фон для кнопок - ) { - Row( - verticalAlignment = Alignment.CenterVertically - ) { - IconButton( - onClick = { - if (currentCount > 0) { - currentCount-- - } - } - ) { - Icon( - imageVector = ImageVector.vectorResource(id = R.drawable.minus), - contentDescription = "Уменьшить", - tint = MaterialTheme.colorScheme.onBackground, - modifier = Modifier.size(10.dp) - ) - } - - Text( - text = "$currentCount", - color = MaterialTheme.colorScheme.onBackground - ) - - IconButton( - onClick = { - if (currentCount < session.availableCount) { - currentCount++ - } - } - ) { - Icon( - imageVector = Icons.Default.Add, - contentDescription = "Увеличить", - tint = MaterialTheme.colorScheme.onBackground, - modifier = Modifier.size(10.dp) - ) - } - } - } - } + if (dismissState.isDismissed(direction = DismissDirection.EndToStart)) { + onSwipe(session, 1) } + + SwipeToDelete( + dismissState = dismissState, + session = session, + onChangeCount = onChangeCount + ) } } Column { Spacer(modifier = Modifier.weight(1f)) Button( - onClick = { }, + onClick = { onAddToOrder(cartUiState.sessionList, 1) }, modifier = Modifier .padding(16.dp) .fillMaxWidth() @@ -175,6 +146,151 @@ fun Cart(id: Int) { } } +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun SwipeToDelete( + dismissState: DismissState, + session: SessionFromCart, + onChangeCount: (SessionFromCart, 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 = { + SessionListItem( + session = session, + modifier = Modifier + .fillMaxWidth() + .padding(10.dp) + .clip(RoundedCornerShape(16.dp)) + .background(MaterialTheme.colorScheme.secondary), + onChangeCount = onChangeCount + ) + } + ) +} + +@Composable +private fun SessionListItem( + session: SessionFromCart, + modifier: Modifier = Modifier, + onChangeCount: (SessionFromCart, Int, Int) -> Unit, +) { + var currentCount by remember { mutableStateOf(session.count) } + + val dateFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm") + val formattedDate = dateFormatter.format(session.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 (session.cinema.image != null) + Image( + bitmap = BitmapFactory.decodeByteArray( + session.cinema.image, + 0, + session.cinema.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 = "${session.cinema.name}, ${session.cinema.year}\n" + + "Цена: ${session.price}\n" + + "${currentCount}/${session.availableCount}", + color = MaterialTheme.colorScheme.onSecondary + ) + } + + Box( + modifier = Modifier + .background( + color = MaterialTheme.colorScheme.background, + shape = RoundedCornerShape(10.dp) + ) // Задаем фон для кнопок + ) { + Row( + verticalAlignment = Alignment.CenterVertically + ) { + IconButton( + onClick = { onChangeCount(session, 1, --currentCount) } + ) { + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.minus), + contentDescription = "Уменьшить", + tint = MaterialTheme.colorScheme.onBackground, + modifier = Modifier.size(10.dp) + ) + } + + Text( + text = "$currentCount", + color = MaterialTheme.colorScheme.onBackground + ) + + IconButton( + onClick = { onChangeCount(session, 1, ++currentCount) } + ) { + Icon( + imageVector = Icons.Default.Add, + contentDescription = "Увеличить", + tint = MaterialTheme.colorScheme.onBackground, + 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) @@ -184,7 +300,7 @@ fun CartPreview() { Surface( color = MaterialTheme.colorScheme.background ) { - Cart(1) + 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 index 6f05f44..1e5c3b0 100644 --- a/app/src/main/java/com/example/myapplication/composeui/navigation/MainNavbar.kt +++ b/app/src/main/java/com/example/myapplication/composeui/navigation/MainNavbar.kt @@ -47,12 +47,14 @@ 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.CinemaList +import com.example.myapplication.database.entities.composeui.CinemaView +import com.example.myapplication.database.entities.composeui.OrderList +import com.example.myapplication.database.entities.composeui.OrderView +import com.example.myapplication.database.entities.composeui.UserProfile +import com.example.myapplication.database.entities.composeui.edit.CinemaEdit +import com.example.myapplication.database.entities.composeui.edit.SessionEdit import com.example.myapplication.datastore.DataStoreManager -import com.example.myapplication.entities.composeui.CinemaList -import com.example.myapplication.entities.composeui.CinemaView -import com.example.myapplication.entities.composeui.OrderList -import com.example.myapplication.entities.composeui.OrderView -import com.example.myapplication.user.composeui.UserProfile @Composable fun Topbar( @@ -175,13 +177,26 @@ fun Navhost( ) { composable(Screen.CinemaList.route) { CinemaList(navController) } composable(Screen.OrderList.route) { OrderList(navController, 1) } - composable(Screen.Cart.route) { Cart(1) } + composable(Screen.Cart.route) { Cart() } composable(Screen.UserProfile.route) { UserProfile(isDarkTheme, dataStore) } + composable( + Screen.CinemaEdit.route, + arguments = listOf(navArgument("id") { type = NavType.IntType }) + ) { + CinemaEdit(navController) + } + composable( + Screen.SessionEdit.route, + arguments = listOf(navArgument("id") { type = NavType.IntType }, + navArgument("cinemaId") { type = NavType.IntType }) + ) { + SessionEdit(navController) + } composable( Screen.CinemaView.route, arguments = listOf(navArgument("id") { type = NavType.IntType }) ) { backStackEntry -> - backStackEntry.arguments?.let { CinemaView(it.getInt("id")) } + backStackEntry.arguments?.let { CinemaView(navController) } } composable( Screen.OrderView.route, 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 index 05900da..522367b 100644 --- a/app/src/main/java/com/example/myapplication/composeui/navigation/Screen.kt +++ b/app/src/main/java/com/example/myapplication/composeui/navigation/Screen.kt @@ -18,6 +18,15 @@ enum class Screen( CinemaList( "Cinema-list", R.string.Cinema_main_title, Icons.Filled.Home ), + CinemaEdit( + "Cinema-edit/{id}", R.string.Cinema_view_title, showInBottomBar = false + ), + SessionEdit( + "Session-edit/{id}/{cinemaId}", R.string.Session_view_title, showInBottomBar = false + ), + CinemaView( + "Cinema-view/{id}", R.string.Cinema_view_title, showInBottomBar = false + ), SessionList( "Session-list", R.string.Sessions_title, showInBottomBar = false ), @@ -27,9 +36,6 @@ enum class Screen( OrderList( "Order-list", R.string.Order_title, Icons.Filled.List ), - CinemaView( - "Cinema-view/{id}", R.string.Cinema_view_title, showInBottomBar = false - ), OrderView( "Order-view/{id}", R.string.Order_view_title, showInBottomBar = false ), 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..eb053a3 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/AppContainer.kt @@ -0,0 +1,49 @@ +package com.example.myapplication.database + +import android.content.Context +import com.example.myapplication.database.entities.repository.CinemaRepository +import com.example.myapplication.database.entities.repository.OfflineCinemaRepository +import com.example.myapplication.database.entities.repository.OfflineOrderRepository +import com.example.myapplication.database.entities.repository.OfflineOrderSessionRepository +import com.example.myapplication.database.entities.repository.OfflineSessionRepository +import com.example.myapplication.database.entities.repository.OfflineUserRepository +import com.example.myapplication.database.entities.repository.OfflineUserSessionRepository +import com.example.myapplication.database.entities.repository.OrderRepository +import com.example.myapplication.database.entities.repository.OrderSessionRepository +import com.example.myapplication.database.entities.repository.SessionRepository +import com.example.myapplication.database.entities.repository.UserRepository +import com.example.myapplication.database.entities.repository.UserSessionRepository + +interface AppContainer { + val cinemaRepository: CinemaRepository + val orderRepository: OrderRepository + val orderSessionRepository: OrderSessionRepository + val sessionRepository: SessionRepository + val userRepository: UserRepository + val userSessionRepository: UserSessionRepository +} + +class AppDataContainer(private val context: Context) : AppContainer { + override val cinemaRepository: CinemaRepository by lazy { + OfflineCinemaRepository(AppDatabase.getInstance(context).cinemaDao()) + } + override val orderRepository: OrderRepository by lazy { + OfflineOrderRepository(AppDatabase.getInstance(context).orderDao()) + } + override val orderSessionRepository: OrderSessionRepository by lazy { + OfflineOrderSessionRepository(AppDatabase.getInstance(context).orderSessionCrossRefDao()) + } + override val sessionRepository: SessionRepository by lazy { + OfflineSessionRepository(AppDatabase.getInstance(context).sessionDao()) + } + override val userRepository: UserRepository by lazy { + OfflineUserRepository(AppDatabase.getInstance(context).userDao()) + } + override val userSessionRepository: UserSessionRepository by lazy { + OfflineUserSessionRepository(AppDatabase.getInstance(context).userSessionCrossRefDao()) + } + + companion object { + const val TIMEOUT = 5000L + } +} \ 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 index 7baa239..a7d437e 100644 --- a/app/src/main/java/com/example/myapplication/database/AppDatabase.kt +++ b/app/src/main/java/com/example/myapplication/database/AppDatabase.kt @@ -2,25 +2,24 @@ package com.example.myapplication.database import android.content.Context import android.graphics.Bitmap -import android.graphics.Color 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.entities.dao.CinemaDao -import com.example.myapplication.entities.dao.OrderDao -import com.example.myapplication.entities.dao.OrderSessionCrossRefDao -import com.example.myapplication.entities.dao.SessionDao -import com.example.myapplication.entities.dao.UserDao -import com.example.myapplication.entities.dao.UserSessionCrossRefDao -import com.example.myapplication.entities.model.Cinema -import com.example.myapplication.entities.model.LocalDateTimeConverter -import com.example.myapplication.entities.model.Order -import com.example.myapplication.entities.model.OrderSessionCrossRef -import com.example.myapplication.entities.model.Session -import com.example.myapplication.entities.model.User -import com.example.myapplication.entities.model.UserSessionCrossRef +import com.example.myapplication.database.entities.dao.CinemaDao +import com.example.myapplication.database.entities.dao.OrderDao +import com.example.myapplication.database.entities.dao.OrderSessionCrossRefDao +import com.example.myapplication.database.entities.dao.SessionDao +import com.example.myapplication.database.entities.dao.UserDao +import com.example.myapplication.database.entities.dao.UserSessionCrossRefDao +import com.example.myapplication.database.entities.model.Cinema +import com.example.myapplication.database.entities.model.LocalDateTimeConverter +import com.example.myapplication.database.entities.model.Order +import com.example.myapplication.database.entities.model.OrderSessionCrossRef +import com.example.myapplication.database.entities.model.Session +import com.example.myapplication.database.entities.model.User +import com.example.myapplication.database.entities.model.UserSessionCrossRef import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -58,12 +57,30 @@ abstract class AppDatabase : RoomDatabase() { userDao.insert(user2) // Cinemas val cinemaDao = database.cinemaDao() - val cinema1 = Cinema(1, "BLUE 1", "Desc1", createColoredImage(Color.BLUE), 2023) - val cinema2 = Cinema(2, "GREEN 2", "Desc2", createColoredImage(Color.GREEN), 2023) - val cinema3 = Cinema(3, "RED 3", "Desc3", createColoredImage(Color.RED), 2023) + val cinema1 = + Cinema(1, "a", "Desc1", createColoredImage(android.graphics.Color.BLUE), 2023) + val cinema2 = + Cinema(2, "b", "Desc2", createColoredImage(android.graphics.Color.GREEN), 2023) + val cinema3 = + Cinema(3, "c", "Desc3", createColoredImage(android.graphics.Color.RED), 2023) + val cinema4 = + Cinema(4, "d", "Desc4", createColoredImage(android.graphics.Color.CYAN), 2023) cinemaDao.insert(cinema1) cinemaDao.insert(cinema2) cinemaDao.insert(cinema3) + cinemaDao.insert(cinema4) + + for (i in 5..20) { + val cinema = Cinema( + uid = i, + name = generateCinemaName(i), + description = "Description $i", + image = createColoredImage(getRandomColorInt()), + year = 2023 + ) + cinemaDao.insert(cinema) + } + // Orders val orderDao = database.orderDao() val order1 = Order(1, 1) @@ -79,28 +96,32 @@ abstract class AppDatabase : RoomDatabase() { val session1 = Session(1, LocalDateTime.now(), 150.0, 120, cinema1.uid) val session2 = Session(2, LocalDateTime.now(), 200.0, 110, cinema2.uid) val session3 = Session(3, LocalDateTime.now(), 300.0, 100, cinema3.uid) - val session4 = Session(4, LocalDateTime.now(), 320.0, 1150, cinema1.uid) + val session4 = Session(4, LocalDateTime.now(), 450.0, 200, cinema1.uid) sessionDao.insert(session1) sessionDao.insert(session2) sessionDao.insert(session3) sessionDao.insert(session4) // OrderSessionCrossRef для связи заказов с сеансами val orderSessionCrossRefDao = database.orderSessionCrossRefDao() - if (session1.uid != null && session2.uid != null && session3.uid != null && session4.uid != null) { - val orderSessionCrossRef1 = OrderSessionCrossRef(order1.uid, session3.uid, 150.0, 5) - val orderSessionCrossRef2 = OrderSessionCrossRef(order1.uid, session2.uid, 300.0, 10) - val orderSessionCrossRef3 = OrderSessionCrossRef(order2.uid, session2.uid, 350.0, 6) - val orderSessionCrossRef4 = OrderSessionCrossRef(order3.uid, session1.uid, 250.0, 10) - val orderSessionCrossRef5 = OrderSessionCrossRef(order3.uid, session3.uid, 150.0, 16) - val orderSessionCrossRef6 = OrderSessionCrossRef(order4.uid, session3.uid, 150.0, 2) - //val orderSessionCrossRef7 = OrderSessionCrossRef(order4.uid, session4.uid, 110.0, 1) + if (session1.uid != null && session2.uid != null && session3.uid != null) { + val orderSessionCrossRef1 = + OrderSessionCrossRef(order1.uid, session3.uid, 150.0, 5) + val orderSessionCrossRef2 = + OrderSessionCrossRef(order1.uid, session2.uid, 300.0, 10) + val orderSessionCrossRef3 = + OrderSessionCrossRef(order2.uid, session2.uid, 350.0, 6) + val orderSessionCrossRef4 = + OrderSessionCrossRef(order3.uid, session1.uid, 250.0, 10) + val orderSessionCrossRef5 = + OrderSessionCrossRef(order3.uid, session3.uid, 150.0, 16) + val orderSessionCrossRef6 = + OrderSessionCrossRef(order4.uid, session3.uid, 150.0, 2) orderSessionCrossRefDao.insert(orderSessionCrossRef1) orderSessionCrossRefDao.insert(orderSessionCrossRef2) orderSessionCrossRefDao.insert(orderSessionCrossRef3) orderSessionCrossRefDao.insert(orderSessionCrossRef4) orderSessionCrossRefDao.insert(orderSessionCrossRef5) orderSessionCrossRefDao.insert(orderSessionCrossRef6) - //orderSessionCrossRefDao.insert(orderSessionCrossRef7) } // UserSessions val userSessionCrossRefDao = database.userSessionCrossRefDao() @@ -143,5 +164,25 @@ abstract class AppDatabase : RoomDatabase() { return stream.toByteArray() } + + 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 + } + + fun generateCinemaName(index: Int): String { + val base = 'a'.toInt() + 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..43e487e --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/AppViewModelProvider.kt @@ -0,0 +1,64 @@ +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.CinemaApplication +import com.example.myapplication.database.entities.composeui.edit.CinemaEditViewModel +import com.example.myapplication.database.entities.composeui.edit.SessionEditViewModel + +object AppViewModelProvider { + val Factory = viewModelFactory { + initializer { + CinemaListViewModel(cinemaApplication().container.cinemaRepository) + } + initializer { + CinemaEditViewModel( + this.createSavedStateHandle(), + cinemaApplication().container.cinemaRepository + ) + } + initializer { + CinemaViewModel( + this.createSavedStateHandle(), + cinemaApplication().container.cinemaRepository, + ) + } + initializer { + SessionListViewModel( + cinemaApplication().container.sessionRepository, + cinemaApplication().container.userSessionRepository, + ) + } + initializer { + SessionEditViewModel( + this.createSavedStateHandle(), + cinemaApplication().container.sessionRepository, + ) + } + initializer { + CartViewModel( + cinemaApplication().container.userSessionRepository, + cinemaApplication().container.orderRepository, + cinemaApplication().container.orderSessionRepository, + cinemaApplication().container.userRepository, + ) + } + initializer { + OrderListViewModel( + cinemaApplication().container.orderRepository, + ) + } + initializer { + OrderViewModel( + this.createSavedStateHandle(), + cinemaApplication().container.orderRepository, + ) + } + } +} + +fun CreationExtras.cinemaApplication(): CinemaApplication = + (this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as CinemaApplication) \ 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..06f353c --- /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.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.example.myapplication.database.AppDataContainer +import com.example.myapplication.database.entities.model.Order +import com.example.myapplication.database.entities.model.OrderSessionCrossRef +import com.example.myapplication.database.entities.model.Session +import com.example.myapplication.database.entities.model.SessionFromCart +import com.example.myapplication.database.entities.model.UserSessionCrossRef +import com.example.myapplication.database.entities.repository.OrderRepository +import com.example.myapplication.database.entities.repository.OrderSessionRepository +import com.example.myapplication.database.entities.repository.UserRepository +import com.example.myapplication.database.entities.repository.UserSessionRepository +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn + +class CartViewModel( + private val userSessionRepository: UserSessionRepository, + private val orderRepository: OrderRepository, + private val orderSessionRepository: OrderSessionRepository, + private val userRepository: UserRepository, +) : ViewModel() { + private val userUid: Int = 1 + + val cartUiState: StateFlow = userRepository.getCartByUser(userUid).map { + CartUiState(it) + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(stopTimeoutMillis = AppDataContainer.TIMEOUT), + initialValue = CartUiState() + ) + + suspend fun addToOrder(userId: Int, sessions: List) { + if (sessions.isEmpty()) + return + val orderId = orderRepository.insertOrder(Order(0, userId)) + sessions.forEach { session -> + orderSessionRepository.insertOrderSession( + OrderSessionCrossRef( + orderId.toInt(), + session.uid, + session.price, + session.count + ) + ) + } + userSessionRepository.deleteUserSessions(userId) + } + + suspend fun removeFromCart(user: Int, session: Session, count: Int = 1) { + userSessionRepository.deleteUserSession(UserSessionCrossRef(user, session.uid, count)) + } + + suspend fun updateFromCart(userId: Int, session: Session, count: Int, availableCount: Int) + : Boolean { + if (count == 0) { + removeFromCart(userId, session, count) + return false + } + if (count > availableCount) + return false + userSessionRepository.updateUserSession(UserSessionCrossRef(userId, session.uid, count)) + return true + } +} + +data class CartUiState(val sessionList: List = listOf()) \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/composeui/CinemaList.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/CinemaList.kt new file mode 100644 index 0000000..98c0934 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/CinemaList.kt @@ -0,0 +1,198 @@ +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.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.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.LaunchedEffect +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +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.Cinema +import kotlinx.coroutines.launch + +@Composable +fun CinemaList( + navController: NavController, + viewModel: CinemaListViewModel = viewModel(factory = AppViewModelProvider.Factory) +) { + val coroutineScope = rememberCoroutineScope() + + val cinemaPagingItems = viewModel.cinemaPagerState.cinemaPagingData.collectAsLazyPagingItems() + + fun findCinemas() { + coroutineScope.launch { + viewModel.findCinemas() + } + } + LaunchedEffect(1) { + findCinemas() + } + + Scaffold( + topBar = {}, + floatingActionButton = { + FloatingActionButton( + onClick = { + val route = Screen.CinemaEdit.route.replace("{id}", 0.toString()) + navController.navigate(route) + }, + containerColor = MaterialTheme.colorScheme.primary, + ) { + Icon( + Icons.Filled.Add, + "Добавить", + tint = MaterialTheme.colorScheme.onPrimary + ) + } + } + ) { innerPadding -> + CinemaList( + modifier = Modifier + .padding(innerPadding) + .fillMaxSize(), + pagingCinema = cinemaPagingItems, + onClick = { uid: Int -> + val route = Screen.CinemaView.route.replace("{id}", uid.toString()) + navController.navigate(route) + }, + onDeleteClick = { cinema: Cinema -> + coroutineScope.launch { + viewModel.deleteCinema(cinema) + } + }, + onEditClick = { uid: Int -> + val route = Screen.CinemaEdit.route.replace("{id}", uid.toString()) + navController.navigate(route) + }, + ) + } +} + +@Composable +private fun CinemaList( + modifier: Modifier = Modifier, + pagingCinema: LazyPagingItems, + onClick: (uid: Int) -> Unit, + onDeleteClick: (cinema: Cinema) -> Unit, + onEditClick: (cinema: Int) -> Unit +) { + Column( + modifier = modifier + ) { + LazyColumn( + modifier = Modifier + .fillMaxSize() + .padding(all = 10.dp) + ) { + items(pagingCinema.itemCount) { index -> + val cinema = pagingCinema[index] + if (cinema != null) { + CinemaListItem( + cinema = cinema, + modifier = Modifier + .padding(vertical = 7.dp) + .clickable { onClick(cinema.uid) } + .background( + color = MaterialTheme.colorScheme.secondary, + shape = RoundedCornerShape(16.dp) + ), + onDeleteClick = onDeleteClick, + onEditClick = onEditClick, + ) + } + } + } + + } +} + +@Composable +private fun CinemaListItem( + cinema: Cinema, + modifier: Modifier = Modifier, + onDeleteClick: (cinema: Cinema) -> Unit, + onEditClick: (cinema: Int) -> Unit +) { + Box( + modifier = modifier + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(8.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + if (cinema.image != null) + Image( + bitmap = BitmapFactory.decodeByteArray( + cinema.image, + 0, + cinema.image.size + ).asImageBitmap(), + contentDescription = null, + modifier = Modifier + .size(90.dp) + .padding(4.dp) + ) + + Text( + "${cinema.name}, ${cinema.year}", + color = MaterialTheme.colorScheme.onSecondary + ) + + // Добавляем пустое пространство для разделения текста и кнопки + Spacer(modifier = Modifier.weight(1f)) + IconButton( + onClick = { onEditClick(cinema.uid) }, + modifier = Modifier.size(24.dp) + ) { + Icon( + imageVector = Icons.Default.Edit, + contentDescription = "Редактировать", + tint = MaterialTheme.colorScheme.onSecondary, + ) + } + IconButton( + onClick = { onDeleteClick(cinema) }, + modifier = Modifier.size(24.dp) + ) { + Icon( + imageVector = Icons.Default.Delete, + contentDescription = "Удалить", + tint = MaterialTheme.colorScheme.onSecondary, + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/composeui/CinemaListViewModel.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/CinemaListViewModel.kt new file mode 100644 index 0000000..e96a051 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/CinemaListViewModel.kt @@ -0,0 +1,43 @@ +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.paging.Pager +import androidx.paging.PagingConfig +import androidx.paging.PagingData +import com.example.myapplication.database.entities.model.Cinema +import com.example.myapplication.database.entities.repository.CinemaRepository +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow + +class CinemaListViewModel( + private val cinemaRepository: CinemaRepository +) : ViewModel() { + private val pagingConfig = PagingConfig( + pageSize = 4, + prefetchDistance = 4 + ) + + var cinemaPagerState by mutableStateOf(CinemaPagerState()) + private set + + fun findCinemas() { + val pager = Pager( + config = pagingConfig, + pagingSourceFactory = { + cinemaRepository.getAllCinemasPaged() + } + ) + cinemaPagerState = CinemaPagerState(pager.flow) + } + + suspend fun deleteCinema(cinema: Cinema) { + cinemaRepository.deleteCinema(cinema) + } +} + +data class CinemaPagerState( + val cinemaPagingData: Flow> = emptyFlow() +) \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/composeui/CinemaView.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/CinemaView.kt new file mode 100644 index 0000000..3413289 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/CinemaView.kt @@ -0,0 +1,137 @@ +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.collectAsState +import androidx.compose.runtime.getValue +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.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.Cinema + +@Composable +fun CinemaView( + navController: NavController, + viewModel: CinemaViewModel = viewModel(factory = AppViewModelProvider.Factory), +) { + val cinemaUiState by viewModel.cinemaUiState.collectAsState() + + Column( + modifier = Modifier + .padding(16.dp) + .fillMaxSize(), + ) { + val cinema: Cinema? = cinemaUiState.cinemaWithSessions?.cinema + if (cinema != 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 = "${cinema.name}, ${cinema.year}", + style = TextStyle( + fontWeight = FontWeight.Bold, + fontSize = 18.sp, + color = MaterialTheme.colorScheme.onSecondary + ), + modifier = Modifier + .padding(bottom = 8.dp) + ) + } + + if (cinema.image != null) + Image( + bitmap = BitmapFactory.decodeByteArray( + cinema.image, + 0, + cinema.image.size + ).asImageBitmap(), + contentDescription = null, + modifier = Modifier + .fillMaxWidth() + .height(200.dp) + .padding(4.dp) + ) + + Text( + text = cinema.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.SessionEdit.route.replace("{id}", 0.toString()) + .replace( + "{cinemaId}", + cinemaUiState.cinemaWithSessions?.cinema?.uid.toString() + ) + navController.navigate(route) + } + ) { + Icon( + imageVector = Icons.Filled.Add, + contentDescription = "Добавить сеанс", + ) + } + } + if (cinemaUiState.cinemaWithSessions != null) { + SessionList(cinemaUiState.cinemaWithSessions!!, navController) + } + } +} diff --git a/app/src/main/java/com/example/myapplication/database/entities/composeui/CinemaViewModel.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/CinemaViewModel.kt new file mode 100644 index 0000000..5407560 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/CinemaViewModel.kt @@ -0,0 +1,31 @@ +package com.example.myapplication.database.entities.composeui + +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.example.myapplication.database.AppDataContainer +import com.example.myapplication.database.entities.model.CinemaWithSessions +import com.example.myapplication.database.entities.repository.CinemaRepository +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn + +class CinemaViewModel( + savedStateHandle: SavedStateHandle, + private val cinemaRepository: CinemaRepository +) : ViewModel() { + private val cinemaUid: Int = checkNotNull(savedStateHandle["id"]) + + val cinemaUiState: StateFlow = cinemaRepository.getCinema( + cinemaUid + ).map { + CinemaUiState(it) + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(stopTimeoutMillis = AppDataContainer.TIMEOUT), + initialValue = CinemaUiState() + ) +} + +data class CinemaUiState(val cinemaWithSessions: CinemaWithSessions? = null) \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/entities/composeui/OrderList.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/OrderList.kt similarity index 74% rename from app/src/main/java/com/example/myapplication/entities/composeui/OrderList.kt rename to app/src/main/java/com/example/myapplication/database/entities/composeui/OrderList.kt index e7f92fd..c0c4ef4 100644 --- a/app/src/main/java/com/example/myapplication/entities/composeui/OrderList.kt +++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/OrderList.kt @@ -1,4 +1,4 @@ -package com.example.myapplication.entities.composeui +package com.example.myapplication.database.entities.composeui import android.content.res.Configuration import androidx.compose.foundation.background @@ -16,40 +16,32 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.mutableStateListOf -import androidx.compose.runtime.remember +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext 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.composeui.navigation.Screen -import com.example.myapplication.database.AppDatabase -import com.example.myapplication.entities.model.Order import com.example.myapplication.ui.theme.PmudemoTheme -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext @Composable -fun OrderList(navController: NavController?, userId: Int?) { - val context = LocalContext.current - val orders = remember { mutableStateListOf() } - LaunchedEffect(Unit) { - withContext(Dispatchers.IO) { - AppDatabase.getInstance(context).orderDao().getAll(userId).collect { data -> - orders.clear() - orders.addAll(data) - } - } - } +fun OrderList( + navController: NavController?, + userId: Int?, + viewModel: OrderListViewModel = viewModel(factory = AppViewModelProvider.Factory) +) { + val coroutineScope = rememberCoroutineScope() + val ordersUiState by viewModel.orderListUiState.collectAsState() LazyColumn( modifier = Modifier .fillMaxSize() .padding(all = 10.dp) ) { - items(orders) { order -> + items(ordersUiState.orderList) { order -> val orderId = Screen.OrderView.route.replace("{id}", order.uid.toString()) Box( modifier = Modifier diff --git a/app/src/main/java/com/example/myapplication/database/entities/composeui/OrderListViewModel.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/OrderListViewModel.kt new file mode 100644 index 0000000..a67bdec --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/OrderListViewModel.kt @@ -0,0 +1,25 @@ +package com.example.myapplication.database.entities.composeui + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.example.myapplication.database.AppDataContainer +import com.example.myapplication.database.entities.model.Order +import com.example.myapplication.database.entities.repository.OrderRepository +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn + +class OrderListViewModel( + private val orderRepository: OrderRepository +) : ViewModel() { + val orderListUiState: StateFlow = orderRepository.getAllOrders(1).map { + OrderListUiState(it) + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(stopTimeoutMillis = AppDataContainer.TIMEOUT), + initialValue = OrderListUiState() + ) +} + +data class OrderListUiState(val orderList: List = listOf()) \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/entities/composeui/OrderView.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/OrderView.kt similarity index 83% rename from app/src/main/java/com/example/myapplication/entities/composeui/OrderView.kt rename to app/src/main/java/com/example/myapplication/database/entities/composeui/OrderView.kt index 101a067..edde1d4 100644 --- a/app/src/main/java/com/example/myapplication/entities/composeui/OrderView.kt +++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/OrderView.kt @@ -1,4 +1,4 @@ -package com.example.myapplication.entities.composeui +package com.example.myapplication.database.entities.composeui import android.content.res.Configuration import android.graphics.BitmapFactory @@ -18,42 +18,33 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.mutableStateListOf +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.platform.LocalContext import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import com.example.myapplication.database.AppDatabase -import com.example.myapplication.entities.model.SessionFromOrder +import androidx.lifecycle.viewmodel.compose.viewModel import com.example.myapplication.ui.theme.PmudemoTheme -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext import org.threeten.bp.format.DateTimeFormatter @Composable -fun OrderView(id: Int) { - val context = LocalContext.current - val sessions = remember { - mutableStateListOf() - } - LaunchedEffect(Unit) { - withContext(Dispatchers.IO) { - sessions.clear() - sessions.addAll(AppDatabase.getInstance(context).orderDao().getByUid(id)) - } - } - +fun OrderView( + id: Int, + viewModel: OrderViewModel = viewModel(factory = AppViewModelProvider.Factory) +) { + val coroutineScope = rememberCoroutineScope() + val orderUiState by viewModel.orderUiState.collectAsState() LazyColumn( modifier = Modifier .padding(10.dp) ) { - items(sessions) { session -> + items(orderUiState.sessionList) { session -> val count = remember { mutableStateOf(session.count) } val dateFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm") val formattedDate = dateFormatter.format(session.dateTime) diff --git a/app/src/main/java/com/example/myapplication/database/entities/composeui/OrderViewModel.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/OrderViewModel.kt new file mode 100644 index 0000000..b863d92 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/OrderViewModel.kt @@ -0,0 +1,28 @@ +package com.example.myapplication.database.entities.composeui + +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.example.myapplication.database.AppDataContainer +import com.example.myapplication.database.entities.model.SessionFromOrder +import com.example.myapplication.database.entities.repository.OrderRepository +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn + +class OrderViewModel( + savedStateHandle: SavedStateHandle, + private val orderRepository: OrderRepository +) : ViewModel() { + private val orderUid: Int = checkNotNull(savedStateHandle["id"]) + val orderUiState: StateFlow = orderRepository.getOrder(orderUid).map { + OrderUiState(it) + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(stopTimeoutMillis = AppDataContainer.TIMEOUT), + initialValue = OrderUiState() + ) +} + +data class OrderUiState(val sessionList: List = listOf()) \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/composeui/SessionList.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/SessionList.kt new file mode 100644 index 0000000..10ff825 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/SessionList.kt @@ -0,0 +1,141 @@ +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.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.Delete +import androidx.compose.material.icons.filled.ShoppingCart +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.rememberCoroutineScope +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 com.example.myapplication.database.entities.model.CinemaWithSessions +import kotlinx.coroutines.launch +import org.threeten.bp.format.DateTimeFormatter + +@Composable +fun SessionList( + cinemaWithSessions: CinemaWithSessions, + navController: NavController, + viewModel: SessionListViewModel = viewModel(factory = AppViewModelProvider.Factory) +) { + val coroutineScope = rememberCoroutineScope() + LazyColumn { + if (cinemaWithSessions.sessions.isEmpty()) { + item { + Text( + text = stringResource(R.string.Session_empty_description), + textAlign = TextAlign.Center, + style = MaterialTheme.typography.titleLarge + ) + } + } else { + items(cinemaWithSessions.sessions, key = { it.uid }) { session -> + val route = Screen.SessionEdit.route.replace( + "{id}", session.uid.toString() + ).replace( + "{cinemaId}", cinemaWithSessions.cinema.uid.toString() + ) + val dateFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm") + val formattedDate = dateFormatter.format(session.dateTime) + Column { + Text( + text = formattedDate, + color = MaterialTheme.colorScheme.onBackground, + ) + Box(modifier = Modifier + .padding(vertical = 7.dp) + .clickable { + navController.navigate(route) + } + .background( + color = MaterialTheme.colorScheme.secondary, + shape = RoundedCornerShape(16.dp) + )) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(8.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + if (cinemaWithSessions.cinema.image != null) Image( + bitmap = BitmapFactory.decodeByteArray( + cinemaWithSessions.cinema.image, + 0, + cinemaWithSessions.cinema.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 = "Цена: ${session.price}\n" + "Билетов: ${session.availableCount}", + color = MaterialTheme.colorScheme.onSecondary + ) + } + + IconButton( + onClick = { + coroutineScope.launch { + viewModel.addSessionInCart(sessionId = session.uid) + } + }, + ) { + Icon( + imageVector = Icons.Filled.ShoppingCart, + contentDescription = null, + modifier = Modifier.size(24.dp), + tint = MaterialTheme.colorScheme.onSecondary + ) + } + IconButton( + onClick = { + coroutineScope.launch { + viewModel.deleteSession(session = session) + } + }, + ) { + Icon( + imageVector = Icons.Default.Delete, + contentDescription = null, + modifier = Modifier.size(24.dp), + tint = MaterialTheme.colorScheme.onSecondary + ) + } + } + } + } + } + } + } +} diff --git a/app/src/main/java/com/example/myapplication/database/entities/composeui/SessionListViewModel.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/SessionListViewModel.kt new file mode 100644 index 0000000..a49dda4 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/SessionListViewModel.kt @@ -0,0 +1,33 @@ +package com.example.myapplication.database.entities.composeui + +import androidx.lifecycle.ViewModel +import com.example.myapplication.database.entities.model.Session +import com.example.myapplication.database.entities.model.SessionFromCinema +import com.example.myapplication.database.entities.model.UserSessionCrossRef +import com.example.myapplication.database.entities.repository.SessionRepository +import com.example.myapplication.database.entities.repository.UserSessionRepository + +class SessionListViewModel( + private val sessionRepository: SessionRepository, + private val userSessionRepository: UserSessionRepository +) : ViewModel() { + suspend fun deleteSession(session: SessionFromCinema) { + sessionRepository.deleteSession( + Session( + uid = session.uid, + dateTime = session.dateTime, + price = session.price, + maxCount = 0, + cinemaId = 0 + ) + ) + } + + suspend fun addSessionInCart(sessionId: Int, count: Int = 1) { + try { + userSessionRepository.insertUserSession(UserSessionCrossRef(1, sessionId, count)) + } catch (_: Exception) { + + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/entities/composeui/UserProfile.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/UserProfile.kt similarity index 98% rename from app/src/main/java/com/example/myapplication/entities/composeui/UserProfile.kt rename to app/src/main/java/com/example/myapplication/database/entities/composeui/UserProfile.kt index 75915c3..5fc6a0b 100644 --- a/app/src/main/java/com/example/myapplication/entities/composeui/UserProfile.kt +++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/UserProfile.kt @@ -1,4 +1,4 @@ -package com.example.myapplication.user.composeui +package com.example.myapplication.database.entities.composeui import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -28,7 +28,6 @@ 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.navigation.NavController import com.example.myapplication.datastore.DataStoreManager import com.example.myapplication.datastore.SettingData import kotlinx.coroutines.launch diff --git a/app/src/main/java/com/example/myapplication/database/entities/composeui/edit/CinemaEdit.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/edit/CinemaEdit.kt new file mode 100644 index 0000000..4f0d3d9 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/edit/CinemaEdit.kt @@ -0,0 +1,127 @@ +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.foundation.text.KeyboardOptions +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.text.input.KeyboardType +import androidx.compose.ui.text.input.VisualTransformation +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 CinemaEdit( + navController: NavController, + viewModel: CinemaEditViewModel = viewModel(factory = AppViewModelProvider.Factory), +) { + val coroutineScope = rememberCoroutineScope() + + CinemaEdit( + cinemaUiState = viewModel.cinemaUiState, + onClick = { + coroutineScope.launch { + viewModel.saveCinema() + navController.popBackStack() + } + }, + onUpdate = viewModel::updateUiState, + ) +} + +@Composable +private fun CinemaEdit( + cinemaUiState: CinemaUiState, + onClick: () -> Unit, + onUpdate: (CinemaDetails) -> Unit, +) { + Column( + Modifier + .fillMaxWidth() + .padding(all = 10.dp) + ) { + OutlinedTextField( + modifier = Modifier.fillMaxWidth(), + value = cinemaUiState.cinemaDetails.name, + onValueChange = { onUpdate(cinemaUiState.cinemaDetails.copy(name = it)) }, + label = { Text(stringResource(id = R.string.Cinema_name)) }, + singleLine = true + ) + OutlinedTextField( + modifier = Modifier.fillMaxWidth(), + value = cinemaUiState.cinemaDetails.description, + onValueChange = { onUpdate(cinemaUiState.cinemaDetails.copy(description = it)) }, + label = { Text(stringResource(id = R.string.Cinema_description)) }, + singleLine = true + ) + OutlinedTextField( + modifier = Modifier.fillMaxWidth(), + value = cinemaUiState.cinemaDetails.year.toString(), + onValueChange = { newValue -> + val parsedYear = newValue.toLongOrNull() ?: 0L + onUpdate(cinemaUiState.cinemaDetails.copy(year = parsedYear)) + }, + label = { Text(stringResource(id = R.string.Cinema_year)) }, + singleLine = true, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), + visualTransformation = VisualTransformation.None // Отключает маскировку + ) + if (cinemaUiState.cinemaDetails.image != null) + ImageUploader( + bitmap = BitmapFactory.decodeByteArray( + cinemaUiState.cinemaDetails.image, + 0, + cinemaUiState.cinemaDetails.image.size + ), + onResult = { byteArray: ByteArray? -> + onUpdate( + cinemaUiState.cinemaDetails.copy( + image = byteArray + ) + ) + } + ) + Button( + onClick = onClick, + enabled = cinemaUiState.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 CinemaEditPreview() { + PmudemoTheme { + Surface( + color = MaterialTheme.colorScheme.background + ) { + CinemaEdit( + cinemaUiState = CinemaUiState(), + onClick = {}, + onUpdate = {}, + ) + } + } +} diff --git a/app/src/main/java/com/example/myapplication/database/entities/composeui/edit/CinemaEditViewModel.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/edit/CinemaEditViewModel.kt new file mode 100644 index 0000000..0a4a872 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/edit/CinemaEditViewModel.kt @@ -0,0 +1,101 @@ +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.Cinema +import com.example.myapplication.database.entities.model.CinemaWithSessions +import com.example.myapplication.database.entities.model.SessionFromCinema +import com.example.myapplication.database.entities.repository.CinemaRepository +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch + +class CinemaEditViewModel( + savedStateHandle: SavedStateHandle, + private val cinemaRepository: CinemaRepository +) : ViewModel() { + var cinemaUiState by mutableStateOf(CinemaUiState()) + private set + + private val cinemaUid: Int = checkNotNull(savedStateHandle["id"]) + + init { + viewModelScope.launch { + if (cinemaUid > 0) { + cinemaUiState = cinemaRepository.getCinema(cinemaUid) + .filterNotNull() + .first() + .toUiState(true) + } + } + } + + fun updateUiState(cinemaDetails: CinemaDetails) { + cinemaUiState = CinemaUiState( + cinemaDetails = cinemaDetails, + isEntryValid = validateInput(cinemaDetails) + ) + } + + suspend fun saveCinema() { + if (validateInput()) { + if (cinemaUid > 0) { + cinemaRepository.updateCinema(cinemaUiState.cinemaDetails.toCinema(cinemaUid)) + } else { + cinemaRepository.insertCinema(cinemaUiState.cinemaDetails.toCinema()) + } + } + } + + private fun validateInput(uiState: CinemaDetails = cinemaUiState.cinemaDetails): Boolean { + return with(uiState) { + name.isNotBlank() + && description.isNotBlank() + && year >= 1900 + && year <= 2100 + } + } +} + +data class CinemaUiState( + val cinemaDetails: CinemaDetails = CinemaDetails(), + val isEntryValid: Boolean = false +) + +data class CinemaDetails( + val name: String = "", + val description: String = "", + val image: ByteArray? = byteArrayOf(), + val year: Long = 1900, + val sessions: List = emptyList() +) + +fun CinemaDetails.toCinema(uid: Int = 0): Cinema = Cinema( + uid = uid, + name = name, + description = description, + image = image, + year = year +) + +fun CinemaWithSessions.toDetails(): CinemaDetails { + val cinema = this.cinema + val sessions = this.sessions + + return CinemaDetails( + name = cinema.name, + description = cinema.description, + image = cinema.image, + year = cinema.year, + sessions = sessions + ) +} + +fun CinemaWithSessions.toUiState(isEntryValid: Boolean = false): CinemaUiState = CinemaUiState( + cinemaDetails = 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/SessionEdit.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/edit/SessionEdit.kt new file mode 100644 index 0000000..ffd808b --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/edit/SessionEdit.kt @@ -0,0 +1,150 @@ +package com.example.myapplication.database.entities.composeui.edit + +import androidx.compose.foundation.layout.Box +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.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.TimePicker +import androidx.compose.material3.rememberDatePickerState +import androidx.compose.material3.rememberTimePickerState +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +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 SessionEdit( + navController: NavController, + viewModel: SessionEditViewModel = viewModel(factory = AppViewModelProvider.Factory), +) { + val coroutineScope = rememberCoroutineScope() + SessionEdit( + sessionUiState = viewModel.sessionUiState, + onClick = { + coroutineScope.launch { + viewModel.saveSession() + 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 SessionEdit( + sessionUiState: SessionUiState, + onClick: () -> Unit, + onUpdate: (SessionDetails) -> Unit, +) { + LazyColumn( + Modifier + .fillMaxWidth() + .padding(all = 10.dp) + ) { + item { + if (sessionUiState.sessionDetails.dateTime != LocalDateTime.MIN) { + val selectedDateMillis = + sessionUiState.sessionDetails.dateTime.toInstant(ZoneOffset.UTC).toEpochMilli() + + val dateState = rememberDatePickerState( + initialDisplayMode = DisplayMode.Input, + initialSelectedDateMillis = selectedDateMillis + ) + val timeState = rememberTimePickerState( + sessionUiState.sessionDetails.dateTime.hour, + sessionUiState.sessionDetails.dateTime.minute + ) + DatePicker( + state = dateState, + modifier = Modifier.padding(16.dp) + ) + Box( + modifier = Modifier.fillMaxWidth(), + contentAlignment = Alignment.Center + ) { + TimePicker(state = timeState) + } + val selectedDate = dateState.selectedDateMillis?.toLocalDate() + val selectedTime = LocalTime.of(timeState.hour, timeState.minute) + if (selectedDate != null) { + val resultDateTime = LocalDateTime.of(selectedDate, selectedTime) + onUpdate(sessionUiState.sessionDetails.copy(dateTime = resultDateTime)) + } + } else { + val dateState = rememberDatePickerState(initialDisplayMode = DisplayMode.Input) + val timeState = rememberTimePickerState() + DatePicker( + state = dateState, + modifier = Modifier.padding(16.dp) + ) + Box( + modifier = Modifier.fillMaxWidth(), + contentAlignment = Alignment.Center + ) { + TimePicker(state = timeState) + } + val selectedDate = dateState.selectedDateMillis?.toLocalDate() + val selectedTime = LocalTime.of(timeState.hour, timeState.minute) + if (selectedDate != null) { + val resultDateTime = LocalDateTime.of(selectedDate, selectedTime) + onUpdate(sessionUiState.sessionDetails.copy(dateTime = resultDateTime)) + } + } + + OutlinedTextField( + modifier = Modifier.fillMaxWidth(), + value = sessionUiState.sessionDetails.price, + label = { Text(text = "Цена") }, + onValueChange = { + onUpdate(sessionUiState.sessionDetails.copy(price = it)) + }, + keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number), + ) + OutlinedTextField( + modifier = Modifier.fillMaxWidth(), + value = sessionUiState.sessionDetails.maxCount.toString(), + onValueChange = { newValue -> + val parsedMaxCount = newValue.toIntOrNull() ?: 0 // Преобразование в Int + onUpdate(sessionUiState.sessionDetails.copy(maxCount = parsedMaxCount)) + }, + label = { Text(text = "Количество") }, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number) + ) + Button( + onClick = onClick, + enabled = sessionUiState.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/SessionEditViewModel.kt b/app/src/main/java/com/example/myapplication/database/entities/composeui/edit/SessionEditViewModel.kt new file mode 100644 index 0000000..a93a444 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/composeui/edit/SessionEditViewModel.kt @@ -0,0 +1,110 @@ +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.Session +import com.example.myapplication.database.entities.repository.SessionRepository +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch +import org.threeten.bp.LocalDateTime + +class SessionEditViewModel( + savedStateHandle: SavedStateHandle, + private val sessionRepository: SessionRepository +) : ViewModel() { + var sessionUiState by mutableStateOf(SessionUiState()) + private set + + private val sessionUid: Int = checkNotNull(savedStateHandle["id"]) + private val cinemaUid: Int = checkNotNull(savedStateHandle["cinemaId"]) + + init { + viewModelScope.launch { + if (sessionUid > 0) { + sessionUiState = sessionRepository.getSession(sessionUid) + .filterNotNull() + .first() + .toUiState(true) + } + } + } + + fun updateUiState(sessionDetails: SessionDetails) { + sessionUiState = SessionUiState( + sessionDetails = sessionDetails, + isEntryValid = validateInput(sessionDetails) + ) + } + + suspend fun saveSession() { + if (validateInput()) { + if (cinemaUid > 0) + if (sessionUid > 0) { + sessionRepository.updateSession( + sessionUiState.sessionDetails + .toSession(uid = sessionUid, cinemaUid = cinemaUid) + ) + } else { + sessionRepository.insertSession( + sessionUiState.sessionDetails.toSession( + cinemaUid = cinemaUid + ) + ) + } + } + } + + private fun validateInput(uiState: SessionDetails = sessionUiState.sessionDetails): Boolean { + return with(uiState) { + dateTime != LocalDateTime.MIN + && isValidDouble(price) + && maxCount > 0 + && cinemaUid > 0 + } + } +} + +val regex = """^-?\d+(.\d+)?+(,\d+)?$""".toRegex() + +fun isValidDouble(input: String): Boolean { + return regex.matches(input) +} + +data class SessionUiState( + val sessionDetails: SessionDetails = SessionDetails(), + val isEntryValid: Boolean = false +) + +data class SessionDetails( + val uid: Int = 0, + val dateTime: LocalDateTime = LocalDateTime.MIN, + val price: String = "0", + val maxCount: Int = 0, + val cinemaId: Int = 0 +) + +fun SessionDetails.toSession(uid: Int = 0, cinemaUid: Int = 0): Session = Session( + uid = uid, + dateTime = dateTime, + price = price.toDoubleOrNull() ?: 0.0, + maxCount = maxCount, + cinemaId = cinemaUid +) + +fun Session.toDetails(): SessionDetails = SessionDetails( + uid = uid, + dateTime = dateTime, + price = price.toString(), + maxCount = maxCount, + cinemaId = cinemaId +) + +fun Session.toUiState(isEntryValid: Boolean = false): SessionUiState = SessionUiState( + sessionDetails = this.toDetails(), + isEntryValid = isEntryValid +) \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/dao/CinemaDao.kt b/app/src/main/java/com/example/myapplication/database/entities/dao/CinemaDao.kt new file mode 100644 index 0000000..74c4d32 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/dao/CinemaDao.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.Cinema +import com.example.myapplication.database.entities.model.SessionFromCinema +import kotlinx.coroutines.flow.Flow + +@Dao +interface CinemaDao { + @Query("select * from cinemas order by name") + fun getAll(): Flow> + + @Query("select * from cinemas order by name") + fun getAllCinemasPaged(): PagingSource + + @Query( + "SELECT c.*, s.uid as session_uid, s.date_time, s.price, s.max_count-IFNULL(SUM(os.count), 0) as available_count " + + "FROM cinemas AS c " + + "LEFT JOIN sessions AS s ON s.cinema_id = c.uid " + + "LEFT JOIN orders_sessions AS os ON os.session_id = s.uid " + + "WHERE c.uid = :cinemaId " + + "GROUP BY session_uid" + ) + fun getByUid(cinemaId: Int?): Flow>> + + @Insert + suspend fun insert(cinema: Cinema) + + @Update + suspend fun update(cinema: Cinema) + + @Delete + suspend fun delete(cinema: Cinema) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/dao/OrderDao.kt b/app/src/main/java/com/example/myapplication/database/entities/dao/OrderDao.kt new file mode 100644 index 0000000..d718553 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/dao/OrderDao.kt @@ -0,0 +1,34 @@ +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.Order +import com.example.myapplication.database.entities.model.SessionFromOrder +import kotlinx.coroutines.flow.Flow + +@Dao +interface OrderDao { + @Query("select * from orders where user_id = :userId") + fun getAll(userId: Int?): Flow> + + @Query( + "SELECT o.*, s.*, os.count, os.frozen_price " + + "FROM orders AS o " + + "JOIN orders_sessions AS os ON os.order_id = o.uid " + + "JOIN sessions AS s ON s.uid = os.session_id " + + "WHERE o.uid = :orderId" + ) + fun getByUid(orderId: Int?): Flow> + + @Insert + suspend fun insert(order: Order): Long + + @Update + suspend fun update(order: Order) + + @Delete + suspend fun delete(order: Order) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/entities/dao/OrderSessionCrossRefDao.kt b/app/src/main/java/com/example/myapplication/database/entities/dao/OrderSessionCrossRefDao.kt similarity index 73% rename from app/src/main/java/com/example/myapplication/entities/dao/OrderSessionCrossRefDao.kt rename to app/src/main/java/com/example/myapplication/database/entities/dao/OrderSessionCrossRefDao.kt index 9fbfa0b..b2783a0 100644 --- a/app/src/main/java/com/example/myapplication/entities/dao/OrderSessionCrossRefDao.kt +++ b/app/src/main/java/com/example/myapplication/database/entities/dao/OrderSessionCrossRefDao.kt @@ -1,11 +1,10 @@ -package com.example.myapplication.entities.dao +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.entities.model.OrderSessionCrossRef +import com.example.myapplication.database.entities.model.OrderSessionCrossRef @Dao interface OrderSessionCrossRefDao { diff --git a/app/src/main/java/com/example/myapplication/entities/dao/SessionDao.kt b/app/src/main/java/com/example/myapplication/database/entities/dao/SessionDao.kt similarity index 50% rename from app/src/main/java/com/example/myapplication/entities/dao/SessionDao.kt rename to app/src/main/java/com/example/myapplication/database/entities/dao/SessionDao.kt index 35c966f..55b3f55 100644 --- a/app/src/main/java/com/example/myapplication/entities/dao/SessionDao.kt +++ b/app/src/main/java/com/example/myapplication/database/entities/dao/SessionDao.kt @@ -1,13 +1,18 @@ -package com.example.myapplication.entities.dao +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.entities.model.Session +import com.example.myapplication.database.entities.model.Session +import kotlinx.coroutines.flow.Flow @Dao interface SessionDao { + @Query("select * from sessions where sessions.uid = :uid") + fun getByUid(uid: Int): Flow + @Insert suspend fun insert(session: Session) diff --git a/app/src/main/java/com/example/myapplication/entities/dao/UserDao.kt b/app/src/main/java/com/example/myapplication/database/entities/dao/UserDao.kt similarity index 61% rename from app/src/main/java/com/example/myapplication/entities/dao/UserDao.kt rename to app/src/main/java/com/example/myapplication/database/entities/dao/UserDao.kt index e54c5c0..5f973bc 100644 --- a/app/src/main/java/com/example/myapplication/entities/dao/UserDao.kt +++ b/app/src/main/java/com/example/myapplication/database/entities/dao/UserDao.kt @@ -1,12 +1,12 @@ -package com.example.myapplication.entities.dao +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.entities.model.SessionFromCart -import com.example.myapplication.entities.model.User +import com.example.myapplication.database.entities.model.SessionFromCart +import com.example.myapplication.database.entities.model.User import kotlinx.coroutines.flow.Flow @Dao @@ -15,12 +15,12 @@ interface UserDao { fun getAll(): Flow> @Query( - "SELECT sessions.*, sessions.max_count-sum(orders_sessions.count) as available_count, " + + "SELECT sessions.*, sessions.max_count-IFNULL(SUM(orders_sessions.count), 0) as available_count, " + "users_sessions.count FROM sessions " + "join users_sessions on sessions.uid = users_sessions.session_id " + - "join orders_sessions on sessions.uid = orders_sessions.session_id " + + "left join orders_sessions on sessions.uid = orders_sessions.session_id " + "where users_sessions.user_id = :userId " + - "GROUP BY orders_sessions.session_id " + "GROUP BY users_sessions.session_id " ) fun getCartByUid(userId: Int): Flow> diff --git a/app/src/main/java/com/example/myapplication/entities/dao/UserSessionCrossRefDao.kt b/app/src/main/java/com/example/myapplication/database/entities/dao/UserSessionCrossRefDao.kt similarity index 61% rename from app/src/main/java/com/example/myapplication/entities/dao/UserSessionCrossRefDao.kt rename to app/src/main/java/com/example/myapplication/database/entities/dao/UserSessionCrossRefDao.kt index a714db2..7ec39e5 100644 --- a/app/src/main/java/com/example/myapplication/entities/dao/UserSessionCrossRefDao.kt +++ b/app/src/main/java/com/example/myapplication/database/entities/dao/UserSessionCrossRefDao.kt @@ -1,13 +1,11 @@ -package com.example.myapplication.entities.dao +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.entities.model.SessionFromCart -import com.example.myapplication.entities.model.UserSessionCrossRef -import kotlinx.coroutines.flow.Flow +import com.example.myapplication.database.entities.model.UserSessionCrossRef @Dao interface UserSessionCrossRefDao { @@ -19,4 +17,7 @@ interface UserSessionCrossRefDao { @Delete suspend fun delete(userSessionCrossRef: UserSessionCrossRef) + + @Query("DELETE FROM users_sessions where users_sessions.user_id = :userId") + suspend fun deleteByUserUid(userId: Int) } \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/entities/model/Cinema.kt b/app/src/main/java/com/example/myapplication/database/entities/model/Cinema.kt similarity index 51% rename from app/src/main/java/com/example/myapplication/entities/model/Cinema.kt rename to app/src/main/java/com/example/myapplication/database/entities/model/Cinema.kt index 5eefdf1..d1c8fae 100644 --- a/app/src/main/java/com/example/myapplication/entities/model/Cinema.kt +++ b/app/src/main/java/com/example/myapplication/database/entities/model/Cinema.kt @@ -1,4 +1,4 @@ -package com.example.myapplication.entities.model +package com.example.myapplication.database.entities.model import androidx.room.ColumnInfo import androidx.room.Entity @@ -8,7 +8,7 @@ import androidx.room.PrimaryKey @Entity(tableName = "cinemas") data class Cinema( @PrimaryKey(autoGenerate = true) - val uid: Int?, + val uid: Int = 0, val name: String, val description: String, @ColumnInfo(typeAffinity = ColumnInfo.BLOB) @@ -21,18 +21,37 @@ data class Cinema( description: String, image: ByteArray?, year: Long - ) : this(null, name, description, image, year) + ) : this(0, name, description, image, year) + + companion object { + fun getCinema(index: Int = 0): Cinema { + return Cinema( + index, + "name", + "desc", + byteArrayOf(), + 0, + ) + } + } override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false other as Cinema if (uid != other.uid) return false + if (name != other.name) return false + if (description != other.description) return false + if (year != other.year) return false return true } override fun hashCode(): Int { - return uid ?: -1 + var result = uid + result = 31 * result + name.hashCode() + result = 31 * result + description.hashCode() + result = 31 * result + year.hashCode() + return result } } diff --git a/app/src/main/java/com/example/myapplication/database/entities/model/CinemaWithSessions.kt b/app/src/main/java/com/example/myapplication/database/entities/model/CinemaWithSessions.kt new file mode 100644 index 0000000..5e118ae --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/model/CinemaWithSessions.kt @@ -0,0 +1,24 @@ +package com.example.myapplication.database.entities.model + +data class CinemaWithSessions( + val cinema: Cinema, + val sessions: List +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as CinemaWithSessions + + if (cinema != other.cinema) return false + if (sessions != other.sessions) return false + + return true + } + + override fun hashCode(): Int { + var result = cinema.hashCode() + result = 31 * result + sessions.hashCode() + return result + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/entities/model/LocalDateTimeConverter.kt b/app/src/main/java/com/example/myapplication/database/entities/model/LocalDateTimeConverter.kt similarity index 85% rename from app/src/main/java/com/example/myapplication/entities/model/LocalDateTimeConverter.kt rename to app/src/main/java/com/example/myapplication/database/entities/model/LocalDateTimeConverter.kt index dd0a24b..884748b 100644 --- a/app/src/main/java/com/example/myapplication/entities/model/LocalDateTimeConverter.kt +++ b/app/src/main/java/com/example/myapplication/database/entities/model/LocalDateTimeConverter.kt @@ -1,4 +1,4 @@ -package com.example.myapplication.entities.model +package com.example.myapplication.database.entities.model import androidx.room.TypeConverter import org.threeten.bp.LocalDateTime diff --git a/app/src/main/java/com/example/myapplication/entities/model/Order.kt b/app/src/main/java/com/example/myapplication/database/entities/model/Order.kt similarity index 93% rename from app/src/main/java/com/example/myapplication/entities/model/Order.kt rename to app/src/main/java/com/example/myapplication/database/entities/model/Order.kt index 49c30ec..9cb1c76 100644 --- a/app/src/main/java/com/example/myapplication/entities/model/Order.kt +++ b/app/src/main/java/com/example/myapplication/database/entities/model/Order.kt @@ -1,4 +1,4 @@ -package com.example.myapplication.entities.model +package com.example.myapplication.database.entities.model import androidx.room.ColumnInfo import androidx.room.Entity diff --git a/app/src/main/java/com/example/myapplication/entities/model/OrderSessionCrossRef.kt b/app/src/main/java/com/example/myapplication/database/entities/model/OrderSessionCrossRef.kt similarity index 93% rename from app/src/main/java/com/example/myapplication/entities/model/OrderSessionCrossRef.kt rename to app/src/main/java/com/example/myapplication/database/entities/model/OrderSessionCrossRef.kt index ba9cfef..98e8d17 100644 --- a/app/src/main/java/com/example/myapplication/entities/model/OrderSessionCrossRef.kt +++ b/app/src/main/java/com/example/myapplication/database/entities/model/OrderSessionCrossRef.kt @@ -1,4 +1,4 @@ -package com.example.myapplication.entities.model +package com.example.myapplication.database.entities.model import androidx.room.ColumnInfo import androidx.room.Entity diff --git a/app/src/main/java/com/example/myapplication/entities/model/Session.kt b/app/src/main/java/com/example/myapplication/database/entities/model/Session.kt similarity index 55% rename from app/src/main/java/com/example/myapplication/entities/model/Session.kt rename to app/src/main/java/com/example/myapplication/database/entities/model/Session.kt index 50850bd..120ba4d 100644 --- a/app/src/main/java/com/example/myapplication/entities/model/Session.kt +++ b/app/src/main/java/com/example/myapplication/database/entities/model/Session.kt @@ -1,4 +1,4 @@ -package com.example.myapplication.entities.model +package com.example.myapplication.database.entities.model import androidx.room.ColumnInfo import androidx.room.Entity @@ -13,21 +13,21 @@ import org.threeten.bp.LocalDateTime entity = Cinema::class, parentColumns = ["uid"], childColumns = ["cinema_id"], - onDelete = ForeignKey.RESTRICT, + onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.RESTRICT ) ] ) data class Session( @PrimaryKey(autoGenerate = true) - val uid: Int?, + val uid: Int = 0, @ColumnInfo(name = "date_time") val dateTime: LocalDateTime, val price: Double, @ColumnInfo(name = "max_count") val maxCount: Int, @ColumnInfo(name = "cinema_id", index = true) - val cinemaId: Int?, + val cinemaId: Int = 0, ) { @Ignore constructor( @@ -35,18 +35,39 @@ data class Session( price: Double, maxCount: Int, cinema: Cinema, - ) : this(null, dateTime, price, maxCount, cinema.uid) + ) : this(0, dateTime, price, maxCount, cinema.uid) + + companion object { + fun getSession(index: Int = 0): Session { + return Session( + 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 Session if (uid != other.uid) return false + if (dateTime != other.dateTime) return false + if (price != other.price) return false + if (maxCount != other.maxCount) return false + if (cinemaId != other.cinemaId) return false return true } override fun hashCode(): Int { - return uid ?: -1 + var result = uid + result = 31 * result + dateTime.hashCode() + result = 31 * result + price.hashCode() + result = 31 * result + maxCount.hashCode() + result = 31 * result + cinemaId.hashCode() + return result } } diff --git a/app/src/main/java/com/example/myapplication/entities/model/SessionFromCart.kt b/app/src/main/java/com/example/myapplication/database/entities/model/SessionFromCart.kt similarity index 78% rename from app/src/main/java/com/example/myapplication/entities/model/SessionFromCart.kt rename to app/src/main/java/com/example/myapplication/database/entities/model/SessionFromCart.kt index bdba5ac..65439c1 100644 --- a/app/src/main/java/com/example/myapplication/entities/model/SessionFromCart.kt +++ b/app/src/main/java/com/example/myapplication/database/entities/model/SessionFromCart.kt @@ -1,4 +1,4 @@ -package com.example.myapplication.entities.model +package com.example.myapplication.database.entities.model import androidx.room.ColumnInfo import androidx.room.Relation @@ -6,7 +6,7 @@ import org.threeten.bp.LocalDateTime data class SessionFromCart( @ColumnInfo(name = "uid") - val uid: Int?, + val uid: Int = 0, @ColumnInfo(name = "date_time") val dateTime: LocalDateTime, val price: Double, @@ -14,10 +14,10 @@ data class SessionFromCart( val availableCount: Int, val count: Int, @ColumnInfo(name = "cinema_id") - val cinemaId: Int?, + val cinemaId: Int = 0, @Relation( parentColumn = "cinema_id", entity = Cinema::class, entityColumn = "uid" - ) val cinema: Cinema + ) val cinema: Cinema ) \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/model/SessionFromCinema.kt b/app/src/main/java/com/example/myapplication/database/entities/model/SessionFromCinema.kt new file mode 100644 index 0000000..8098e98 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/model/SessionFromCinema.kt @@ -0,0 +1,35 @@ +package com.example.myapplication.database.entities.model + +import androidx.room.ColumnInfo +import org.threeten.bp.LocalDateTime +import org.threeten.bp.format.DateTimeFormatter + +data class SessionFromCinema( + @ColumnInfo(name = "session_uid") + val uid: Int, + @ColumnInfo(name = "date_time") + val dateTime: LocalDateTime, + val price: Double, + @ColumnInfo(name = "available_count") + val availableCount: Int, +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + other as SessionFromCinema + if (uid != other.uid) return false + val dateFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm") + if (dateFormatter.format(dateTime) != dateFormatter.format(other.dateTime)) return false + if (price != other.price) return false + if (availableCount != other.availableCount) return false + return true + } + + override fun hashCode(): Int { + var result = uid + result = 31 * result + dateTime.hashCode() + result = 31 * result + price.hashCode() + result = 31 * result + availableCount.hashCode() + return result + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/entities/model/SessionFromOrder.kt b/app/src/main/java/com/example/myapplication/database/entities/model/SessionFromOrder.kt similarity index 90% rename from app/src/main/java/com/example/myapplication/entities/model/SessionFromOrder.kt rename to app/src/main/java/com/example/myapplication/database/entities/model/SessionFromOrder.kt index 7ec3de3..0a13e21 100644 --- a/app/src/main/java/com/example/myapplication/entities/model/SessionFromOrder.kt +++ b/app/src/main/java/com/example/myapplication/database/entities/model/SessionFromOrder.kt @@ -1,4 +1,4 @@ -package com.example.myapplication.entities.model +package com.example.myapplication.database.entities.model import androidx.room.ColumnInfo import androidx.room.Relation diff --git a/app/src/main/java/com/example/myapplication/entities/model/User.kt b/app/src/main/java/com/example/myapplication/database/entities/model/User.kt similarity index 86% rename from app/src/main/java/com/example/myapplication/entities/model/User.kt rename to app/src/main/java/com/example/myapplication/database/entities/model/User.kt index 9bd7291..4e66bf0 100644 --- a/app/src/main/java/com/example/myapplication/entities/model/User.kt +++ b/app/src/main/java/com/example/myapplication/database/entities/model/User.kt @@ -1,4 +1,4 @@ -package com.example.myapplication.entities.model +package com.example.myapplication.database.entities.model import androidx.room.Entity import androidx.room.PrimaryKey @@ -6,7 +6,7 @@ import androidx.room.PrimaryKey @Entity(tableName = "users") data class User( @PrimaryKey(autoGenerate = true) - val uid: Int?, + val uid: Int = 0, val login: String, val password: String ) { diff --git a/app/src/main/java/com/example/myapplication/database/entities/model/UserSessionCrossRef.kt b/app/src/main/java/com/example/myapplication/database/entities/model/UserSessionCrossRef.kt new file mode 100644 index 0000000..eee2027 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/model/UserSessionCrossRef.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_sessions", + primaryKeys = ["user_id", "session_id"] +) +data class UserSessionCrossRef( + @ColumnInfo(name = "user_id", index = true) + val userId: Int, + @ColumnInfo(name = "session_id", index = true) + val sessionId: 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 UserSessionCrossRef + if (userId == other.userId && sessionId == other.sessionId) { + return true + } + return false + } + + override fun hashCode(): Int { + return hash(userId, sessionId) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/repository/CinemaRepository.kt b/app/src/main/java/com/example/myapplication/database/entities/repository/CinemaRepository.kt new file mode 100644 index 0000000..f2a11e9 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/repository/CinemaRepository.kt @@ -0,0 +1,15 @@ +package com.example.myapplication.database.entities.repository + +import androidx.paging.PagingSource +import com.example.myapplication.database.entities.model.Cinema +import com.example.myapplication.database.entities.model.CinemaWithSessions +import kotlinx.coroutines.flow.Flow + +interface CinemaRepository { + fun getAllCinemas(): Flow> + fun getAllCinemasPaged(): PagingSource + fun getCinema(uid: Int): Flow + suspend fun insertCinema(cinema: Cinema) + suspend fun updateCinema(cinema: Cinema) + suspend fun deleteCinema(cinema: Cinema) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineCinemaRepository.kt b/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineCinemaRepository.kt new file mode 100644 index 0000000..3e1311d --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineCinemaRepository.kt @@ -0,0 +1,33 @@ +package com.example.myapplication.database.entities.repository + +import androidx.paging.PagingSource +import com.example.myapplication.database.entities.dao.CinemaDao +import com.example.myapplication.database.entities.model.Cinema +import com.example.myapplication.database.entities.model.CinemaWithSessions +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow + +class OfflineCinemaRepository(private val cinemaDao: CinemaDao) : CinemaRepository { + override fun getAllCinemas(): Flow> = cinemaDao.getAll() + + override fun getAllCinemasPaged(): PagingSource = cinemaDao.getAllCinemasPaged() + + override fun getCinema(uid: Int): Flow { + return flow { + cinemaDao.getByUid(uid).collect { + emit(it.firstNotNullOf { + CinemaWithSessions( + cinema = it.key, + sessions = it.value + ) + }) + } + } + } + + override suspend fun insertCinema(cinema: Cinema) = cinemaDao.insert(cinema) + + override suspend fun updateCinema(cinema: Cinema) = cinemaDao.update(cinema) + + override suspend fun deleteCinema(cinema: Cinema) = cinemaDao.delete(cinema) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineOrderRepository.kt b/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineOrderRepository.kt new file mode 100644 index 0000000..5efb9a0 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineOrderRepository.kt @@ -0,0 +1,18 @@ +package com.example.myapplication.database.entities.repository + +import com.example.myapplication.database.entities.dao.OrderDao +import com.example.myapplication.database.entities.model.Order +import com.example.myapplication.database.entities.model.SessionFromOrder +import kotlinx.coroutines.flow.Flow + +class OfflineOrderRepository(private val orderDao: OrderDao) : OrderRepository { + override fun getAllOrders(userId: Int?): Flow> = orderDao.getAll(userId) + + override fun getOrder(orderId: Int?): Flow> = orderDao.getByUid(orderId) + + override suspend fun insertOrder(order: Order): Long = orderDao.insert(order) + + override suspend fun updateOrder(order: Order) = orderDao.update(order) + + override suspend fun deleteOrder(order: Order) = orderDao.delete(order) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineOrderSessionRepository.kt b/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineOrderSessionRepository.kt new file mode 100644 index 0000000..8bdfcb9 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineOrderSessionRepository.kt @@ -0,0 +1,16 @@ +package com.example.myapplication.database.entities.repository + +import com.example.myapplication.database.entities.dao.OrderSessionCrossRefDao +import com.example.myapplication.database.entities.model.OrderSessionCrossRef + +class OfflineOrderSessionRepository(private val orderSessionDao: OrderSessionCrossRefDao) : + OrderSessionRepository { + override suspend fun insertOrderSession(orderSessionCrossRef: OrderSessionCrossRef) = + orderSessionDao.insert(orderSessionCrossRef) + + override suspend fun updateOrderSession(orderSessionCrossRef: OrderSessionCrossRef) = + orderSessionDao.update(orderSessionCrossRef) + + override suspend fun deleteOrderSession(orderSessionCrossRef: OrderSessionCrossRef) = + orderSessionDao.delete(orderSessionCrossRef) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineSessionRepository.kt b/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineSessionRepository.kt new file mode 100644 index 0000000..ffdf4b1 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineSessionRepository.kt @@ -0,0 +1,15 @@ +package com.example.myapplication.database.entities.repository + +import com.example.myapplication.database.entities.dao.SessionDao +import com.example.myapplication.database.entities.model.Session +import kotlinx.coroutines.flow.Flow + +class OfflineSessionRepository(private val sessionDao: SessionDao) : SessionRepository { + override fun getSession(uid: Int): Flow = sessionDao.getByUid(uid) + + override suspend fun insertSession(session: Session) = sessionDao.insert(session) + + override suspend fun updateSession(session: Session) = sessionDao.update(session) + + override suspend fun deleteSession(session: Session) = sessionDao.delete(session) +} \ 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..c3539a6 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineUserRepository.kt @@ -0,0 +1,19 @@ +package com.example.myapplication.database.entities.repository + +import com.example.myapplication.database.entities.dao.UserDao +import com.example.myapplication.database.entities.model.SessionFromCart +import com.example.myapplication.database.entities.model.User +import kotlinx.coroutines.flow.Flow + +class OfflineUserRepository(private val userDao: UserDao) : UserRepository { + override fun getAllUsers(): Flow> = userDao.getAll() + + override fun getCartByUser(userId: Int): Flow> = + 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) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineUserSessionRepository.kt b/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineUserSessionRepository.kt new file mode 100644 index 0000000..d104805 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/repository/OfflineUserSessionRepository.kt @@ -0,0 +1,18 @@ +package com.example.myapplication.database.entities.repository + +import com.example.myapplication.database.entities.dao.UserSessionCrossRefDao +import com.example.myapplication.database.entities.model.UserSessionCrossRef + +class OfflineUserSessionRepository(private val userSessionDao: UserSessionCrossRefDao) : + UserSessionRepository { + override suspend fun insertUserSession(userSessionCrossRef: UserSessionCrossRef) = + userSessionDao.insert(userSessionCrossRef) + + override suspend fun updateUserSession(userSessionCrossRef: UserSessionCrossRef) = + userSessionDao.update(userSessionCrossRef) + + override suspend fun deleteUserSession(userSessionCrossRef: UserSessionCrossRef) = + userSessionDao.delete(userSessionCrossRef) + + override suspend fun deleteUserSessions(userId: Int) = userSessionDao.deleteByUserUid(userId) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/repository/OrderRepository.kt b/app/src/main/java/com/example/myapplication/database/entities/repository/OrderRepository.kt new file mode 100644 index 0000000..0568a8a --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/repository/OrderRepository.kt @@ -0,0 +1,13 @@ +package com.example.myapplication.database.entities.repository + +import com.example.myapplication.database.entities.model.Order +import com.example.myapplication.database.entities.model.SessionFromOrder +import kotlinx.coroutines.flow.Flow + +interface OrderRepository { + fun getAllOrders(userId: Int?): Flow> + fun getOrder(orderId: Int?): Flow> + suspend fun insertOrder(order: Order): Long + suspend fun updateOrder(order: Order) + suspend fun deleteOrder(order: Order) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/repository/OrderSessionRepository.kt b/app/src/main/java/com/example/myapplication/database/entities/repository/OrderSessionRepository.kt new file mode 100644 index 0000000..5658c87 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/repository/OrderSessionRepository.kt @@ -0,0 +1,9 @@ +package com.example.myapplication.database.entities.repository + +import com.example.myapplication.database.entities.model.OrderSessionCrossRef + +interface OrderSessionRepository { + suspend fun insertOrderSession(orderSessionCrossRef: OrderSessionCrossRef) + suspend fun updateOrderSession(orderSessionCrossRef: OrderSessionCrossRef) + suspend fun deleteOrderSession(orderSessionCrossRef: OrderSessionCrossRef) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/database/entities/repository/SessionRepository.kt b/app/src/main/java/com/example/myapplication/database/entities/repository/SessionRepository.kt new file mode 100644 index 0000000..4ccd6fd --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/repository/SessionRepository.kt @@ -0,0 +1,11 @@ +package com.example.myapplication.database.entities.repository + +import com.example.myapplication.database.entities.model.Session +import kotlinx.coroutines.flow.Flow + +interface SessionRepository { + fun getSession(uid: Int): Flow + suspend fun insertSession(session: Session) + suspend fun updateSession(session: Session) + suspend fun deleteSession(session: Session) +} \ 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..caeb716 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/repository/UserRepository.kt @@ -0,0 +1,13 @@ +package com.example.myapplication.database.entities.repository + +import com.example.myapplication.database.entities.model.SessionFromCart +import com.example.myapplication.database.entities.model.User +import kotlinx.coroutines.flow.Flow + +interface UserRepository { + fun getAllUsers(): Flow> + fun getCartByUser(userId: Int): Flow> + 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/entities/repository/UserSessionRepository.kt b/app/src/main/java/com/example/myapplication/database/entities/repository/UserSessionRepository.kt new file mode 100644 index 0000000..c9a7334 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/database/entities/repository/UserSessionRepository.kt @@ -0,0 +1,10 @@ +package com.example.myapplication.database.entities.repository + +import com.example.myapplication.database.entities.model.UserSessionCrossRef + +interface UserSessionRepository { + suspend fun insertUserSession(userSessionCrossRef: UserSessionCrossRef) + suspend fun updateUserSession(userSessionCrossRef: UserSessionCrossRef) + suspend fun deleteUserSession(userSessionCrossRef: UserSessionCrossRef) + suspend fun deleteUserSessions(userId: Int) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/entities/composeui/CinemaList.kt b/app/src/main/java/com/example/myapplication/entities/composeui/CinemaList.kt deleted file mode 100644 index 6e37dae..0000000 --- a/app/src/main/java/com/example/myapplication/entities/composeui/CinemaList.kt +++ /dev/null @@ -1,109 +0,0 @@ -package com.example.myapplication.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.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.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.LaunchedEffect -import androidx.compose.runtime.mutableStateListOf -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.asImageBitmap -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.navigation.NavController -import com.example.myapplication.composeui.navigation.Screen -import com.example.myapplication.database.AppDatabase -import com.example.myapplication.entities.model.Cinema -import com.example.myapplication.ui.theme.PmudemoTheme -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext - -@Composable -fun CinemaList(navController: NavController?) { - val context = LocalContext.current - val cinemas = remember { mutableStateListOf() } - LaunchedEffect(Unit) { - withContext(Dispatchers.IO) { - AppDatabase.getInstance(context).cinemaDao().getAll().collect { data -> - cinemas.clear() - cinemas.addAll(data) - } - } - } - LazyColumn( - modifier = Modifier - .fillMaxSize() - .padding(all = 10.dp) - ) { - items(cinemas) { cinema -> - val cinemaId = Screen.CinemaView.route.replace("{id}", cinema.uid.toString()) - Box( - modifier = Modifier - .fillMaxWidth() - .padding(all = 10.dp) - .clickable { navController?.navigate(cinemaId) } - .background( - color = MaterialTheme.colorScheme.secondary, - shape = RoundedCornerShape(16.dp) - ) - ) { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(8.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(8.dp) - ) { - if (cinema.image != null) - Image( - bitmap = BitmapFactory.decodeByteArray( - cinema.image, - 0, - cinema.image.size - ).asImageBitmap(), - contentDescription = null, - modifier = Modifier - .size(90.dp) - .padding(4.dp) - ) - - Text( - "${cinema.name}, ${cinema.year}", - color = MaterialTheme.colorScheme.onSecondary - ) - } - } - } - } -} - -@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 CinemaListPreview() { - PmudemoTheme { - Surface( - color = MaterialTheme.colorScheme.background - ) { - CinemaList(navController = null) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/entities/composeui/CinemaView.kt b/app/src/main/java/com/example/myapplication/entities/composeui/CinemaView.kt deleted file mode 100644 index 52aa226..0000000 --- a/app/src/main/java/com/example/myapplication/entities/composeui/CinemaView.kt +++ /dev/null @@ -1,209 +0,0 @@ -package com.example.myapplication.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.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.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.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.ShoppingCart -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.mutableStateListOf -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.asImageBitmap -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.example.myapplication.database.AppDatabase -import com.example.myapplication.entities.model.Cinema -import com.example.myapplication.entities.model.SessionFromCinema -import com.example.myapplication.ui.theme.PmudemoTheme -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import org.threeten.bp.format.DateTimeFormatter - -@Composable -fun CinemaView(id: Int) { - val context = LocalContext.current - val cinemaWithSessions = remember { - mutableStateListOf>>() - } - LaunchedEffect(Unit) { - withContext(Dispatchers.IO) { - cinemaWithSessions.clear() - cinemaWithSessions - .addAll(AppDatabase.getInstance(context).cinemaDao().getByUid(id).map { (cinema, sessionFromCinema) -> - Pair(cinema, sessionFromCinema) - }) - } - } - val cinema = cinemaWithSessions.firstOrNull()?.first - val sessions = cinemaWithSessions.firstOrNull()?.second - - LazyColumn( - modifier = Modifier - .fillMaxSize() - .padding(16.dp) - ) { - item { - Box( - modifier = Modifier - .fillMaxSize() - .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) - ) { - Text( - text = "${cinema?.name ?: ""}, ${cinema?.year ?: 1930}", - style = TextStyle( - fontWeight = FontWeight.Bold, - fontSize = 18.sp, - color = MaterialTheme.colorScheme.onSecondary - ), - modifier = Modifier - .fillMaxWidth() - .padding(bottom = 8.dp) - ) - if (cinema?.image != null) - Image( - bitmap = BitmapFactory.decodeByteArray( - cinema.image, - 0, - cinema.image.size - ).asImageBitmap(), - contentDescription = null, - modifier = Modifier - .fillMaxWidth() - .height(200.dp) - .padding(4.dp) - ) - - Text( - text = cinema?.description ?: "", - color = MaterialTheme.colorScheme.onSecondary - ) - } - } - } - item { - Text( - text = "Сеансы", - style = TextStyle( - fontWeight = FontWeight.Bold, - fontSize = 18.sp, - color = MaterialTheme.colorScheme.onBackground - ), - modifier = Modifier - .fillMaxWidth() - .padding(top = 8.dp, bottom = 8.dp), - ) - } - - if (sessions != null) { - items(sessions) { session -> - val dateFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm") - val formattedDate = dateFormatter.format(session.dateTime) - Text( - text = formattedDate, - color = MaterialTheme.colorScheme.onBackground, - ) - Box( - modifier = Modifier - .fillMaxWidth() - .padding(10.dp) - .clip(RoundedCornerShape(16.dp)) - .background(MaterialTheme.colorScheme.secondary) - ) { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(8.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween - ) { - if (cinema?.image != null) - Image( - bitmap = BitmapFactory.decodeByteArray( - cinema.image, - 0, - cinema.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 = "Цена: ${session.price}\n" + - "Билетов: ${session.availableCount}", - color = MaterialTheme.colorScheme.onSecondary - ) - } - } - - Icon( - imageVector = Icons.Filled.ShoppingCart, - contentDescription = null, - modifier = Modifier - .padding(10.dp) - .size(24.dp) - .clickable {} - .align(Alignment.CenterEnd), - tint = MaterialTheme.colorScheme.onSecondary - ) - } - } - } - } -} - - -@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 CinemaViewPreview() { - PmudemoTheme { - Surface( - color = MaterialTheme.colorScheme.background - ) { - CinemaView(id = 0) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/entities/dao/CinemaDao.kt b/app/src/main/java/com/example/myapplication/entities/dao/CinemaDao.kt deleted file mode 100644 index 83029ea..0000000 --- a/app/src/main/java/com/example/myapplication/entities/dao/CinemaDao.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.example.myapplication.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.entities.model.Cinema -import com.example.myapplication.entities.model.SessionFromCinema -import kotlinx.coroutines.flow.Flow - -@Dao -interface CinemaDao { - @Query("select * from cinemas order by name") - fun getAll(): Flow> - - @Query("SELECT c.*, s.date_time, s.price, s.max_count-IFNULL(SUM(os.count), 0) as available_count " + - "FROM cinemas AS c " + - "JOIN sessions AS s ON s.cinema_id = c.uid " + - "LEFT JOIN orders_sessions AS os ON os.session_id = s.uid " + - "WHERE c.uid = :cinemaId " + - "GROUP BY os.session_id") - suspend fun getByUid(cinemaId: Int?): Map> - - @Insert - suspend fun insert(cinema: Cinema) - - @Update - suspend fun update(cinema: Cinema) - - @Delete - suspend fun delete(cinema: Cinema) -} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/entities/dao/OrderDao.kt b/app/src/main/java/com/example/myapplication/entities/dao/OrderDao.kt deleted file mode 100644 index 7d7802a..0000000 --- a/app/src/main/java/com/example/myapplication/entities/dao/OrderDao.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.example.myapplication.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.entities.model.Order -import com.example.myapplication.entities.model.SessionFromOrder -import kotlinx.coroutines.flow.Flow - -@Dao -interface OrderDao { - @Query("select * from orders where user_id = :userId") - fun getAll(userId: Int?): Flow> - - @Query("SELECT o.*, s.*, os.count, os.frozen_price " + - "FROM orders AS o " + - "JOIN orders_sessions AS os ON os.order_id = o.uid " + - "JOIN sessions AS s ON s.uid = os.session_id " + - "WHERE o.uid = :orderId") - fun getByUid(orderId: Int?): List - - @Insert - suspend fun insert(order: Order) - - @Update - suspend fun update(order: Order) - - @Delete - suspend fun delete(order: Order) -} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/entities/model/SessionFromCinema.kt b/app/src/main/java/com/example/myapplication/entities/model/SessionFromCinema.kt deleted file mode 100644 index 0078dc2..0000000 --- a/app/src/main/java/com/example/myapplication/entities/model/SessionFromCinema.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.example.myapplication.entities.model - -import androidx.room.ColumnInfo -import org.threeten.bp.LocalDateTime - -data class SessionFromCinema ( - @ColumnInfo(name = "uid") - val uid: Int?, - @ColumnInfo(name = "date_time") - val dateTime: LocalDateTime, - val price: Double, - @ColumnInfo(name = "available_count") - val availableCount: Int, -) \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/entities/model/UserSessionCrossRef.kt b/app/src/main/java/com/example/myapplication/entities/model/UserSessionCrossRef.kt deleted file mode 100644 index b17b1a4..0000000 --- a/app/src/main/java/com/example/myapplication/entities/model/UserSessionCrossRef.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.example.myapplication.entities.model - -import androidx.room.ColumnInfo -import androidx.room.Entity - -@Entity( - tableName = "users_sessions", - primaryKeys = ["user_id", "session_id"] -) -data class UserSessionCrossRef( - @ColumnInfo(name = "user_id", index = true) - val userId: Int, - @ColumnInfo(name = "session_id", index = true) - val sessionId: Int, - @ColumnInfo(name = "count") - val count: Int, -) \ No newline at end of file diff --git a/app/src/main/res/drawable/minus.xml b/app/src/main/res/drawable/minus.xml index 0ff475a..fdbe783 100644 --- a/app/src/main/res/drawable/minus.xml +++ b/app/src/main/res/drawable/minus.xml @@ -3,6 +3,7 @@ android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"> - + \ No newline at end of file diff --git a/app/src/main/res/drawable/photo.jpg b/app/src/main/res/drawable/photo.jpg index 9fb88cf01ba39e610afd4378d070e7263d9097e2..9f39ccfd5447786d4d0914545086d5637e1f6e6d 100644 GIT binary patch literal 62928 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 DtcxF{ diff --git a/app/src/main/res/drawable/ticket.xml b/app/src/main/res/drawable/ticket.xml deleted file mode 100644 index 9e5fe53..0000000 --- a/app/src/main/res/drawable/ticket.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2c381a8..e99636b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2,6 +2,7 @@ pmu-demo Фильмы Фильм + Сеанс Заказ Название Год @@ -11,4 +12,11 @@ Мои заказы Профиль Сеансы + Время + Сохранить + Записи о фильмах отсутствуют + Записи о сеансах отсутствуют + Фильм не указан + Размер загруженного изображения: %1$dx%2$d + Загрузите изображение \ No newline at end of file diff --git a/app/src/test/java/com/example/myapplication/ExampleUnitTest.kt b/app/src/test/java/com/example/myapplication/ExampleUnitTest.kt index e500fb8..f308018 100644 --- a/app/src/test/java/com/example/myapplication/ExampleUnitTest.kt +++ b/app/src/test/java/com/example/myapplication/ExampleUnitTest.kt @@ -1,9 +1,8 @@ package com.example.myapplication +import org.junit.Assert.assertEquals import org.junit.Test -import org.junit.Assert.* - /** * Example local unit test, which will execute on the development machine (host). *