Compare commits

..

4 Commits
master ... lw3

Author SHA1 Message Date
eb23e9c671 lw3 2023-12-09 19:40:07 +04:00
edb8f8b6b6 lw3 2023-12-09 19:39:08 +04:00
5a18ee67fc lw3 2023-12-05 14:48:48 +04:00
f879990c39 lw3 2023-12-05 14:48:15 +04:00
51 changed files with 1991 additions and 222 deletions

View File

@ -1,123 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</codeStyleSettings>
</code_scheme>
</component>

View File

@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

View 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>

View File

@ -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
View 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>

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -10,13 +10,13 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MyApplication"
android:theme="@style/Theme.Pmudemo"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:name=".MainComposeActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.MyApplication">
android:theme="@style/Theme.Pmudemo">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

View File

@ -1,43 +0,0 @@
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.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.example.myapplication.ui.theme.MyApplicationTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApplicationTheme {
// A surface container using the 'background' color from the theme
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
Greeting("Android")
}
}
}
}
}
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
Text(
text = "Hello $name!",
modifier = modifier
)
}
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
MyApplicationTheme {
Greeting("Android")
}
}

View File

@ -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
)
}
}
}
}
}

View 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)
}
}
}

View File

@ -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)
}
}

View File

@ -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) }
}
}
}

View File

@ -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()
}
}
}

View File

@ -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
)
}
}

View File

@ -0,0 +1,5 @@
package com.example.myapplication.datastore
data class SettingData(
val isDarkTheme: Boolean
)

View File

@ -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)
}
}
}

View File

@ -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)
}
}
}

View File

@ -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)
}
}
}

View File

@ -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)
}
}
}

View File

@ -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) })
}
}
}*/

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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
}
}

View File

@ -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) }
}
}

View File

@ -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
}
}

View File

@ -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)
}
}

View File

@ -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
}
}

View File

@ -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
)

View File

@ -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,
)

View File

@ -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
)

View File

@ -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
}
}

View File

@ -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,
)

View File

@ -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)

View File

@ -10,23 +10,33 @@ import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat
private val DarkColorScheme = darkColorScheme(
primary = Purple80,
secondary = PurpleGrey80,
tertiary = Pink80
primary = DarkGray,
onPrimary = Color.White,
secondary = Gray,
onSecondary = Color.Black,
background = BgGray,
onBackground = Color.White,
)
private val LightColorScheme = lightColorScheme(
primary = Purple40,
secondary = PurpleGrey40,
tertiary = Pink40
primary = LightBlueGray,
onPrimary = Color.White,
/* Other default colors to override
secondary = LightGray,
onSecondary = Color.Black,
onBackground = Color.Black,
/* Other default colors to override
background = Color(0xFFFFFBFE),
surface = Color(0xFFFFFBFE),
onPrimary = Color.White,
@ -38,11 +48,11 @@ private val LightColorScheme = lightColorScheme(
)
@Composable
fun MyApplicationTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
content: @Composable () -> Unit
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 -> {
@ -63,8 +73,8 @@ fun MyApplicationTheme(
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
colorScheme = colorScheme,
typography = Typography,
content = content
)
}

View File

@ -8,14 +8,14 @@ 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
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,

View File

@ -1,11 +0,0 @@
package com.example.myapplication.ui.theme
import androidx.compose.ui.graphics.Color
val Purple80 = Color(0xFFD0BCFF)
val PurpleGrey80 = Color(0xFFCCC2DC)
val Pink80 = Color(0xFFEFB8C8)
val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71)
val Pink40 = Color(0xFF7D5260)

View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View 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>

View File

@ -1,3 +1,14 @@
<resources>
<string name="app_name">My Application</string>
<string name="app_name">pmu-demo</string>
<string name="Cinema_main_title">Фильмы</string>
<string name="Cinema_view_title">Фильм</string>
<string name="Order_view_title">Заказ</string>
<string name="Cinema_name">Название</string>
<string name="Cinema_year">Год</string>
<string name="Cinema_description">Описание</string>
<string name="Cinema_image">Изображение</string>
<string name="Cart_title">Корзина</string>
<string name="Order_title">Мои заказы</string>
<string name="Profile_title">Профиль</string>
<string name="Sessions_title">Сеансы</string>
</resources>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.MyApplication" parent="android:Theme.Material.Light.NoActionBar" />
<style name="Theme.Pmudemo" parent="android:Theme.Material.Light.NoActionBar" />
</resources>

View File

@ -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
}

View File

@ -1,4 +1,4 @@
#Mon Sep 18 13:48:19 GMT+04:00 2023
#Sun Oct 01 15:07:08 GMT+04:00 2023
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip

View File

@ -15,3 +15,4 @@ dependencyResolutionManagement {
rootProject.name = "My Application"
include(":app")