lw3
This commit is contained in:
parent
370020819d
commit
f879990c39
41
.idea/inspectionProfiles/Project_Default.xml
Normal file
41
.idea/inspectionProfiles/Project_Default.xml
Normal file
@ -0,0 +1,41 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="1.8.10" />
|
||||
<option name="version" value="1.8.20" />
|
||||
</component>
|
||||
</project>
|
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
@ -1,6 +1,7 @@
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
id("com.google.devtools.ksp")
|
||||
}
|
||||
|
||||
android {
|
||||
@ -23,21 +24,24 @@ android {
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
jvmTarget = "17"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = "1.4.3"
|
||||
kotlinCompilerExtensionVersion = "1.4.5"
|
||||
}
|
||||
packaging {
|
||||
resources {
|
||||
@ -47,15 +51,30 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation("com.jakewharton.threetenabp:threetenabp:1.2.1")
|
||||
implementation("androidx.datastore:datastore-preferences:1.0.0")
|
||||
// Core
|
||||
implementation("androidx.core:core-ktx:1.9.0")
|
||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")
|
||||
implementation("androidx.activity:activity-compose:1.7.0")
|
||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
|
||||
|
||||
// UI
|
||||
implementation("androidx.activity:activity-compose:1.7.2")
|
||||
implementation(platform("androidx.compose:compose-bom:2023.03.00"))
|
||||
implementation("androidx.navigation:navigation-compose:2.6.0")
|
||||
implementation("androidx.compose.ui:ui")
|
||||
implementation("androidx.compose.ui:ui-graphics")
|
||||
implementation("androidx.compose.ui:ui-tooling-preview")
|
||||
implementation("androidx.compose.material3:material3")
|
||||
|
||||
// Room
|
||||
val room_version = "2.5.2"
|
||||
implementation("androidx.room:room-runtime:$room_version")
|
||||
annotationProcessor("androidx.room:room-compiler:$room_version")
|
||||
ksp("androidx.room:room-compiler:$room_version")
|
||||
implementation("androidx.room:room-ktx:$room_version")
|
||||
implementation("androidx.room:room-paging:$room_version")
|
||||
|
||||
// Tests
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
||||
|
BIN
app/src/images/photo.jpg
Normal file
BIN
app/src/images/photo.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
@ -0,0 +1,43 @@
|
||||
package com.example.myapplication
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.example.myapplication.composeui.navigation.MainNavbar
|
||||
import com.example.myapplication.datastore.DataStoreManager
|
||||
import com.example.myapplication.ui.theme.PmudemoTheme
|
||||
import com.jakewharton.threetenabp.AndroidThreeTen
|
||||
|
||||
class MainComposeActivity : ComponentActivity() {
|
||||
private val dataStoreManager = DataStoreManager(this)
|
||||
private val isDarkTheme = mutableStateOf(true)
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
application.deleteDatabase("pmy-db")
|
||||
AndroidThreeTen.init(this)
|
||||
setContent {
|
||||
PmudemoTheme(darkTheme = isDarkTheme.value) {
|
||||
LaunchedEffect(key1 = true) {
|
||||
dataStoreManager.getSettings().collect { setting ->
|
||||
isDarkTheme.value = setting.isDarkTheme
|
||||
}
|
||||
}
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
color = MaterialTheme.colorScheme.background
|
||||
) {
|
||||
MainNavbar(
|
||||
isDarkTheme = isDarkTheme,
|
||||
dataStoreManager = dataStoreManager
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
190
app/src/main/java/com/example/myapplication/composeui/Cart.kt
Normal file
190
app/src/main/java/com/example/myapplication/composeui/Cart.kt
Normal file
@ -0,0 +1,190 @@
|
||||
package com.example.myapplication.composeui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.BitmapFactory
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.example.myapplication.R
|
||||
import com.example.myapplication.database.AppDatabase
|
||||
import com.example.myapplication.entities.model.SessionFromCart
|
||||
import com.example.myapplication.ui.theme.PmudemoTheme
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.threeten.bp.format.DateTimeFormatter
|
||||
|
||||
@Composable
|
||||
fun Cart(id: Int) {
|
||||
val context = LocalContext.current
|
||||
val sessions = remember { mutableStateListOf<SessionFromCart>() }
|
||||
LaunchedEffect(Unit) {
|
||||
withContext(Dispatchers.IO) {
|
||||
AppDatabase.getInstance(context).userDao().getCartByUid(id).collect { data ->
|
||||
sessions.clear()
|
||||
sessions.addAll(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.padding(all = 10.dp)
|
||||
) {
|
||||
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,
|
||||
)
|
||||
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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Column {
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
Button(
|
||||
onClick = { },
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
.fillMaxWidth()
|
||||
) { Text("Купить") }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
fun CartPreview() {
|
||||
PmudemoTheme {
|
||||
Surface(
|
||||
color = MaterialTheme.colorScheme.background
|
||||
) {
|
||||
Cart(1)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,218 @@
|
||||
package com.example.myapplication.composeui.navigation
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Person
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.NavigationBar
|
||||
import androidx.compose.material3.NavigationBarItem
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavDestination
|
||||
import androidx.navigation.NavDestination.Companion.hierarchy
|
||||
import androidx.navigation.NavGraph.Companion.findStartDestination
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.NavType
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.navArgument
|
||||
import com.example.myapplication.composeui.Cart
|
||||
import com.example.myapplication.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(
|
||||
navController: NavHostController,
|
||||
currentScreen: Screen?
|
||||
) {
|
||||
var searchQuery by remember { mutableStateOf("") }
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(color = MaterialTheme.colorScheme.primary)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(10.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
if (
|
||||
navController.previousBackStackEntry != null
|
||||
&& (currentScreen == null || !currentScreen.showInBottomBar)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.ArrowBack,
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.size(30.dp)
|
||||
.clickable { navController.navigateUp() },
|
||||
tint = MaterialTheme.colorScheme.secondary
|
||||
)
|
||||
} else
|
||||
Icon(
|
||||
imageVector = Icons.Default.Person,
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.size(30.dp)
|
||||
.clickable { navController.navigate(Screen.UserProfile.route) },
|
||||
tint = MaterialTheme.colorScheme.secondary
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
|
||||
BasicTextField(
|
||||
value = searchQuery,
|
||||
onValueChange = { newValue -> searchQuery = newValue },
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.height(36.dp)
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.onPrimary,
|
||||
RoundedCornerShape(18.dp)
|
||||
)
|
||||
.padding(start = 13.dp, top = 8.dp),
|
||||
keyboardOptions = KeyboardOptions.Default.copy(
|
||||
imeAction = androidx.compose.ui.text.input.ImeAction.Search
|
||||
),
|
||||
keyboardActions = KeyboardActions(
|
||||
onSearch = { }
|
||||
)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
|
||||
Icon(
|
||||
imageVector = Icons.Default.Search,
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.size(30.dp)
|
||||
.clickable { },
|
||||
tint = MaterialTheme.colorScheme.secondary
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Navbar(
|
||||
navController: NavHostController,
|
||||
currentDestination: NavDestination?,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
NavigationBar(modifier = modifier, containerColor = MaterialTheme.colorScheme.primary) {
|
||||
Screen.bottomBarItems.forEach { screen ->
|
||||
NavigationBarItem(
|
||||
icon = {
|
||||
Icon(
|
||||
screen.icon,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.secondary
|
||||
)
|
||||
},
|
||||
label = { Text(stringResource(screen.resourceId)) },
|
||||
selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,
|
||||
onClick = {
|
||||
navController.navigate(screen.route) {
|
||||
popUpTo(navController.graph.findStartDestination().id) {
|
||||
saveState = true
|
||||
}
|
||||
launchSingleTop = true
|
||||
restoreState = true
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Navhost(
|
||||
navController: NavHostController,
|
||||
innerPadding: PaddingValues,
|
||||
isDarkTheme: MutableState<Boolean>,
|
||||
dataStore: DataStoreManager,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
NavHost(
|
||||
navController,
|
||||
startDestination = Screen.CinemaList.route,
|
||||
modifier.padding(innerPadding)
|
||||
) {
|
||||
composable(Screen.CinemaList.route) { CinemaList(navController) }
|
||||
composable(Screen.OrderList.route) { OrderList(navController, 1) }
|
||||
composable(Screen.Cart.route) { Cart(1) }
|
||||
composable(Screen.UserProfile.route) { UserProfile(isDarkTheme, dataStore) }
|
||||
composable(
|
||||
Screen.CinemaView.route,
|
||||
arguments = listOf(navArgument("id") { type = NavType.IntType })
|
||||
) { backStackEntry ->
|
||||
backStackEntry.arguments?.let { CinemaView(it.getInt("id")) }
|
||||
}
|
||||
composable(
|
||||
Screen.OrderView.route,
|
||||
arguments = listOf(navArgument("id") { type = NavType.IntType })
|
||||
) { backStackEntry ->
|
||||
backStackEntry.arguments?.let { OrderView(it.getInt("id")) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun MainNavbar(
|
||||
isDarkTheme: MutableState<Boolean>,
|
||||
dataStoreManager: DataStoreManager
|
||||
) {
|
||||
val navController = rememberNavController()
|
||||
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
||||
val currentDestination = navBackStackEntry?.destination
|
||||
val currentScreen = currentDestination?.route?.let { Screen.getItem(it) }
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
Topbar(navController, currentScreen)
|
||||
},
|
||||
bottomBar = {
|
||||
if (currentScreen == null || currentScreen.showInBottomBar) {
|
||||
Navbar(navController, currentDestination)
|
||||
}
|
||||
}
|
||||
) { innerPadding ->
|
||||
Navhost(navController, innerPadding, isDarkTheme, dataStoreManager)
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package com.example.myapplication.composeui.navigation
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Favorite
|
||||
import androidx.compose.material.icons.filled.Home
|
||||
import androidx.compose.material.icons.filled.List
|
||||
import androidx.compose.material.icons.filled.ShoppingCart
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import com.example.myapplication.R
|
||||
|
||||
enum class Screen(
|
||||
val route: String,
|
||||
@StringRes val resourceId: Int,
|
||||
val icon: ImageVector = Icons.Filled.Favorite,
|
||||
val showInBottomBar: Boolean = true
|
||||
) {
|
||||
CinemaList(
|
||||
"Cinema-list", R.string.Cinema_main_title, Icons.Filled.Home
|
||||
),
|
||||
SessionList(
|
||||
"Session-list", R.string.Sessions_title, showInBottomBar = false
|
||||
),
|
||||
Cart(
|
||||
"cart", R.string.Cart_title, Icons.Filled.ShoppingCart
|
||||
),
|
||||
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
|
||||
),
|
||||
UserProfile(
|
||||
"User-profile", R.string.Profile_title, showInBottomBar = false
|
||||
);
|
||||
|
||||
companion object {
|
||||
val bottomBarItems = listOf(
|
||||
CinemaList,
|
||||
Cart,
|
||||
OrderList
|
||||
)
|
||||
|
||||
fun getItem(route: String): Screen? {
|
||||
val findRoute = route.split("/").first()
|
||||
return values().find { value -> value.route.startsWith(findRoute) }
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,147 @@
|
||||
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 kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.threeten.bp.LocalDateTime
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
@Database(
|
||||
entities = [Cinema::class, Session::class, Order::class,
|
||||
OrderSessionCrossRef::class, User::class, UserSessionCrossRef::class],
|
||||
version = 1,
|
||||
exportSchema = false
|
||||
)
|
||||
@TypeConverters(LocalDateTimeConverter::class)
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
abstract fun cinemaDao(): CinemaDao
|
||||
abstract fun sessionDao(): SessionDao
|
||||
abstract fun orderDao(): OrderDao
|
||||
abstract fun orderSessionCrossRefDao(): OrderSessionCrossRefDao
|
||||
abstract fun userDao(): UserDao
|
||||
abstract fun userSessionCrossRefDao(): UserSessionCrossRefDao
|
||||
|
||||
companion object {
|
||||
private const val DB_NAME: String = "pmy-db"
|
||||
|
||||
@Volatile
|
||||
private var INSTANCE: AppDatabase? = null
|
||||
|
||||
private suspend fun populateDatabase() {
|
||||
INSTANCE?.let { database ->
|
||||
// Users
|
||||
val userDao = database.userDao()
|
||||
val user1 = User(1, "Login", "password")
|
||||
val user2 = User(2, "Login123", "password123")
|
||||
userDao.insert(user1)
|
||||
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)
|
||||
cinemaDao.insert(cinema1)
|
||||
cinemaDao.insert(cinema2)
|
||||
cinemaDao.insert(cinema3)
|
||||
// Orders
|
||||
val orderDao = database.orderDao()
|
||||
val order1 = Order(1, 1)
|
||||
val order2 = Order(2, 1)
|
||||
val order3 = Order(3, 1)
|
||||
val order4 = Order(4, 1)
|
||||
orderDao.insert(order1)
|
||||
orderDao.insert(order2)
|
||||
orderDao.insert(order3)
|
||||
orderDao.insert(order4)
|
||||
// Sessions
|
||||
val sessionDao = database.sessionDao()
|
||||
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)
|
||||
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)
|
||||
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()
|
||||
val userSessionCrossRef1 = UserSessionCrossRef(1, 1, 5)
|
||||
val userSessionCrossRef2 = UserSessionCrossRef(1, 3, 15)
|
||||
userSessionCrossRefDao.insert(userSessionCrossRef1)
|
||||
userSessionCrossRefDao.insert(userSessionCrossRef2)
|
||||
}
|
||||
}
|
||||
|
||||
fun getInstance(appContext: Context): AppDatabase {
|
||||
return INSTANCE ?: synchronized(this) {
|
||||
Room.databaseBuilder(
|
||||
appContext,
|
||||
AppDatabase::class.java,
|
||||
DB_NAME
|
||||
)
|
||||
.addCallback(object : Callback() {
|
||||
override fun onCreate(db: SupportSQLiteDatabase) {
|
||||
super.onCreate(db)
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
populateDatabase()
|
||||
}
|
||||
}
|
||||
})
|
||||
.build()
|
||||
.also { INSTANCE = it }
|
||||
}
|
||||
}
|
||||
|
||||
private fun createColoredImage(color: Int): ByteArray {
|
||||
val width = 100
|
||||
val height = 100
|
||||
|
||||
val bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
||||
bmp.eraseColor(color)
|
||||
|
||||
val stream = ByteArrayOutputStream()
|
||||
bmp.compress(Bitmap.CompressFormat.PNG, 100, stream)
|
||||
|
||||
return stream.toByteArray()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package com.example.myapplication.datastore
|
||||
|
||||
import android.content.Context
|
||||
import androidx.datastore.core.DataStore
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.core.booleanPreferencesKey
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.preferencesDataStore
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore("data_store")
|
||||
|
||||
class DataStoreManager(private val context: Context) {
|
||||
suspend fun saveSettings(settingData: SettingData) {
|
||||
context.dataStore.edit { pref ->
|
||||
pref[booleanPreferencesKey("isDarkTheme")] = settingData.isDarkTheme
|
||||
}
|
||||
}
|
||||
|
||||
fun getSettings() = context.dataStore.data.map { pref ->
|
||||
return@map SettingData(
|
||||
pref[booleanPreferencesKey("isDarkTheme")] ?: true
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package com.example.myapplication.datastore
|
||||
|
||||
data class SettingData(
|
||||
val isDarkTheme: Boolean
|
||||
)
|
@ -0,0 +1,109 @@
|
||||
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<Cinema>() }
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,209 @@
|
||||
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<Pair<Cinema, List<SessionFromCinema>>>()
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
package com.example.myapplication.entities.composeui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.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.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.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<Order>() }
|
||||
LaunchedEffect(Unit) {
|
||||
withContext(Dispatchers.IO) {
|
||||
AppDatabase.getInstance(context).orderDao().getAll(userId).collect { data ->
|
||||
orders.clear()
|
||||
orders.addAll(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(all = 10.dp)
|
||||
) {
|
||||
items(orders) { order ->
|
||||
val orderId = Screen.OrderView.route.replace("{id}", order.uid.toString())
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(all = 10.dp)
|
||||
.clickable { navController?.navigate(orderId) }
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
shape = RoundedCornerShape(16.dp)
|
||||
)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text("Заказ №${order.uid}", 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 OrderListPreview() {
|
||||
PmudemoTheme {
|
||||
Surface(
|
||||
color = MaterialTheme.colorScheme.background
|
||||
) {
|
||||
OrderList(navController = null, 1)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,123 @@
|
||||
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.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.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.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.example.myapplication.database.AppDatabase
|
||||
import com.example.myapplication.entities.model.SessionFromOrder
|
||||
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<SessionFromOrder>()
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
withContext(Dispatchers.IO) {
|
||||
sessions.clear()
|
||||
sessions.addAll(AppDatabase.getInstance(context).orderDao().getByUid(id))
|
||||
}
|
||||
}
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.padding(10.dp)
|
||||
) {
|
||||
items(sessions) { session ->
|
||||
val count = 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,
|
||||
)
|
||||
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)
|
||||
.padding(start = 8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "${session.cinema.name}, ${session.cinema.year}\n" +
|
||||
"Цена: ${session.frozenPrice}\n" +
|
||||
"Количество: ${count.value}",
|
||||
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 OrderViewPreview() {
|
||||
PmudemoTheme {
|
||||
Surface(
|
||||
color = MaterialTheme.colorScheme.background
|
||||
) {
|
||||
OrderView(id = 1)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,166 @@
|
||||
package com.example.myapplication.user.composeui
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.SwitchDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavController
|
||||
import com.example.myapplication.datastore.DataStoreManager
|
||||
import com.example.myapplication.datastore.SettingData
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun UserProfile(
|
||||
isDarkTheme: MutableState<Boolean>,
|
||||
dataStoreManager: DataStoreManager
|
||||
) {
|
||||
var username by remember { mutableStateOf("") }
|
||||
var password by remember { mutableStateOf("") }
|
||||
var isRegistration by remember { mutableStateOf(false) }
|
||||
|
||||
LazyColumn {
|
||||
item {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "Логин",
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally)
|
||||
)
|
||||
BasicTextField(
|
||||
value = username,
|
||||
onValueChange = { newValue -> username = newValue },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.size(36.dp)
|
||||
.background(MaterialTheme.colorScheme.secondary, RoundedCornerShape(18.dp))
|
||||
.padding(start = 13.dp, top = 8.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "Пароль",
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally)
|
||||
)
|
||||
BasicTextField(
|
||||
value = password,
|
||||
onValueChange = { newValue -> password = newValue },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.size(36.dp)
|
||||
.background(MaterialTheme.colorScheme.secondary, RoundedCornerShape(18.dp))
|
||||
.padding(start = 13.dp, top = 8.dp),
|
||||
visualTransformation = PasswordVisualTransformation()
|
||||
)
|
||||
|
||||
if (isRegistration) {
|
||||
Button(
|
||||
onClick = { },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp)
|
||||
) {
|
||||
Text("Регистрация")
|
||||
}
|
||||
Text(
|
||||
text = "Уже есть аккаунт? Войти",
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
isRegistration = false
|
||||
}
|
||||
.align(Alignment.CenterHorizontally),
|
||||
color = MaterialTheme.colorScheme.onBackground
|
||||
)
|
||||
} else {
|
||||
Button(
|
||||
onClick = { },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp)
|
||||
) {
|
||||
Text("Вход")
|
||||
}
|
||||
Text(
|
||||
text = "Нет аккаунта? Зарегистрироваться",
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
isRegistration = true
|
||||
}
|
||||
.align(Alignment.CenterHorizontally),
|
||||
color = MaterialTheme.colorScheme.onBackground
|
||||
)
|
||||
}
|
||||
val switchColors = SwitchDefaults.colors(
|
||||
checkedThumbColor = MaterialTheme.colorScheme.primary, // Change the color when the switch is checked
|
||||
checkedTrackColor = MaterialTheme.colorScheme.secondary, // Change the color of the track when the switch is checked
|
||||
uncheckedThumbColor = MaterialTheme.colorScheme.primary, // Change the color when the switch is unchecked
|
||||
uncheckedTrackColor = MaterialTheme.colorScheme.onPrimary // Change the color of the track when the switch is unchecked
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
horizontalArrangement = Arrangement.End
|
||||
) {
|
||||
Text(
|
||||
"Темная тема", modifier = Modifier
|
||||
.align(Alignment.CenterVertically)
|
||||
.padding(5.dp)
|
||||
)
|
||||
|
||||
val coroutine = rememberCoroutineScope()
|
||||
|
||||
Switch(
|
||||
checked = isDarkTheme.value,
|
||||
onCheckedChange = {
|
||||
isDarkTheme.value = !isDarkTheme.value
|
||||
coroutine.launch {
|
||||
dataStoreManager.saveSettings(SettingData(isDarkTheme = isDarkTheme.value))
|
||||
}
|
||||
},
|
||||
colors = switchColors
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
fun UserProfilePreview() {
|
||||
PmudemoTheme {
|
||||
Surface(
|
||||
color = MaterialTheme.colorScheme.background
|
||||
) {
|
||||
UserProfile(navController = null, isDarkTheme = remember { mutableStateOf(true) })
|
||||
}
|
||||
}
|
||||
}*/
|
@ -0,0 +1,33 @@
|
||||
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<List<Cinema>>
|
||||
|
||||
@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<Cinema, List<SessionFromCinema>>
|
||||
|
||||
@Insert
|
||||
suspend fun insert(cinema: Cinema)
|
||||
|
||||
@Update
|
||||
suspend fun update(cinema: Cinema)
|
||||
|
||||
@Delete
|
||||
suspend fun delete(cinema: Cinema)
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
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<List<Order>>
|
||||
|
||||
@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<SessionFromOrder>
|
||||
|
||||
@Insert
|
||||
suspend fun insert(order: Order)
|
||||
|
||||
@Update
|
||||
suspend fun update(order: Order)
|
||||
|
||||
@Delete
|
||||
suspend fun delete(order: Order)
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
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.OrderSessionCrossRef
|
||||
|
||||
@Dao
|
||||
interface OrderSessionCrossRefDao {
|
||||
@Insert
|
||||
suspend fun insert(orderSessionCrossRef: OrderSessionCrossRef)
|
||||
|
||||
@Update
|
||||
suspend fun update(orderSessionCrossRef: OrderSessionCrossRef)
|
||||
|
||||
@Delete
|
||||
suspend fun delete(orderSessionCrossRef: OrderSessionCrossRef)
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.example.myapplication.entities.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Update
|
||||
import com.example.myapplication.entities.model.Session
|
||||
|
||||
@Dao
|
||||
interface SessionDao {
|
||||
@Insert
|
||||
suspend fun insert(session: Session)
|
||||
|
||||
@Update
|
||||
suspend fun update(session: Session)
|
||||
|
||||
@Delete
|
||||
suspend fun delete(session: Session)
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
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.SessionFromCart
|
||||
import com.example.myapplication.entities.model.User
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface UserDao {
|
||||
@Query("select * from users order by login collate nocase asc")
|
||||
fun getAll(): Flow<List<User>>
|
||||
|
||||
@Query(
|
||||
"SELECT sessions.*, sessions.max_count-sum(orders_sessions.count) 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 " +
|
||||
"where users_sessions.user_id = :userId " +
|
||||
"GROUP BY orders_sessions.session_id "
|
||||
)
|
||||
fun getCartByUid(userId: Int): Flow<List<SessionFromCart>>
|
||||
|
||||
@Insert
|
||||
suspend fun insert(user: User)
|
||||
|
||||
@Update
|
||||
suspend fun update(user: User)
|
||||
|
||||
@Delete
|
||||
suspend fun delete(user: User)
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
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.SessionFromCart
|
||||
import com.example.myapplication.entities.model.UserSessionCrossRef
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface UserSessionCrossRefDao {
|
||||
@Insert
|
||||
suspend fun insert(userSessionCrossRef: UserSessionCrossRef)
|
||||
|
||||
@Update
|
||||
suspend fun update(userSessionCrossRef: UserSessionCrossRef)
|
||||
|
||||
@Delete
|
||||
suspend fun delete(userSessionCrossRef: UserSessionCrossRef)
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package com.example.myapplication.entities.model
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Ignore
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "cinemas")
|
||||
data class Cinema(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
val uid: Int?,
|
||||
val name: String,
|
||||
val description: String,
|
||||
@ColumnInfo(typeAffinity = ColumnInfo.BLOB)
|
||||
val image: ByteArray?,
|
||||
val year: Long
|
||||
) {
|
||||
@Ignore
|
||||
constructor(
|
||||
name: String,
|
||||
description: String,
|
||||
image: ByteArray?,
|
||||
year: Long
|
||||
) : this(null, name, description, image, year)
|
||||
|
||||
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
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return uid ?: -1
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,16 @@
|
||||
package com.example.myapplication.entities.model
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import org.threeten.bp.LocalDateTime
|
||||
|
||||
class LocalDateTimeConverter {
|
||||
@TypeConverter
|
||||
fun fromLocalDateTime(value: LocalDateTime?): String? {
|
||||
return value?.toString()
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun toLocalDateTime(value: String?): LocalDateTime? {
|
||||
return value?.let { LocalDateTime.parse(it) }
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package com.example.myapplication.entities.model
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(
|
||||
tableName = "orders", foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = User::class,
|
||||
parentColumns = ["uid"],
|
||||
childColumns = ["user_id"],
|
||||
onDelete = ForeignKey.RESTRICT,
|
||||
onUpdate = ForeignKey.RESTRICT
|
||||
)
|
||||
]
|
||||
)
|
||||
data class Order(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
val uid: Int,
|
||||
@ColumnInfo(name = "user_id", index = true)
|
||||
val userId: Int?,
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
other as Order
|
||||
if (uid != other.uid) return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return uid
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,37 @@
|
||||
package com.example.myapplication.entities.model
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import java.util.Objects
|
||||
|
||||
@Entity(
|
||||
tableName = "orders_sessions",
|
||||
primaryKeys = ["order_id", "session_id"]
|
||||
)
|
||||
data class OrderSessionCrossRef(
|
||||
@ColumnInfo(name = "order_id", index = true)
|
||||
val orderId: Int,
|
||||
@ColumnInfo(name = "session_id", index = true)
|
||||
val sessionId: Int,
|
||||
@ColumnInfo(name = "frozen_price")
|
||||
val frozenPrice: Double,
|
||||
val count: Int
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) {
|
||||
return true
|
||||
}
|
||||
if (javaClass != other?.javaClass) {
|
||||
return false
|
||||
}
|
||||
other as OrderSessionCrossRef
|
||||
if (orderId == other.orderId && sessionId == other.sessionId) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return Objects.hash(orderId, sessionId)
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package com.example.myapplication.entities.model
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.Ignore
|
||||
import androidx.room.PrimaryKey
|
||||
import org.threeten.bp.LocalDateTime
|
||||
|
||||
@Entity(
|
||||
tableName = "sessions", foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = Cinema::class,
|
||||
parentColumns = ["uid"],
|
||||
childColumns = ["cinema_id"],
|
||||
onDelete = ForeignKey.RESTRICT,
|
||||
onUpdate = ForeignKey.RESTRICT
|
||||
)
|
||||
]
|
||||
)
|
||||
data class Session(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
val uid: Int?,
|
||||
@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?,
|
||||
) {
|
||||
@Ignore
|
||||
constructor(
|
||||
dateTime: LocalDateTime,
|
||||
price: Double,
|
||||
maxCount: Int,
|
||||
cinema: Cinema,
|
||||
) : this(null, dateTime, price, maxCount, cinema.uid)
|
||||
|
||||
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
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return uid ?: -1
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,23 @@
|
||||
package com.example.myapplication.entities.model
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Relation
|
||||
import org.threeten.bp.LocalDateTime
|
||||
|
||||
data class SessionFromCart(
|
||||
@ColumnInfo(name = "uid")
|
||||
val uid: Int?,
|
||||
@ColumnInfo(name = "date_time")
|
||||
val dateTime: LocalDateTime,
|
||||
val price: Double,
|
||||
@ColumnInfo(name = "available_count")
|
||||
val availableCount: Int,
|
||||
val count: Int,
|
||||
@ColumnInfo(name = "cinema_id")
|
||||
val cinemaId: Int?,
|
||||
@Relation(
|
||||
parentColumn = "cinema_id",
|
||||
entity = Cinema::class,
|
||||
entityColumn = "uid"
|
||||
) val cinema: Cinema
|
||||
)
|
@ -0,0 +1,14 @@
|
||||
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,
|
||||
)
|
@ -0,0 +1,23 @@
|
||||
package com.example.myapplication.entities.model
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Relation
|
||||
import org.threeten.bp.LocalDateTime
|
||||
|
||||
data class SessionFromOrder(
|
||||
@ColumnInfo(name = "uid")
|
||||
val uid: Int?,
|
||||
@ColumnInfo(name = "date_time")
|
||||
val dateTime: LocalDateTime,
|
||||
@ColumnInfo(name = "frozen_price")
|
||||
val frozenPrice: Double,
|
||||
val count: Int,
|
||||
@ColumnInfo(name = "cinema_id")
|
||||
val cinemaId: Int?,
|
||||
@Relation(
|
||||
parentColumn = "cinema_id",
|
||||
entity = Cinema::class,
|
||||
entityColumn = "uid"
|
||||
)
|
||||
val cinema: Cinema
|
||||
)
|
@ -0,0 +1,24 @@
|
||||
package com.example.myapplication.entities.model
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "users")
|
||||
data class User(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
val uid: Int?,
|
||||
val login: String,
|
||||
val password: String
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
other as User
|
||||
if (uid != other.uid) return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return uid ?: -1
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
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,
|
||||
)
|
@ -0,0 +1,11 @@
|
||||
package com.example.myapplication.ui.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val LightGray = Color(0xFFB2CCD6)
|
||||
val LightBlueGray = Color(0xFF70A3B2)
|
||||
val LightBgGray = Color(0xFFCED6DC)
|
||||
|
||||
val Gray = Color(0xFFD6D6D6)
|
||||
val DarkGray = Color(0xFF191A1F)
|
||||
val BgGray = Color(0xFF2A2D32)
|
@ -0,0 +1,80 @@
|
||||
package com.example.myapplication.ui.theme
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.core.view.WindowCompat
|
||||
|
||||
private val DarkColorScheme = darkColorScheme(
|
||||
primary = DarkGray,
|
||||
onPrimary = Color.White,
|
||||
|
||||
secondary = Gray,
|
||||
onSecondary = Color.Black,
|
||||
|
||||
background = BgGray,
|
||||
onBackground = Color.White,
|
||||
)
|
||||
|
||||
private val LightColorScheme = lightColorScheme(
|
||||
primary = LightBlueGray,
|
||||
onPrimary = Color.White,
|
||||
|
||||
secondary = LightGray,
|
||||
onSecondary = Color.Black,
|
||||
|
||||
onBackground = Color.Black,
|
||||
|
||||
/* Other default colors to override
|
||||
background = Color(0xFFFFFBFE),
|
||||
surface = Color(0xFFFFFBFE),
|
||||
onPrimary = Color.White,
|
||||
onSecondary = Color.White,
|
||||
onTertiary = Color.White,
|
||||
onBackground = Color(0xFF1C1B1F),
|
||||
onSurface = Color(0xFF1C1B1F),
|
||||
*/
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun PmudemoTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
// Dynamic color is available on Android 12+
|
||||
dynamicColor: Boolean = false,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val colorScheme = when {
|
||||
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
val context = LocalContext.current
|
||||
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||
}
|
||||
|
||||
darkTheme -> DarkColorScheme
|
||||
else -> LightColorScheme
|
||||
}
|
||||
val view = LocalView.current
|
||||
if (!view.isInEditMode) {
|
||||
SideEffect {
|
||||
val window = (view.context as Activity).window
|
||||
window.statusBarColor = colorScheme.primary.toArgb()
|
||||
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
|
||||
}
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = Typography,
|
||||
content = content
|
||||
)
|
||||
}
|
34
app/src/main/java/com/example/myapplication/ui.theme/Type.kt
Normal file
34
app/src/main/java/com/example/myapplication/ui.theme/Type.kt
Normal file
@ -0,0 +1,34 @@
|
||||
package com.example.myapplication.ui.theme
|
||||
|
||||
import androidx.compose.material3.Typography
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
// Set of Material typography styles to start with
|
||||
val Typography = Typography(
|
||||
bodyLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 16.sp,
|
||||
lineHeight = 24.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
/* Other default text styles to override
|
||||
titleLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 22.sp,
|
||||
lineHeight = 28.sp,
|
||||
letterSpacing = 0.sp
|
||||
),
|
||||
labelSmall = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 11.sp,
|
||||
lineHeight = 16.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
*/
|
||||
)
|
8
app/src/main/res/drawable/minus.xml
Normal file
8
app/src/main/res/drawable/minus.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path android:fillColor="#000000"
|
||||
android:pathData="M19,13H5v-2h14v2z"/>
|
||||
</vector>
|
BIN
app/src/main/res/drawable/photo.jpg
Normal file
BIN
app/src/main/res/drawable/photo.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
25
app/src/main/res/drawable/ticket.xml
Normal file
25
app/src/main/res/drawable/ticket.xml
Normal file
@ -0,0 +1,25 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="64dp"
|
||||
android:height="64dp"
|
||||
android:viewportWidth="64"
|
||||
android:viewportHeight="64">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
|
||||
<!-- Зона фона -->
|
||||
<rect width="64" height="64" rx="8" ry="8" fill="#FFC107" />
|
||||
|
||||
<!-- Билет -->
|
||||
<rect x="8" y="12" width="48" height="40" rx="4" ry="4" fill="#FFFFFF" />
|
||||
|
||||
<!-- Линия для разделения -->
|
||||
<line x1="8" y1="32" x2="56" y2="32" stroke="#FFA000" stroke-width="2" />
|
||||
|
||||
<!-- Линия для номера билета -->
|
||||
<line x1="16" y1="48" x2="48" y2="48" stroke="#FFA000" stroke-width="2" />
|
||||
|
||||
<!-- Текст для номера билета -->
|
||||
<text x="32" y="53" fill="#FFA000" font-size="12" text-anchor="middle">
|
||||
</text>
|
||||
</svg>
|
||||
|
||||
</vector>
|
@ -1,5 +1,6 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
id("com.android.application") version "8.1.1" apply false
|
||||
id("org.jetbrains.kotlin.android") version "1.8.10" apply false
|
||||
id("com.android.application") version "8.1.2" apply false
|
||||
id("org.jetbrains.kotlin.android") version "1.8.20" apply false
|
||||
id("com.google.devtools.ksp") version "1.8.20-1.0.11" apply false
|
||||
}
|
Loading…
Reference in New Issue
Block a user