started 4 lab

This commit is contained in:
Zyzf 2023-12-03 18:03:39 +04:00
parent e3e4d1cacb
commit 889e80ad9e
36 changed files with 942 additions and 730 deletions

View File

@ -1,17 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="deploymentTargetDropDown"> <component name="deploymentTargetDropDown">
<targetSelectedWithDropDown> <value>
<Target> <entry key="app">
<type value="QUICK_BOOT_TARGET" /> <State />
<deviceKey> </entry>
<Key> </value>
<type value="VIRTUAL_DEVICE_PATH" />
<value value="$USER_HOME$/.android/avd/Pixel_3a_API_25.avd" />
</Key>
</deviceKey>
</Target>
</targetSelectedWithDropDown>
<timeTargetWasSelectedWithDropDown value="2023-11-16T12:43:19.045949464Z" />
</component> </component>
</project> </project>

View File

@ -4,16 +4,15 @@
<component name="GradleSettings"> <component name="GradleSettings">
<option name="linkedExternalProjectsSettings"> <option name="linkedExternalProjectsSettings">
<GradleProjectSettings> <GradleProjectSettings>
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="jbr-17" /> <option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
<option name="modules"> <option name="modules">
<set> <set>
<option value="$PROJECT_DIR$" /> <option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" /> <option value="$PROJECT_DIR$/app" />
</set> </set>
</option> </option>
<option name="resolveExternalAnnotations" value="false" />
</GradleProjectSettings> </GradleProjectSettings>
</option> </option>
</component> </component>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="KotlinJpsPluginSettings"> <component name="KotlinJpsPluginSettings">
<option name="version" value="1.9.10" /> <option name="version" value="1.9.20" />
</component> </component>
</project> </project>

10
.idea/migrations.xml Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectMigrations">
<option name="MigrateToGradleLocalJavaHome">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</component>
</project>

View File

@ -41,7 +41,7 @@ android {
compose = true compose = true
} }
composeOptions { composeOptions {
kotlinCompilerExtensionVersion = "1.5.3" kotlinCompilerExtensionVersion = "1.5.5"
} }
packaging { packaging {
resources { resources {
@ -63,34 +63,32 @@ dependencies {
implementation("androidx.activity:activity-compose:1.8.1") implementation("androidx.activity:activity-compose:1.8.1")
implementation(platform("androidx.compose:compose-bom:2023.10.01")) implementation(platform("androidx.compose:compose-bom:2023.10.01"))
implementation("androidx.navigation:navigation-compose:2.7.5") implementation("androidx.navigation:navigation-compose:2.7.5")
implementation("androidx.compose.ui:ui:1.6.0-beta01") implementation("androidx.compose.ui:ui:1.6.0-beta02")
implementation("androidx.compose.ui:ui-graphics:1.6.0-beta01") implementation("androidx.compose.ui:ui-graphics:1.6.0-beta02")
implementation("androidx.compose.ui:ui-tooling-preview:1.6.0-beta01") implementation("androidx.compose.ui:ui-tooling-preview:1.6.0-beta02")
implementation("androidx.compose.material3:material3:1.2.0-alpha11") implementation("androidx.compose.material3:material3:1.2.0-alpha12")
// Retrofit // Retrofit
implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0") implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0")
implementation("com.squareup.retrofit2:retrofit:2.9.0") implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("io.coil-kt:coil-compose:2.5.0") implementation("io.coil-kt:coil-compose:2.5.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.1") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
implementation("com.jcraft:jsch:0.1.55") implementation("com.jcraft:jsch:0.1.55")
// Room // Room
implementation("androidx.room:room-runtime:2.6.0") implementation("androidx.room:room-runtime:2.6.1")
annotationProcessor("androidx.room:room-compiler:2.6.0") annotationProcessor("androidx.room:room-compiler:2.6.1")
ksp("androidx.room:room-compiler:2.6.0") ksp("androidx.room:room-compiler:2.6.1")
implementation("androidx.room:room-ktx:2.6.0") implementation("androidx.room:room-ktx:2.6.1")
implementation("androidx.room:room-paging:2.6.0") implementation("androidx.room:room-paging:2.6.1")
// Tests // Tests
testImplementation("junit:junit:4.13.2") testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
androidTestImplementation(platform("androidx.compose:compose-bom:2023.10.01")) androidTestImplementation(platform("androidx.compose:compose-bom:2023.10.01"))
androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.6.0-beta01") androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.6.0-beta02")
debugImplementation("androidx.compose.ui:ui-tooling:1.6.0-beta01") debugImplementation("androidx.compose.ui:ui-tooling:1.6.0-beta02")
debugImplementation("androidx.compose.ui:ui-test-manifest:1.6.0-beta01") debugImplementation("androidx.compose.ui:ui-test-manifest:1.6.0-beta02")
} }

View File

@ -0,0 +1,14 @@
package com.zyzf.coffeepreorder
import android.app.Application
import com.zyzf.coffeepreorder.database.AppContainer
import com.zyzf.coffeepreorder.database.AppDataContainer
class CoffeeApplication : Application() {
lateinit var container: AppContainer
override fun onCreate() {
super.onCreate()
container = AppDataContainer(this)
}
}

View File

@ -10,7 +10,7 @@ import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import com.zyzf.coffeepreorder.composeui.navigation.MainNavbar import com.zyzf.coffeepreorder.ui.navigation.MainNavbar
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {

View File

@ -1,390 +0,0 @@
package com.zyzf.coffeepreorder.coffee.composeui
import android.annotation.SuppressLint
import android.content.res.Configuration
import android.graphics.Bitmap.CompressFormat
import android.graphics.BitmapFactory
import android.net.Uri
import android.os.StrictMode
import android.os.StrictMode.ThreadPolicy
import android.util.Log
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
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.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.outlined.Create
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.OutlinedIconButton
import androidx.compose.material3.OutlinedTextField
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.mutableDoubleStateOf
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.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import coil.compose.AsyncImage
import coil.request.ImageRequest
import com.jcraft.jsch.Channel
import com.jcraft.jsch.ChannelSftp
import com.jcraft.jsch.JSch
import com.jcraft.jsch.JSchException
import com.jcraft.jsch.Session
import com.jcraft.jsch.SftpException
import com.zyzf.coffeepreorder.R
import com.zyzf.coffeepreorder.database.AppDatabase
import com.zyzf.coffeepreorder.database.model.Coffee
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.FileOutputStream
import java.util.Properties
@SuppressLint("UnrememberedMutableState")
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class,
DelicateCoroutinesApi::class
)
@Composable
fun CoffeeList(navController: NavController?) {
val openDialog = remember { mutableStateOf(false) }
val add = remember { mutableStateOf(false) }
val coffee = remember { mutableStateOf(Coffee("", 0.0, "", null, 0)) }
val context = LocalContext.current
val itemsList = remember { mutableStateListOf<Coffee>() }
var imageUri: Any? by remember { mutableStateOf(R.drawable.img) }
val photoPicker = rememberLauncherForActivityResult(
contract = ActivityResultContracts.PickVisualMedia()
) {
if (it != null) {
Log.d("PhotoPicker", "Selected URI: $it")
imageUri = it
} else {
Log.d("PhotoPicker", "No media selected")
}
}
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
AppDatabase.getInstance(context).coffeeDao().getAll().collect { data ->
itemsList.clear()
itemsList.addAll(data)
}
}
}
LazyColumn(
horizontalAlignment = Alignment.CenterHorizontally) {
items(itemsList) { currentCoffee ->
Row(modifier = Modifier
.fillMaxWidth()
.heightIn(max = 140.dp)
.padding(bottom = 10.dp, top = 10.dp),
horizontalArrangement = Arrangement.SpaceAround) {
AsyncImage(
model = ImageRequest.Builder(context = LocalContext.current).data("https://zyzf.space/s/zXgFRTmbR4KMxMH/download?path=&files=coffee_image_" + currentCoffee.uid +".png")
.crossfade(true).build(),
error = painterResource(R.drawable.ic_broken_image),
placeholder = painterResource(R.drawable.loading_img),
contentDescription = "Кофе",
contentScale = ContentScale.Crop,
modifier = Modifier
.size(100.dp)
.clip(RoundedCornerShape(50.dp))
)
Column(
Modifier
.weight(2f)
.padding(start = 20.dp)) {
Text(text = currentCoffee.name, fontSize = 25.sp)
Text(text = String.format("%.2f", currentCoffee.cost), fontSize = 20.sp)
Text(text = currentCoffee.ingredients, fontSize = 15.sp)
Row(
Modifier
.fillMaxWidth()
.padding(top = 5.dp)) {
Button(
onClick = {
GlobalScope.launch (Dispatchers.Main) {
currentCoffee.uid?.let {
AppDatabase.getInstance(context).cartDao().get().uid?.let { it1 ->
AppDatabase.getInstance(context).cartDao().insertCoffee(
it1,
it,
1
)
}
}
AppDatabase.getInstance(context).coffeeDao().getAll().collect { data ->
itemsList.clear()
itemsList.addAll(data)
}
}
},
shape = CircleShape,
modifier = Modifier.fillMaxWidth(fraction = 0.75f)
) {
Icon(
imageVector = Icons.Default.Add,
contentDescription = "Favorite",
modifier = Modifier.size(20.dp)
)
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
Text(text = "В корзину")
}
OutlinedIconButton(
onClick = {
coffee.value = currentCoffee
add.value = false
openDialog.value = true
},
Modifier
.padding(start = 10.dp)
.clip(CircleShape)) {
Icon(
imageVector = Icons.Outlined.Create,
contentDescription = "Favorite",
modifier = Modifier.size(20.dp)
)
}
}
}
}
}
}
Box(modifier = Modifier.fillMaxSize()) {
FloatingActionButton(onClick = { coffee.value = Coffee("", 0.0, "", null, 0); add.value = true; openDialog.value = true },
Modifier
.padding(all = 20.dp)
.align(alignment = Alignment.BottomEnd)) {
Icon(
imageVector = Icons.Filled.Add,
contentDescription = "Add",
modifier = Modifier.size(20.dp)
)
}
}
if (openDialog.value) {
var name by remember { mutableStateOf(coffee.value.name) }
var cost by remember { mutableDoubleStateOf(coffee.value.cost) }
var ingredients by remember { mutableStateOf(coffee.value.ingredients) }
ModalBottomSheet(onDismissRequest = { openDialog.value = false }) {
Column(modifier = Modifier
.fillMaxWidth()
.padding(all = 10.dp),
horizontalAlignment = Alignment.CenterHorizontally) {
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
value = name, onValueChange = {name = it},
label = {
Text(stringResource(id = R.string.coffee_name))
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
)
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
value = cost.toString(), onValueChange = {cost = it.toDouble()},
label = {
Text(stringResource(id = R.string.coffee_cost))
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal)
)
OutlinedTextField(modifier = Modifier.fillMaxWidth().padding(bottom = 10.dp),
value = ingredients, onValueChange = {ingredients = it},
label = {
Text(stringResource(id = R.string.coffee_ingredients))
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
)
AsyncImage(
modifier = Modifier
.size(100.dp).clip(RoundedCornerShape(50.dp)).border(2.dp, MaterialTheme.colorScheme.outline, shape = RoundedCornerShape(50.dp))
.clickable {
photoPicker.launch(
PickVisualMediaRequest(
ActivityResultContracts.PickVisualMedia.ImageOnly
)
)
}.border(2.dp, MaterialTheme.colorScheme.outlineVariant, shape = RoundedCornerShape(50.dp)),
contentDescription = "Кофе",
error = painterResource(R.drawable.ic_broken_image),
placeholder = painterResource(R.drawable.loading_img),
contentScale = ContentScale.Crop,
model = ImageRequest.Builder(LocalContext.current)
.data(if (coffee.value.name != "") "https://zyzf.space/s/zXgFRTmbR4KMxMH/download?path=&files=coffee_image_" + coffee.value.uid +".png" else imageUri)
.crossfade(enable = true)
.build(),
)
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
if (add.value) {
Button(onClick = {
GlobalScope.launch (Dispatchers.Main) {
val newCoffee: Long = AppDatabase.getInstance(context).coffeeDao().insert(
name = name, cost = cost, ingredients = ingredients
)
val inputStream = context.contentResolver.openInputStream(imageUri as Uri)
val bitmap = BitmapFactory.decodeStream(inputStream)
val f = File(context.cacheDir, "coffee_image_$newCoffee.png")
withContext(Dispatchers.IO) {
f.createNewFile()
val bos = ByteArrayOutputStream()
bitmap.compress(CompressFormat.PNG, 0 /*ignored for PNG*/, bos)
val bitmapdata = bos.toByteArray()
val fos = FileOutputStream(f)
fos.write(bitmapdata)
fos.flush()
fos.close()
}
copyFileToSftp(f, "/mnt/nextcloud/data/Zyzf/files/Images")
AppDatabase.getInstance(context).coffeeDao().getAll().collect { data ->
itemsList.clear()
itemsList.addAll(data)
}
openDialog.value = false
}
}, modifier = Modifier.padding(0.dp, 10.dp, 0.dp, 30.dp)) {
Text("Добавить")
}
} else {
Button(onClick = {
GlobalScope.launch (Dispatchers.Main) {
AppDatabase.getInstance(context).coffeeDao().update(
Coffee(name = name, cost = cost, ingredients = ingredients, cartId = coffee.value.cartId, count = coffee.value.count)
)
AppDatabase.getInstance(context).coffeeDao().getAll().collect { data ->
itemsList.clear()
itemsList.addAll(data)
}
}
}, modifier = Modifier.padding(0.dp, 10.dp, 0.dp, 30.dp)) {
Text("Изменить")
}
Spacer(modifier = Modifier.padding(all = 20.dp))
Button(onClick = {
GlobalScope.launch (Dispatchers.Main) {
AppDatabase.getInstance(context).coffeeDao().delete(
coffee.value
)
AppDatabase.getInstance(context).coffeeDao().getAll().collect { data ->
itemsList.clear()
itemsList.addAll(data)
}
openDialog.value = false
}
}, modifier = Modifier.padding(0.dp, 10.dp, 0.dp, 30.dp)) {
Text("Удалить")
}
}
}
}
}
}
}
const val REMOTE_HOST = "109.197.199.134"
const val USERNAME = "zyzf"
const val PASSWORD = "250303Zyzf-d-grad"
const val REMOTE_PORT = 2223
fun copyFileToSftp(srcFile: File, ftpPath: String): Boolean {
var jschSession: Session? = null
try {
val jsch = JSch()
jsch.setKnownHosts("/home/mkyong/.ssh/known_hosts")
jschSession = jsch.getSession(USERNAME, REMOTE_HOST, REMOTE_PORT)
jschSession.setPassword(PASSWORD)
val config = Properties()
config["StrictHostKeyChecking"] = "no"
jschSession.setConfig(config)
val policy = ThreadPolicy.Builder().permitAll().build()
StrictMode.setThreadPolicy(policy)
jschSession.connect(10000)
val sftp: Channel = jschSession.openChannel("sftp")
sftp.connect(5000)
val channelSftp: ChannelSftp = sftp as ChannelSftp
channelSftp.put(srcFile.absolutePath, ftpPath)
channelSftp.exit()
} catch (e: JSchException) {
e.printStackTrace()
return false
} catch (e: SftpException) {
e.printStackTrace()
return false
} finally {
jschSession?.disconnect()
}
return true
}
@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 CoffeeListPreview() {
CoffeePreorderTheme {
Surface(
color = MaterialTheme.colorScheme.background
) {
CoffeeList(navController = null)
}
}
}

View File

@ -1,72 +0,0 @@
package com.zyzf.coffeepreorder.coffee.composeui
import android.content.res.Configuration
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.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.zyzf.coffeepreorder.R
import com.zyzf.coffeepreorder.coffee.model.getCoffee
import com.zyzf.coffeepreorder.composeui.navigation.Screen
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CoffeeView(navController: NavController?, id: Int) {
val coffee = getCoffee()[id]
ModalBottomSheet(onDismissRequest = { navController?.navigate(Screen.CoffeeList.route) }) {
Column(modifier = Modifier.fillMaxWidth().padding(all = 10.dp),
horizontalAlignment = Alignment.CenterHorizontally) {
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
value = coffee.name, onValueChange = {}, readOnly = true,
label = {
Text(stringResource(id = R.string.coffee_name))
}
)
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
value = coffee.cost.toString(), onValueChange = {}, readOnly = true,
label = {
Text(stringResource(id = R.string.coffee_cost))
}
)
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
value = coffee.ingredients, onValueChange = {}, readOnly = true,
label = {
Text(stringResource(id = R.string.coffee_ingredients))
}
)
Row(modifier = Modifier.fillMaxWidth()) {
Button(onClick = { navController?.navigate(Screen.CoffeeView.route) }) {
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 StudentViewPreview() {
CoffeePreorderTheme {
Surface(
color = MaterialTheme.colorScheme.background
) {
CoffeeView(navController = null, id = 0)
}
}
}

View File

@ -1,27 +0,0 @@
package com.zyzf.coffeepreorder.coffee.model
import com.zyzf.coffeepreorder.R
import java.io.Serializable
data class Coffee(
val name: String,
val cost: Double,
val ingredients: String,
val image: Int
) : Serializable
fun getCoffee(): List<Coffee> {
return listOf(
Coffee("Coffee1", 200.0, "Ing1", R.drawable.coffee_image),
Coffee("Coffee2", 300.0, "Ing2", R.drawable.coffee_image),
Coffee("Coffee3", 500.0, "Ing3", R.drawable.coffee_image),
Coffee("Coffee3", 500.0, "Ing3", R.drawable.coffee_image),
Coffee("Coffee3", 500.0, "Ing3", R.drawable.coffee_image),
Coffee("Coffee3", 500.0, "Ing3", R.drawable.coffee_image),
Coffee("Coffee3", 500.0, "Ing3", R.drawable.coffee_image),
Coffee("Coffee3", 500.0, "Ing3", R.drawable.coffee_image),
Coffee("Coffee3", 500.0, "Ing3", R.drawable.coffee_image),
Coffee("Coffee3", 500.0, "Ing3", R.drawable.coffee_image),
Coffee("Coffee3", 500.0, "Ing3", R.drawable.coffee_image)
)
}

View File

@ -1,152 +0,0 @@
package com.zyzf.coffeepreorder.composeui
import android.content.res.Configuration
import androidx.compose.foundation.layout.Column
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.shape.CircleShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Check
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
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.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import coil.compose.AsyncImage
import coil.request.ImageRequest
import com.zyzf.coffeepreorder.R
import com.zyzf.coffeepreorder.composeui.navigation.Screen
import com.zyzf.coffeepreorder.database.AppDatabase
import com.zyzf.coffeepreorder.database.model.User
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@OptIn(DelicateCoroutinesApi::class)
@Composable
fun Login(navController: NavController?) {
val context = LocalContext.current
var login by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") }
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
AppDatabase.getInstance(context).userDao().getAll()
}
}
Column(Modifier.padding(all = 10.dp), horizontalAlignment = Alignment.CenterHorizontally) {
AsyncImage(
model = ImageRequest.Builder(context = LocalContext.current).data("https://zyzf.space/s/YsHjPo3NDmoptSk/download/coffee_image.png")
.crossfade(true).build(),
error = painterResource(R.drawable.ic_broken_image),
placeholder = painterResource(R.drawable.loading_img),
contentDescription = "Кофе",
contentScale = ContentScale.Crop,
modifier = Modifier.size(150.dp)
)
Spacer(modifier = Modifier.padding(all = 20.dp))
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
value = login, onValueChange = {login = it},
label = {
Text(stringResource(id = R.string.profile_login_label))
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
)
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
value = password, onValueChange = {password = it},
label = {
Text(stringResource(id = R.string.profile_passw_label))
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
visualTransformation = PasswordVisualTransformation()
)
Spacer(modifier = Modifier.padding(all = 20.dp))
Button(
onClick = {
var user: User?
GlobalScope.launch (Dispatchers.Main) {
user = AppDatabase.getInstance(context).userDao().tryLogin(login, password)
if (user != null) {
AppDatabase.getInstance(context).userDao().logout()
AppDatabase.getInstance(context).userDao().setLogined(user!!.uid!!)
navController?.navigate(Screen.CoffeeList.route)
} else {
password = ""
login = "Неверный логин или пароль"
}
}
},
shape = CircleShape,
modifier = Modifier.fillMaxWidth(fraction = 0.75f),
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primaryContainer,
contentColor = MaterialTheme.colorScheme.primary
)
) {
Icon(
imageVector = Icons.Default.Check,
contentDescription = "Favorite",
modifier = Modifier.size(20.dp)
)
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
Text(text = "Войти")
}
Button(
onClick = { navController?.navigate(Screen.Register.route) },
shape = CircleShape,
modifier = Modifier.fillMaxWidth(fraction = 0.75f),
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.secondaryContainer,
contentColor = MaterialTheme.colorScheme.secondary
)
) {
// Inner content including an icon and a text label
Icon(
imageVector = Icons.Default.Add,
contentDescription = "Favorite",
modifier = Modifier.size(20.dp)
)
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
Text(text = "Зарегистрироваться")
}
}
}
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun LoginPreview() {
CoffeePreorderTheme {
Surface(
color = MaterialTheme.colorScheme.background
) {
Login(null)
}
}
}

View File

@ -0,0 +1,31 @@
package com.zyzf.coffeepreorder.database
import android.content.Context
import com.zyzf.coffeepreorder.database.repository.CartRepository
import com.zyzf.coffeepreorder.database.repository.CoffeeRepository
import com.zyzf.coffeepreorder.database.repository.OfflineCartRepository
import com.zyzf.coffeepreorder.database.repository.OfflineCoffeeRepository
import com.zyzf.coffeepreorder.database.repository.OfflineUserRepository
import com.zyzf.coffeepreorder.database.repository.UserRepository
interface AppContainer {
val coffeeRepository: CoffeeRepository
val userRepository: UserRepository
val cartRepository: CartRepository
}
class AppDataContainer(private val context: Context) : AppContainer {
override val coffeeRepository: CoffeeRepository by lazy {
OfflineCoffeeRepository(AppDatabase.getInstance(context).coffeeDao())
}
override val userRepository: UserRepository by lazy {
OfflineUserRepository(AppDatabase.getInstance(context).userDao())
}
override val cartRepository: CartRepository by lazy {
OfflineCartRepository(AppDatabase.getInstance(context).cartDao())
}
companion object {
const val TIMEOUT = 5000L
}
}

View File

@ -32,7 +32,7 @@ abstract class AppDatabase : RoomDatabase() {
INSTANCE?.let { database -> INSTANCE?.let { database ->
// Users // Users
val userDao = database.userDao() val userDao = database.userDao()
val user1 = User("zyzf", "Ян К.", "+79911152503", "250303zyzf") val user1 = User("zyzf", "Ян К.", "+79911152503", "250303zyzf", "admin")
userDao.insert(user1) userDao.insert(user1)
// Coffees // Coffees
val coffeeDao = database.coffeeDao() val coffeeDao = database.coffeeDao()

View File

@ -3,7 +3,6 @@ package com.zyzf.coffeepreorder.database.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Delete import androidx.room.Delete
import androidx.room.Query import androidx.room.Query
import androidx.room.Update
import com.zyzf.coffeepreorder.database.model.Coffee import com.zyzf.coffeepreorder.database.model.Coffee
import com.zyzf.coffeepreorder.database.model.CoffeeWithCart import com.zyzf.coffeepreorder.database.model.CoffeeWithCart
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@ -20,13 +19,13 @@ interface CoffeeDao {
fun getSumInCart(): Double fun getSumInCart(): Double
@Query("select coffee.uid, name, cost, ingredients, cart_id, count, cart.uid as cart_uid from coffee left join cart on coffee.cart_id = cart.uid where coffee.uid = :uid") @Query("select coffee.uid, name, cost, ingredients, cart_id, count, cart.uid as cart_uid from coffee left join cart on coffee.cart_id = cart.uid where coffee.uid = :uid")
suspend fun getByUid(uid: Int): CoffeeWithCart suspend fun getByUid(uid: Int): CoffeeWithCart?
@Query("insert into coffee (name, cost, ingredients, count) values (:name, :cost, :ingredients, 0)") @Query("insert into coffee (name, cost, ingredients, count) values (:name, :cost, :ingredients, 0)")
suspend fun insert(name: String, cost: Double, ingredients: String): Long suspend fun insert(name: String, cost: Double, ingredients: String): Long
@Update @Query("update coffee set name = :name, cost = :cost, ingredients = :ingredients, cart_id = :cartId, count = :count where uid = :uid")
suspend fun update(coffee: Coffee) suspend fun update(uid: Int, name: String, cost: Double, ingredients: String, cartId: Int?, count: Int): Int?
@Delete @Delete
suspend fun delete(coffee: Coffee) suspend fun delete(coffee: Coffee)

View File

@ -12,16 +12,17 @@ interface UserDao {
fun getAll(): Flow<List<User>> fun getAll(): Flow<List<User>>
@Query("select * from user where login = :login and password = :password") @Query("select * from user where login = :login and password = :password")
suspend fun tryLogin(login: String, password: String): User suspend fun tryLogin(login: String, password: String): User?
@Query("select * from user where uid = :uid") @Query("select * from user where uid = :uid")
suspend fun getByUid(uid: Int): User suspend fun getByUid(uid: Int): User?
@Query("select user.uid, login, fio, phone, password from user join user_logined on user_logined.user_id = user.uid limit 1") @Query("select user.uid, login, fio, phone, password, role from user join user_logined on user_logined.user_id = user.uid limit 1")
suspend fun getLogined(): User suspend fun getLogined(): User?
@Query("insert into user_logined (user_id) values (:userId)") @Query("insert into user_logined (user_id) values (:userId)")
suspend fun setLogined(userId: Int) suspend fun setLogined(userId: Int)
@Query("delete from user_logined") @Query("delete from user_logined")
suspend fun logout() suspend fun logout()
@ -29,7 +30,7 @@ interface UserDao {
suspend fun insert(user: User) suspend fun insert(user: User)
@Query("update user set login = :login, fio = :fio, phone = :phone, password = :password where uid = :uid") @Query("update user set login = :login, fio = :fio, phone = :phone, password = :password where uid = :uid")
suspend fun update(uid: Int, login: String, fio: String, phone: String, password: String) : Int suspend fun update(uid: Int, login: String, fio: String, phone: String, password: String) : Int?
@Query("delete from user where uid = :uid") @Query("delete from user where uid = :uid")
suspend fun delete(uid: Int) suspend fun delete(uid: Int)

View File

@ -38,12 +38,24 @@ data class Coffee(
count: Int? count: Int?
) : this(null, name, cost, ingredients, null, 0) ) : this(null, name, cost, ingredients, null, 0)
companion object {
fun getCoffee(index: Int = 0): Coffee {
return Coffee(
index,
"",
0.0,
"",
null,
0
)
}
}
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
if (javaClass != other?.javaClass) return false if (javaClass != other?.javaClass) return false
other as Coffee other as Coffee
if (uid != other.uid) return false return uid == other.uid
return true
} }
override fun hashCode(): Int { override fun hashCode(): Int {

View File

@ -16,22 +16,24 @@ data class User(
@ColumnInfo(name = "phone") @ColumnInfo(name = "phone")
var phone: String, var phone: String,
@ColumnInfo(name = "password") @ColumnInfo(name = "password")
var password: String var password: String,
@ColumnInfo(name = "role")
var role: String
) { ) {
@Ignore @Ignore
constructor( constructor(
login: String, login: String,
fio: String, fio: String,
phone: String, phone: String,
password: String password: String,
) : this(null, login, fio, phone, password) role: String
) : this(null, login, fio, phone, password, role)
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
if (javaClass != other?.javaClass) return false if (javaClass != other?.javaClass) return false
other as User other as User
if (uid != other.uid) return false return uid == other.uid
return true
} }
override fun hashCode(): Int { override fun hashCode(): Int {

View File

@ -0,0 +1,12 @@
package com.zyzf.coffeepreorder.database.repository
import com.zyzf.coffeepreorder.database.model.Cart
interface CartRepository {
suspend fun get(): Cart
suspend fun insert(cart: Cart)
suspend fun insertCoffee(cartId: Int, coffeeId: Int, count: Int)
suspend fun deleteCoffee(coffeeId: Int, count: Int)
suspend fun update(cart: Cart)
suspend fun deleteAll()
}

View File

@ -0,0 +1,15 @@
package com.zyzf.coffeepreorder.database.repository
import com.zyzf.coffeepreorder.database.model.Coffee
import com.zyzf.coffeepreorder.database.model.CoffeeWithCart
import kotlinx.coroutines.flow.Flow
interface CoffeeRepository {
fun getAll(): Flow<List<Coffee>>
fun getAllInCart(): Flow<List<Coffee>>
fun getSumInCart(): Double
suspend fun getByUid(uid: Int): CoffeeWithCart?
suspend fun insert(name: String, cost: Double, ingredients: String): Long
suspend fun update(uid: Int, name: String, cost: Double, ingredients: String, cartId: Int?, count: Int): Int?
suspend fun delete(coffee: Coffee)
}

View File

@ -0,0 +1,13 @@
package com.zyzf.coffeepreorder.database.repository
import com.zyzf.coffeepreorder.database.dao.CartDao
import com.zyzf.coffeepreorder.database.model.Cart
class OfflineCartRepository(private val cartDao: CartDao) : CartRepository {
override suspend fun get(): Cart = cartDao.get()
override suspend fun insert(cart: Cart) = cartDao.insert(cart)
override suspend fun insertCoffee(cartId: Int, coffeeId: Int, count: Int) = cartDao.insertCoffee(cartId, coffeeId, count)
override suspend fun deleteCoffee(coffeeId: Int, count: Int) = cartDao.deleteCoffee(coffeeId, count)
override suspend fun update(cart: Cart) = cartDao.update(cart)
override suspend fun deleteAll() = cartDao.deleteAll()
}

View File

@ -0,0 +1,16 @@
package com.zyzf.coffeepreorder.database.repository
import com.zyzf.coffeepreorder.database.dao.CoffeeDao
import com.zyzf.coffeepreorder.database.model.Coffee
import com.zyzf.coffeepreorder.database.model.CoffeeWithCart
import kotlinx.coroutines.flow.Flow
class OfflineCoffeeRepository(private val coffeeDao: CoffeeDao) : CoffeeRepository {
override fun getAll(): Flow<List<Coffee>> = coffeeDao.getAll()
override fun getAllInCart(): Flow<List<Coffee>> = coffeeDao.getAllInCart()
override fun getSumInCart(): Double = coffeeDao.getSumInCart()
override suspend fun getByUid(uid: Int): CoffeeWithCart? = coffeeDao.getByUid(uid)
override suspend fun insert(name: String, cost: Double, ingredients: String): Long = coffeeDao.insert(name, cost, ingredients)
override suspend fun update(uid: Int, name: String, cost: Double, ingredients: String, cartId: Int?, count: Int): Int? = coffeeDao.update(uid, name, cost, ingredients, cartId, count)
override suspend fun delete(coffee: Coffee) = coffeeDao.delete(coffee)
}

View File

@ -0,0 +1,17 @@
package com.zyzf.coffeepreorder.database.repository
import com.zyzf.coffeepreorder.database.dao.UserDao
import com.zyzf.coffeepreorder.database.model.User
import kotlinx.coroutines.flow.Flow
class OfflineUserRepository(private val userDao: UserDao) : UserRepository {
override fun getAll(): Flow<List<User>> = userDao.getAll()
override suspend fun tryLogin(login: String, password: String): User? = userDao.tryLogin(login, password)
override suspend fun getByUid(uid: Int): User? = userDao.getByUid(uid)
override suspend fun getLogined(): User? = userDao.getLogined()
override suspend fun setLogined(userId: Int) = userDao.setLogined(userId)
override suspend fun logout() = userDao.logout()
override suspend fun insert(user: User) = userDao.insert(user)
override suspend fun update(uid: Int, login: String, fio: String, phone: String, password: String) : Int? = userDao.update(uid, login, fio, phone, password)
override suspend fun delete(uid: Int) = userDao.delete(uid)
}

View File

@ -0,0 +1,16 @@
package com.zyzf.coffeepreorder.database.repository
import com.zyzf.coffeepreorder.database.model.User
import kotlinx.coroutines.flow.Flow
interface UserRepository {
fun getAll(): Flow<List<User>>
suspend fun tryLogin(login: String, password: String): User?
suspend fun getByUid(uid: Int): User?
suspend fun getLogined(): User?
suspend fun setLogined(userId: Int)
suspend fun logout()
suspend fun insert(user: User)
suspend fun update(uid: Int, login: String, fio: String, phone: String, password: String) : Int?
suspend fun delete(uid: Int)
}

View File

@ -0,0 +1,23 @@
package com.zyzf.coffeepreorder.ui
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewmodel.CreationExtras
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.zyzf.coffeepreorder.CoffeeApplication
import com.zyzf.coffeepreorder.ui.coffee.CoffeeListViewModel
import com.zyzf.coffeepreorder.ui.login.LoginViewModel
object AppViewModelProvider {
val Factory = viewModelFactory {
initializer {
CoffeeListViewModel(coffeeApplication().container.coffeeRepository, coffeeApplication().container.cartRepository)
}
initializer {
LoginViewModel(coffeeApplication().container.userRepository)
}
}
}
fun CreationExtras.coffeeApplication(): CoffeeApplication =
(this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as CoffeeApplication)

View File

@ -1,4 +1,4 @@
package com.zyzf.coffeepreorder.composeui package com.zyzf.coffeepreorder.ui.cart
import android.content.res.Configuration import android.content.res.Configuration
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@ -21,7 +21,6 @@ import androidx.compose.material.icons.filled.Clear
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExtendedFloatingActionButton import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
@ -48,9 +47,9 @@ import androidx.navigation.NavController
import coil.compose.AsyncImage import coil.compose.AsyncImage
import coil.request.ImageRequest import coil.request.ImageRequest
import com.zyzf.coffeepreorder.R import com.zyzf.coffeepreorder.R
import com.zyzf.coffeepreorder.composeui.navigation.Screen
import com.zyzf.coffeepreorder.database.AppDatabase import com.zyzf.coffeepreorder.database.AppDatabase
import com.zyzf.coffeepreorder.database.model.Coffee import com.zyzf.coffeepreorder.database.model.Coffee
import com.zyzf.coffeepreorder.ui.navigation.Screen
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -58,7 +57,7 @@ import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@OptIn(ExperimentalMaterial3Api::class, DelicateCoroutinesApi::class) @OptIn(DelicateCoroutinesApi::class)
@Composable @Composable
fun Cart(navController: NavController?) { fun Cart(navController: NavController?) {
val openDialog = remember { mutableStateOf(false) } val openDialog = remember { mutableStateOf(false) }

View File

@ -0,0 +1,362 @@
package com.zyzf.coffeepreorder.ui.coffee
import android.content.Context
import android.content.res.Configuration
import android.net.Uri
import android.util.Log
import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.border
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.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
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.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.outlined.Create
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.OutlinedIconButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SheetState
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableDoubleStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
import coil.compose.AsyncImage
import coil.request.ImageRequest
import com.zyzf.coffeepreorder.R
import com.zyzf.coffeepreorder.database.model.Coffee
import com.zyzf.coffeepreorder.ui.AppViewModelProvider
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CoffeeList(
viewModel: CoffeeListViewModel = viewModel(factory = AppViewModelProvider.Factory)
) {
val coroutineScope = rememberCoroutineScope()
val coffeeListUiState = viewModel.coffeeListUiState.collectAsState()
val sheetState = rememberModalBottomSheetState()
val openDialog = remember { mutableStateOf(false) }
val photoPicker = rememberLauncherForActivityResult(
contract = ActivityResultContracts.PickVisualMedia()
) {
if (it != null) {
Log.d("PhotoPicker", "Selected URI: $it")
viewModel.imageUri = it
} else {
Log.d("PhotoPicker", "No media selected")
}
}
Scaffold(
topBar = {},
floatingActionButton = {
FloatingActionButton(
onClick = {
coroutineScope.launch {
viewModel.cleanCurrentCoffee()
}
},
Modifier
.padding(all = 20.dp)
) {
Icon(
imageVector = Icons.Filled.Add,
contentDescription = "Add",
modifier = Modifier.size(20.dp)
)
}
}
) { innerPadding ->
coffeeListUiState?.value?.coffeeList?.let {
CoffeeList(
modifier = Modifier
.padding(innerPadding)
.fillMaxSize(),
coffeeList = it,
onAddToCartClick = { coffeeUid: Int ->
coroutineScope.launch {
viewModel.addCoffeeToCart(coffeeUid = coffeeUid)
}
},
onEditClick = { coffee: Coffee ->
coroutineScope.launch {
viewModel.editCoffee(coffee = coffee)
}
}
)
}
AddEditModalBottomSheet(
coffee = viewModel.currentCoffee,
sheetState = sheetState,
openDialog = openDialog,
onAddClick = { coffee: Coffee, context: Context ->
coroutineScope.launch {
viewModel.createCoffee(coffee, context)
}
},
onEditClick = { coffee: Coffee, context: Context ->
coroutineScope.launch {
viewModel.editCoffee(coffee, context)
}
},
onDeleteClick = { coffee: Coffee ->
coroutineScope.launch {
viewModel.deleteCoffee(coffee)
}
},
photoPicker = photoPicker,
imageUri = viewModel.imageUri
)
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun AddEditModalBottomSheet(
coffee: Coffee,
sheetState: SheetState,
openDialog: MutableState<Boolean>,
onAddClick: (coffee: Coffee, context: Context) -> Unit,
onEditClick: (coffee: Coffee, context: Context) -> Unit,
onDeleteClick: (coffee: Coffee) -> Unit,
photoPicker: ManagedActivityResultLauncher<PickVisualMediaRequest, Uri?>,
imageUri: Any?,
modifier: Modifier = Modifier
) {
var name by remember { mutableStateOf(coffee.name) }
var cost by remember { mutableDoubleStateOf(coffee.cost) }
var ingredients by remember { mutableStateOf(coffee.ingredients) }
val context = LocalContext.current
if (openDialog.value) {
ModalBottomSheet(
onDismissRequest = { openDialog.value = false },
sheetState = sheetState
) {
Column(modifier = Modifier
.fillMaxWidth()
.padding(all = 10.dp),
horizontalAlignment = Alignment.CenterHorizontally) {
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
value = name, onValueChange = {name = it},
label = {
Text(stringResource(id = R.string.coffee_name))
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
)
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
value = cost.toString(), onValueChange = {cost = it.toDouble()},
label = {
Text(stringResource(id = R.string.coffee_cost))
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal)
)
OutlinedTextField(modifier = Modifier
.fillMaxWidth()
.padding(bottom = 10.dp),
value = ingredients, onValueChange = {ingredients = it},
label = {
Text(stringResource(id = R.string.coffee_ingredients))
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
)
AsyncImage(
modifier = Modifier
.size(100.dp)
.clip(RoundedCornerShape(50.dp))
.border(
2.dp,
MaterialTheme.colorScheme.outline,
shape = RoundedCornerShape(50.dp)
)
.clickable {
photoPicker.launch(
PickVisualMediaRequest(
ActivityResultContracts.PickVisualMedia.ImageOnly
)
)
}
.border(
2.dp,
MaterialTheme.colorScheme.outlineVariant,
shape = RoundedCornerShape(50.dp)
),
contentDescription = "Кофе",
error = painterResource(R.drawable.ic_broken_image),
placeholder = painterResource(R.drawable.loading_img),
contentScale = ContentScale.Crop,
model = ImageRequest.Builder(LocalContext.current)
.data(if (coffee.name != "") "https://zyzf.space/s/zXgFRTmbR4KMxMH/download?path=&files=coffee_image_" + coffee.uid +".png" else imageUri)
.crossfade(enable = true)
.build(),
)
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
if (coffee.uid == 0) {
Button(onClick = {
onAddClick(coffee, context)
openDialog.value = false
}, modifier = Modifier.padding(0.dp, 10.dp, 0.dp, 30.dp)) {
Text("Добавить")
}
} else {
Button(onClick = {
onEditClick(coffee, context)
openDialog.value = false
}, modifier = Modifier.padding(0.dp, 10.dp, 0.dp, 30.dp)) {
Text("Изменить")
}
Spacer(modifier = Modifier.padding(all = 20.dp))
Button(onClick = {
onDeleteClick(coffee)
openDialog.value = false
}, modifier = Modifier.padding(0.dp, 10.dp, 0.dp, 30.dp)) {
Text("Удалить")
}
}
}
}
}
}
}
@Composable
private fun CoffeeList(
modifier: Modifier = Modifier,
coffeeList: List<Coffee>,
onAddToCartClick: (coffeeUid: Int) -> Unit,
onEditClick: (coffee: Coffee) -> Unit
) {
LazyColumn(
horizontalAlignment = Alignment.CenterHorizontally) {
items(items = coffeeList, key = { it.uid!! }) { coffee ->
CoffeeListItem(coffee = coffee, onAddToCartClick = onAddToCartClick, onEditClick = onEditClick)
}
}
}
@Composable
private fun CoffeeListItem(
coffee: Coffee,
modifier: Modifier = Modifier,
onAddToCartClick: (coffeeUid: Int) -> Unit,
onEditClick: (coffee: Coffee) -> Unit
) {
Row(modifier = Modifier
.fillMaxWidth()
.heightIn(max = 140.dp)
.padding(bottom = 10.dp, top = 10.dp),
horizontalArrangement = Arrangement.SpaceAround) {
AsyncImage(
model = ImageRequest.Builder(context = LocalContext.current).data("https://zyzf.space/s/zXgFRTmbR4KMxMH/download?path=&files=coffee_image_" + coffee.uid +".png")
.crossfade(true).build(),
error = painterResource(R.drawable.ic_broken_image),
placeholder = painterResource(R.drawable.loading_img),
contentDescription = "Кофе",
contentScale = ContentScale.Crop,
modifier = Modifier
.size(100.dp)
.clip(RoundedCornerShape(50.dp))
)
Column(
Modifier
.weight(2f)
.padding(start = 20.dp)) {
Text(text = coffee.name, fontSize = 25.sp)
Text(text = String.format("%.2f", coffee.cost), fontSize = 20.sp)
Text(text = coffee.ingredients, fontSize = 15.sp)
Row(
Modifier
.fillMaxWidth()
.padding(top = 5.dp)) {
Button(
onClick = {
coffee.uid?.let { onAddToCartClick(it) }
},
shape = CircleShape,
modifier = Modifier.fillMaxWidth(fraction = 0.75f)
) {
Icon(
imageVector = Icons.Default.Add,
contentDescription = "Favorite",
modifier = Modifier.size(20.dp)
)
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
Text(text = "В корзину")
}
OutlinedIconButton(
onClick = {
onEditClick(coffee)
},
Modifier
.padding(start = 10.dp)
.clip(CircleShape)) {
Icon(
imageVector = Icons.Outlined.Create,
contentDescription = "Favorite",
modifier = Modifier.size(20.dp)
)
}
}
}
}
}
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun CoffeeListPreview() {
CoffeePreorderTheme {
Surface(
color = MaterialTheme.colorScheme.background
) {
CoffeeList(
coffeeList = (1..20).map { i -> Coffee.getCoffee(i) },
onAddToCartClick = {},
onEditClick = {}
)
}
}
}

View File

@ -0,0 +1,141 @@
package com.zyzf.coffeepreorder.ui.coffee
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import android.os.StrictMode
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.jcraft.jsch.Channel
import com.jcraft.jsch.ChannelSftp
import com.jcraft.jsch.JSch
import com.jcraft.jsch.JSchException
import com.jcraft.jsch.Session
import com.jcraft.jsch.SftpException
import com.zyzf.coffeepreorder.R
import com.zyzf.coffeepreorder.database.AppDataContainer
import com.zyzf.coffeepreorder.database.model.Cart
import com.zyzf.coffeepreorder.database.model.Coffee
import com.zyzf.coffeepreorder.database.repository.CartRepository
import com.zyzf.coffeepreorder.database.repository.CoffeeRepository
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.withContext
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.FileOutputStream
import java.util.Properties
class CoffeeListViewModel(
private val coffeeRepository: CoffeeRepository,
private val cartRepository: CartRepository
) : ViewModel() {
val coffeeListUiState: StateFlow<CoffeeListUiState> = coffeeRepository.getAll().map {
CoffeeListUiState(it)
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(stopTimeoutMillis = AppDataContainer.TIMEOUT),
initialValue = CoffeeListUiState()
)
var currentCoffee: Coffee = Coffee.getCoffee()
var imageUri: Any? = R.drawable.img
suspend fun addCoffeeToCart(coffeeUid: Int) {
val cart: Cart = cartRepository.get()
cart.uid?.let { cartRepository.insertCoffee(it, coffeeUid, 1) }
}
fun cleanCurrentCoffee() {
currentCoffee = Coffee.getCoffee()
}
fun editCoffee(coffee: Coffee) {
currentCoffee = coffee
}
suspend fun createCoffee(coffee: Coffee, context: Context) {
val newCoffee: Long = coffeeRepository.insert(coffee.name, coffee.cost, coffee.ingredients)
val inputStream = context.contentResolver.openInputStream(imageUri as Uri)
val bitmap = BitmapFactory.decodeStream(inputStream)
val f = File(context.cacheDir, "coffee_image_$newCoffee.png")
withContext(Dispatchers.IO) {
f.createNewFile()
val bos = ByteArrayOutputStream()
bitmap.compress(Bitmap.CompressFormat.PNG, 0 /*ignored for PNG*/, bos)
val bitmapdata = bos.toByteArray()
val fos = FileOutputStream(f)
fos.write(bitmapdata)
fos.flush()
fos.close()
}
copyFileToSftp(f, "/mnt/nextcloud/data/Zyzf/files/Images")
}
suspend fun editCoffee(coffee: Coffee, context: Context) {
val editedCoffee: Int = coffeeRepository.update(coffee.uid!!, coffee.name, coffee.cost, coffee.ingredients, coffee.cartId, coffee.count)!!
val inputStream = context.contentResolver.openInputStream(imageUri as Uri)
val bitmap = BitmapFactory.decodeStream(inputStream)
val f = File(context.cacheDir, "coffee_image_$editedCoffee.png")
withContext(Dispatchers.IO) {
f.createNewFile()
val bos = ByteArrayOutputStream()
bitmap.compress(Bitmap.CompressFormat.PNG, 0 /*ignored for PNG*/, bos)
val bitmapdata = bos.toByteArray()
val fos = FileOutputStream(f)
fos.write(bitmapdata)
fos.flush()
fos.close()
}
copyFileToSftp(f, "/mnt/nextcloud/data/Zyzf/files/Images")
}
suspend fun deleteCoffee(coffee: Coffee) {
coffeeRepository.delete(coffee)
}
}
data class CoffeeListUiState(val coffeeList: List<Coffee> = listOf())
const val REMOTE_HOST = "109.197.199.134"
const val USERNAME = "zyzf"
const val PASSWORD = "250303Zyzf-d-grad"
const val REMOTE_PORT = 2223
fun copyFileToSftp(srcFile: File, ftpPath: String): Boolean {
var jschSession: Session? = null
try {
val jsch = JSch()
jsch.setKnownHosts("/home/zyzf/.ssh/known_hosts")
jschSession = jsch.getSession(USERNAME, REMOTE_HOST, REMOTE_PORT)
jschSession.setPassword(PASSWORD)
val config = Properties()
config["StrictHostKeyChecking"] = "no"
jschSession.setConfig(config)
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
StrictMode.setThreadPolicy(policy)
jschSession.connect(10000)
val sftp: Channel = jschSession.openChannel("sftp")
sftp.connect(5000)
val channelSftp: ChannelSftp = sftp as ChannelSftp
channelSftp.put(srcFile.absolutePath, ftpPath)
channelSftp.exit()
} catch (e: JSchException) {
e.printStackTrace()
return false
} catch (e: SftpException) {
e.printStackTrace()
return false
} finally {
jschSession?.disconnect()
}
return true
}

View File

@ -0,0 +1,153 @@
package com.zyzf.coffeepreorder.ui.login
import android.annotation.SuppressLint
import android.content.res.Configuration
import androidx.compose.foundation.layout.Column
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.shape.CircleShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Check
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import coil.compose.AsyncImage
import coil.request.ImageRequest
import com.zyzf.coffeepreorder.R
import com.zyzf.coffeepreorder.ui.AppViewModelProvider
import com.zyzf.coffeepreorder.ui.navigation.Screen
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
import kotlinx.coroutines.launch
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
@Composable
fun Login(
navController: NavController?,
viewModel: LoginViewModel = viewModel(factory = AppViewModelProvider.Factory)
) {
val coroutineScope = rememberCoroutineScope()
val userListUiState = viewModel.userListUiState.collectAsState()
var login by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") }
Scaffold(
topBar = {},
floatingActionButton = {}
) {
Column(Modifier.padding(all = 10.dp), horizontalAlignment = Alignment.CenterHorizontally) {
AsyncImage(
model = ImageRequest.Builder(context = LocalContext.current)
.data("https://zyzf.space/s/YsHjPo3NDmoptSk/download/coffee_image.png")
.crossfade(true).build(),
error = painterResource(R.drawable.ic_broken_image),
placeholder = painterResource(R.drawable.loading_img),
contentDescription = "Кофе",
contentScale = ContentScale.Crop,
modifier = Modifier.size(150.dp)
)
Spacer(modifier = Modifier.padding(all = 20.dp))
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = login, onValueChange = { login = it },
label = {
Text(stringResource(id = R.string.profile_login_label))
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
)
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = password, onValueChange = { password = it },
label = {
Text(stringResource(id = R.string.profile_passw_label))
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
visualTransformation = PasswordVisualTransformation()
)
Spacer(modifier = Modifier.padding(all = 20.dp))
Button(
onClick = {
coroutineScope.launch {
if (viewModel.tryLogin(login, password)) {
navController?.navigate(Screen.CoffeeList.route)
} else {
password = ""
login = "Неверный логин или пароль"
}
}
},
shape = CircleShape,
modifier = Modifier.fillMaxWidth(fraction = 0.75f),
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primaryContainer,
contentColor = MaterialTheme.colorScheme.primary
)
) {
Icon(
imageVector = Icons.Default.Check,
contentDescription = "Favorite",
modifier = Modifier.size(20.dp)
)
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
Text(text = "Войти")
}
Button(
onClick = { navController?.navigate(Screen.Register.route) },
shape = CircleShape,
modifier = Modifier.fillMaxWidth(fraction = 0.75f),
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.secondaryContainer,
contentColor = MaterialTheme.colorScheme.secondary
)
) {
Icon(
imageVector = Icons.Default.Add,
contentDescription = "Favorite",
modifier = Modifier.size(20.dp)
)
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
Text(text = "Зарегистрироваться")
}
}
}
}
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun LoginPreview() {
CoffeePreorderTheme {
Surface(
color = MaterialTheme.colorScheme.background
) {
Login(null)
}
}
}

View File

@ -0,0 +1,36 @@
package com.zyzf.coffeepreorder.ui.login
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.zyzf.coffeepreorder.database.AppDataContainer
import com.zyzf.coffeepreorder.database.model.User
import com.zyzf.coffeepreorder.database.repository.UserRepository
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
class LoginViewModel(
private val userRepository: UserRepository
) : ViewModel() {
val userListUiState: StateFlow<UserListUiState> = userRepository.getAll().map {
UserListUiState(it)
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(stopTimeoutMillis = AppDataContainer.TIMEOUT),
initialValue = UserListUiState()
)
var user: User? = null
suspend fun tryLogin(login: String, password: String): Boolean {
user = userRepository.tryLogin(login, password)
return if (user != null) {
userRepository.logout()
userRepository.setLogined(user!!.uid!!)
true
} else {
false
}
}
}
data class UserListUiState(val userList: List<User> = listOf())

View File

@ -1,10 +1,10 @@
package com.zyzf.coffeepreorder.composeui.navigation package com.zyzf.coffeepreorder.ui.navigation
import android.content.res.Configuration import android.content.res.Configuration
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
@ -25,20 +25,17 @@ import androidx.navigation.NavDestination
import androidx.navigation.NavDestination.Companion.hierarchy import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.NavGraph.Companion.findStartDestination import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.zyzf.coffeepreorder.R import com.zyzf.coffeepreorder.R
import com.zyzf.coffeepreorder.coffee.composeui.CoffeeList import com.zyzf.coffeepreorder.ui.cart.Cart
import com.zyzf.coffeepreorder.coffee.composeui.CoffeeView import com.zyzf.coffeepreorder.ui.coffee.CoffeeList
import com.zyzf.coffeepreorder.composeui.Cart import com.zyzf.coffeepreorder.ui.login.Login
import com.zyzf.coffeepreorder.composeui.Login import com.zyzf.coffeepreorder.ui.order.Order
import com.zyzf.coffeepreorder.composeui.Order import com.zyzf.coffeepreorder.ui.profile.Profile
import com.zyzf.coffeepreorder.composeui.Profile import com.zyzf.coffeepreorder.ui.register.Register
import com.zyzf.coffeepreorder.composeui.Register
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@ -62,7 +59,7 @@ fun Topbar(
) { ) {
IconButton(onClick = { navController.navigateUp() }) { IconButton(onClick = { navController.navigateUp() }) {
Icon( Icon(
imageVector = Icons.Filled.ArrowBack, imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = null, contentDescription = null,
tint = MaterialTheme.colorScheme.onPrimary tint = MaterialTheme.colorScheme.onPrimary
) )
@ -111,16 +108,10 @@ fun Navhost(
) { ) {
composable(Screen.Login.route) { Login(navController) } composable(Screen.Login.route) { Login(navController) }
composable(Screen.Register.route) { Register(navController) } composable(Screen.Register.route) { Register(navController) }
composable(Screen.CoffeeList.route) { CoffeeList(navController) } composable(Screen.CoffeeList.route) { CoffeeList() }
composable(Screen.Profile.route) { Profile(navController) } composable(Screen.Profile.route) { Profile(navController) }
composable(Screen.Cart.route) { Cart(navController) } composable(Screen.Cart.route) { Cart(navController) }
composable(Screen.Order.route) { Order(navController) } composable(Screen.Order.route) { Order(navController) }
composable(
Screen.CoffeeView.route,
arguments = listOf(navArgument("id") { type = NavType.IntType })
) { backStackEntry ->
backStackEntry.arguments?.let { CoffeeView(navController, it.getInt("id")) }
}
} }
} }

View File

@ -1,4 +1,4 @@
package com.zyzf.coffeepreorder.composeui.navigation package com.zyzf.coffeepreorder.ui.navigation
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
@ -46,7 +46,7 @@ enum class Screen(
fun getItem(route: String): Screen? { fun getItem(route: String): Screen? {
val findRoute = route.split("/").first() val findRoute = route.split("/").first()
return values().find { value -> value.route.startsWith(findRoute) } return entries.find { value -> value.route.startsWith(findRoute) }
} }
} }
} }

View File

@ -1,4 +1,4 @@
package com.zyzf.coffeepreorder.composeui package com.zyzf.coffeepreorder.ui.order
import android.content.res.Configuration import android.content.res.Configuration
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@ -41,8 +41,8 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties import androidx.compose.ui.window.DialogProperties
import androidx.navigation.NavController import androidx.navigation.NavController
import com.zyzf.coffeepreorder.composeui.navigation.Screen
import com.zyzf.coffeepreorder.database.AppDatabase import com.zyzf.coffeepreorder.database.AppDatabase
import com.zyzf.coffeepreorder.ui.navigation.Screen
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext

View File

@ -1,4 +1,4 @@
package com.zyzf.coffeepreorder.composeui package com.zyzf.coffeepreorder.ui.profile
import android.content.res.Configuration import android.content.res.Configuration
import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.BorderStroke
@ -41,9 +41,9 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.navigation.NavController import androidx.navigation.NavController
import com.zyzf.coffeepreorder.R import com.zyzf.coffeepreorder.R
import com.zyzf.coffeepreorder.composeui.navigation.Screen
import com.zyzf.coffeepreorder.database.AppDatabase import com.zyzf.coffeepreorder.database.AppDatabase
import com.zyzf.coffeepreorder.database.model.User import com.zyzf.coffeepreorder.database.model.User
import com.zyzf.coffeepreorder.ui.navigation.Screen
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -58,7 +58,7 @@ fun Profile(navController: NavController?) {
val openDialogExit = remember { mutableStateOf(false) } val openDialogExit = remember { mutableStateOf(false) }
val openDialogDelete = remember { mutableStateOf(false) } val openDialogDelete = remember { mutableStateOf(false) }
val context = LocalContext.current val context = LocalContext.current
var user: User by remember { mutableStateOf(User("", "", "", "")) } var user: User by remember { mutableStateOf(User("", "", "", "", "")) }
var userLogin by remember { mutableStateOf("") } var userLogin by remember { mutableStateOf("") }
var userFIO by remember { mutableStateOf("") } var userFIO by remember { mutableStateOf("") }
var userPhone by remember { mutableStateOf("") } var userPhone by remember { mutableStateOf("") }
@ -67,7 +67,7 @@ fun Profile(navController: NavController?) {
val userNewPsswdConf = remember { mutableStateOf("") } val userNewPsswdConf = remember { mutableStateOf("") }
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
user = AppDatabase.getInstance(context).userDao().getLogined() user = AppDatabase.getInstance(context).userDao().getLogined()!!
userLogin = user.login userLogin = user.login
userFIO = user.fio userFIO = user.fio
userPhone = user.phone userPhone = user.phone
@ -195,7 +195,7 @@ fun Profile(navController: NavController?) {
GlobalScope.launch (Dispatchers.Main) { GlobalScope.launch (Dispatchers.Main) {
if (userOldPsswd.value == user.password && userNewPsswd.value == userNewPsswdConf.value) { if (userOldPsswd.value == user.password && userNewPsswd.value == userNewPsswdConf.value) {
AppDatabase.getInstance(context).userDao().update(user.uid!!, userLogin, userFIO, userPhone, userNewPsswd.value) AppDatabase.getInstance(context).userDao().update(user.uid!!, userLogin, userFIO, userPhone, userNewPsswd.value)
user = AppDatabase.getInstance(context).userDao().getLogined() user = AppDatabase.getInstance(context).userDao().getLogined()!!
} }
} }
openDialogEdit.value = false openDialogEdit.value = false

View File

@ -1,4 +1,4 @@
package com.zyzf.coffeepreorder.composeui package com.zyzf.coffeepreorder.ui.register
import android.content.res.Configuration import android.content.res.Configuration
@ -37,9 +37,9 @@ import androidx.navigation.NavController
import coil.compose.AsyncImage import coil.compose.AsyncImage
import coil.request.ImageRequest import coil.request.ImageRequest
import com.zyzf.coffeepreorder.R import com.zyzf.coffeepreorder.R
import com.zyzf.coffeepreorder.composeui.navigation.Screen
import com.zyzf.coffeepreorder.database.AppDatabase import com.zyzf.coffeepreorder.database.AppDatabase
import com.zyzf.coffeepreorder.database.model.User import com.zyzf.coffeepreorder.database.model.User
import com.zyzf.coffeepreorder.ui.navigation.Screen
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -106,7 +106,7 @@ fun Register(navController: NavController?) {
var user: User? var user: User?
GlobalScope.launch (Dispatchers.Main) { GlobalScope.launch (Dispatchers.Main) {
if (password == confPassword) { if (password == confPassword) {
AppDatabase.getInstance(context).userDao().insert(User(login, fio, phone, password)) AppDatabase.getInstance(context).userDao().insert(User(login, fio, phone, password, "user"))
user = AppDatabase.getInstance(context).userDao().tryLogin(login, password) user = AppDatabase.getInstance(context).userDao().tryLogin(login, password)
if (user != null) { if (user != null) {
AppDatabase.getInstance(context).userDao().logout() AppDatabase.getInstance(context).userDao().logout()

View File

@ -1,6 +1,6 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins { plugins {
id("com.android.application") version "8.1.4" apply false id("com.android.application") version "8.2.0" apply false
id("org.jetbrains.kotlin.android") version "1.9.10" apply false id("org.jetbrains.kotlin.android") version "1.9.20" apply false
id("com.google.devtools.ksp") version "1.9.10-1.0.13" apply false id("com.google.devtools.ksp") version "1.9.20-1.0.14" apply false
} }

View File

@ -1,6 +1,6 @@
#Sun Oct 15 15:51:04 GMT+04:00 2023 #Sun Oct 15 15:51:04 GMT+04:00 2023
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists