much done, need fixes

This commit is contained in:
zyzf 2023-12-04 01:52:39 +04:00
parent 889e80ad9e
commit ef95904c6f
15 changed files with 144 additions and 213 deletions

View File

@ -5,7 +5,7 @@
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
<option name="gradleJvm" value="jbr-17" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />

View File

@ -1,3 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">

View File

@ -51,10 +51,18 @@ android {
}
kotlin {
jvmToolchain(17)
jvmToolchain( 17)
}
dependencies {
// Retrofit
implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0")
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("io.coil-kt:coil-compose:2.5.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
implementation("com.jcraft:jsch:0.1.55")
// Core
implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
@ -66,22 +74,15 @@ dependencies {
implementation("androidx.compose.ui:ui:1.6.0-beta02")
implementation("androidx.compose.ui:ui-graphics:1.6.0-beta02")
implementation("androidx.compose.ui:ui-tooling-preview:1.6.0-beta02")
implementation("androidx.compose.material3:material3:1.2.0-alpha12")
implementation("androidx.compose.material3:material3:1.1.2")
// Retrofit
implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0")
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("io.coil-kt:coil-compose:2.5.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
implementation("com.jcraft:jsch:0.1.55")
// Room
implementation("androidx.room:room-runtime:2.6.1")
annotationProcessor("androidx.room:room-compiler:2.6.1")
ksp("androidx.room:room-compiler:2.6.1")
implementation("androidx.room:room-ktx:2.6.1")
implementation("androidx.room:room-paging:2.6.1")
val room_version = "2.6.1"
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")

View File

@ -7,7 +7,8 @@
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="com.google.android.gms.persmission.AD_ID" />
<application
android:allowBackup="true"
android:name=".CoffeeApplication"
android:allowBackup="false"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
@ -17,11 +18,12 @@
android:theme="@style/Theme.CoffeePreorder"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:name=".MainComposeActivity"
android:exported="true"
android:theme="@style/Theme.CoffeePreorder">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

View File

@ -13,7 +13,7 @@ import androidx.compose.ui.tooling.preview.Preview
import com.zyzf.coffeepreorder.ui.navigation.MainNavbar
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
class MainActivity : ComponentActivity() {
class MainComposeActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
@ -27,18 +27,4 @@ class MainActivity : ComponentActivity() {
}
}
}
}
@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 MainNavbarPreview() {
CoffeePreorderTheme {
Surface(
color = MaterialTheme.colorScheme.background
) {
MainNavbar()
}
}
}

View File

@ -16,8 +16,7 @@ data class Cart(
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Cart
if (uid != other.uid) return false
return true
return uid == other.uid
}
override fun hashCode(): Int {

View File

@ -19,11 +19,11 @@ data class Coffee(
@PrimaryKey(autoGenerate = true)
val uid: Int?,
@ColumnInfo(name = "name")
val name: String,
var name: String,
@ColumnInfo(name = "cost")
val cost: Double,
var cost: Double,
@ColumnInfo(name = "ingredients")
val ingredients: String,
var ingredients: String,
@ColumnInfo(name = "cart_id", index = true)
val cartId: Int?,
@ColumnInfo(name = "count")

View File

@ -30,8 +30,7 @@ data class UserLogined(
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as UserLogined
if (uid != other.uid) return false
return true
return uid == other.uid
}
override fun hashCode(): Int {

View File

@ -6,16 +6,12 @@ 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)
}
}
}

View File

@ -79,6 +79,7 @@ fun CoffeeList(
val coffeeListUiState = viewModel.coffeeListUiState.collectAsState()
val sheetState = rememberModalBottomSheetState()
val openDialog = remember { mutableStateOf(false) }
val coffee = remember { mutableStateOf(Coffee.getCoffee()) }
val photoPicker = rememberLauncherForActivityResult(
contract = ActivityResultContracts.PickVisualMedia()
) {
@ -95,7 +96,8 @@ fun CoffeeList(
FloatingActionButton(
onClick = {
coroutineScope.launch {
viewModel.cleanCurrentCoffee()
coffee.value = Coffee.getCoffee()
openDialog.value = true
}
},
Modifier
@ -109,7 +111,7 @@ fun CoffeeList(
}
}
) { innerPadding ->
coffeeListUiState?.value?.coffeeList?.let {
coffeeListUiState.value.coffeeList.let {
CoffeeList(
modifier = Modifier
.padding(innerPadding)
@ -120,15 +122,16 @@ fun CoffeeList(
viewModel.addCoffeeToCart(coffeeUid = coffeeUid)
}
},
onEditClick = { coffee: Coffee ->
onEditClick = { curcoffee: Coffee ->
coroutineScope.launch {
viewModel.editCoffee(coffee = coffee)
coffee.value = curcoffee
openDialog.value = true
}
}
)
}
AddEditModalBottomSheet(
coffee = viewModel.currentCoffee,
coffee = coffee,
sheetState = sheetState,
openDialog = openDialog,
onAddClick = { coffee: Coffee, context: Context ->
@ -155,7 +158,7 @@ fun CoffeeList(
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun AddEditModalBottomSheet(
coffee: Coffee,
coffee: MutableState<Coffee>,
sheetState: SheetState,
openDialog: MutableState<Boolean>,
onAddClick: (coffee: Coffee, context: Context) -> Unit,
@ -165,9 +168,12 @@ private fun AddEditModalBottomSheet(
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) }
var name: String by remember { mutableStateOf("")}
var cost: Double by remember { mutableDoubleStateOf(0.0) }
var ingredients: String by remember { mutableStateOf("")}
name = coffee.value.name
cost = coffee.value.cost
ingredients = coffee.value.ingredients
val context = LocalContext.current
if (openDialog.value) {
ModalBottomSheet(
@ -227,28 +233,28 @@ private fun AddEditModalBottomSheet(
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)
.data(if (coffee.value.uid != 0) "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 (coffee.uid == 0) {
if (coffee.value.uid == 0) {
Button(onClick = {
onAddClick(coffee, context)
onAddClick(Coffee(coffee.value.uid, name, cost, ingredients, coffee.value.cartId, coffee.value.count), context)
openDialog.value = false
}, modifier = Modifier.padding(0.dp, 10.dp, 0.dp, 30.dp)) {
Text("Добавить")
}
} else {
Button(onClick = {
onEditClick(coffee, context)
onEditClick(Coffee(coffee.value.uid, name, cost, ingredients, coffee.value.cartId, coffee.value.count), 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)
onDeleteClick(coffee.value)
openDialog.value = false
}, modifier = Modifier.padding(0.dp, 10.dp, 0.dp, 30.dp)) {
Text("Удалить")

View File

@ -41,19 +41,12 @@ class CoffeeListViewModel(
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)

View File

@ -1,6 +1,5 @@
package com.zyzf.coffeepreorder.ui.login
package com.zyzf.coffeepreorder.composeui
import android.annotation.SuppressLint
import android.content.res.Configuration
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
@ -17,15 +16,13 @@ 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.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -37,104 +34,106 @@ 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.database.AppDatabase
import com.zyzf.coffeepreorder.database.model.User
import com.zyzf.coffeepreorder.ui.navigation.Screen
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
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
@OptIn(DelicateCoroutinesApi::class)
@Composable
fun Login(
navController: NavController?,
viewModel: LoginViewModel = viewModel(factory = AppViewModelProvider.Factory)
) {
val coroutineScope = rememberCoroutineScope()
val userListUiState = viewModel.userListUiState.collectAsState()
fun Login(navController: NavController?) {
val context = LocalContext.current
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)
)
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 = {
coroutineScope.launch {
if (viewModel.tryLogin(login, password)) {
navController?.navigate(Screen.CoffeeList.route)
} else {
password = ""
login = "Неверный логин или пароль"
}
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
)
) {
Icon(
imageVector = Icons.Default.Add,
contentDescription = "Favorite",
modifier = Modifier.size(20.dp)
)
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
Text(text = "Зарегистрироваться")
}
}
},
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 = "Зарегистрироваться")
}
}
}

View File

@ -1,36 +0,0 @@
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

@ -4,7 +4,7 @@ import android.content.res.Configuration
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
@ -30,9 +30,9 @@ import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.zyzf.coffeepreorder.R
import com.zyzf.coffeepreorder.composeui.Login
import com.zyzf.coffeepreorder.ui.cart.Cart
import com.zyzf.coffeepreorder.ui.coffee.CoffeeList
import com.zyzf.coffeepreorder.ui.login.Login
import com.zyzf.coffeepreorder.ui.order.Order
import com.zyzf.coffeepreorder.ui.profile.Profile
import com.zyzf.coffeepreorder.ui.register.Register
@ -45,7 +45,7 @@ fun Topbar(
currentScreen: Screen?
) {
TopAppBar(
colors = TopAppBarDefaults.mediumTopAppBarColors(
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.primary,
titleContentColor = MaterialTheme.colorScheme.onPrimary,
),
@ -59,7 +59,7 @@ fun Topbar(
) {
IconButton(onClick = { navController.navigateUp() }) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
imageVector = Icons.Filled.ArrowBack,
contentDescription = null,
tint = MaterialTheme.colorScheme.onPrimary
)
@ -115,6 +115,7 @@ fun Navhost(
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MainNavbar() {
val navController = rememberNavController()
@ -134,17 +135,4 @@ fun MainNavbar() {
) { innerPadding ->
Navhost(navController, innerPadding)
}
}
@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 MainNavbarPreview() {
CoffeePreorderTheme {
Surface(
color = MaterialTheme.colorScheme.background
) {
MainNavbar()
}
}
}

View File

@ -32,21 +32,18 @@ enum class Screen(
),
Order(
"order", R.string.coffee_order, Icons.Filled.ShoppingCart
),
CoffeeView(
"coffee-view/{id}", R.string.coffee_view_title, showInBottomBar = false
);
companion object {
val bottomBarItems = listOf(
CoffeeList,
Profile,
Cart,
Cart
)
fun getItem(route: String): Screen? {
val findRoute = route.split("/").first()
return entries.find { value -> value.route.startsWith(findRoute) }
return values().find { value -> value.route.startsWith(findRoute) }
}
}
}