diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml new file mode 100644 index 0000000..931c4b7 --- /dev/null +++ b/.idea/deploymentTargetDropDown.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 0ad17cb..8978d23 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ - diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 7d5eea7..f717f1e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -60,16 +60,22 @@ dependencies { implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2") // UI - implementation("androidx.activity:activity-compose:1.8.0") + implementation("androidx.activity:activity-compose:1.8.1") implementation(platform("androidx.compose:compose-bom:2023.10.01")) - implementation("androidx.navigation:navigation-compose:2.7.4") - implementation("androidx.compose.ui:ui:1.6.0-alpha08") - implementation("androidx.compose.ui:ui-graphics:1.6.0-alpha08") - implementation("androidx.compose.ui:ui-tooling-preview:1.6.0-alpha08") - implementation("androidx.compose.material3:material3:1.2.0-alpha10") + implementation("androidx.navigation:navigation-compose:2.7.5") + implementation("androidx.compose.ui:ui:1.6.0-beta01") + implementation("androidx.compose.ui:ui-graphics:1.6.0-beta01") + implementation("androidx.compose.ui:ui-tooling-preview:1.6.0-beta01") + implementation("androidx.compose.material3:material3:1.2.0-alpha11") - implementation("io.coil-kt:coil-compose:2.4.0") + // 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.1") + implementation("com.jcraft:jsch:0.1.55") + // Room implementation("androidx.room:room-runtime:2.6.0") annotationProcessor("androidx.room:room-compiler:2.6.0") @@ -82,9 +88,9 @@ dependencies { androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") androidTestImplementation(platform("androidx.compose:compose-bom:2023.10.01")) - androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.6.0-alpha08") - debugImplementation("androidx.compose.ui:ui-tooling:1.6.0-alpha08") - debugImplementation("androidx.compose.ui:ui-test-manifest:1.6.0-alpha08") + androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.6.0-beta01") + debugImplementation("androidx.compose.ui:ui-tooling:1.6.0-beta01") + debugImplementation("androidx.compose.ui:ui-test-manifest:1.6.0-beta01") } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 78fcc00..7338f2b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,6 +4,8 @@ + + - diff --git a/app/src/main/java/com/zyzf/coffeepreorder/coffee/composeui/CoffeeList.kt b/app/src/main/java/com/zyzf/coffeepreorder/coffee/composeui/CoffeeList.kt index d522c2b..f19ad6e 100644 --- a/app/src/main/java/com/zyzf/coffeepreorder/coffee/composeui/CoffeeList.kt +++ b/app/src/main/java/com/zyzf/coffeepreorder/coffee/composeui/CoffeeList.kt @@ -2,13 +2,23 @@ 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.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.heightIn @@ -17,6 +27,7 @@ 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 @@ -54,17 +65,31 @@ 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) +@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class, + DelicateCoroutinesApi::class +) @Composable fun CoffeeList(navController: NavController?) { val openDialog = remember { mutableStateOf(false) } @@ -72,6 +97,20 @@ fun CoffeeList(navController: NavController?) { val coffee = remember { mutableStateOf(Coffee("", 0.0, "", null,0)) } val context = LocalContext.current val itemsList = remember { mutableStateListOf() } + + 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 -> @@ -90,13 +129,15 @@ fun CoffeeList(navController: NavController?) { horizontalArrangement = Arrangement.SpaceAround) { AsyncImage( - model = ImageRequest.Builder(context = LocalContext.current).data("http://109.197.199.134/s/zXgFRTmbR4KMxMH/download?path=%2F&files=coffee_image" + coffee.value.uid + ".png") + 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.fillMaxHeight().weight(1f) + modifier = Modifier + .size(100.dp) + .clip(RoundedCornerShape(50.dp)) ) Column( @@ -187,26 +228,66 @@ fun CoffeeList(navController: NavController?) { }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal) ) - OutlinedTextField(modifier = Modifier.fillMaxWidth(), + 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) { - AppDatabase.getInstance(context).coffeeDao().insert( - Coffee(name = name, cost = cost, ingredients = ingredients, cartId = null, 0) + 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) } } - }) { + }, modifier = Modifier.padding(0.dp, 10.dp, 0.dp, 30.dp)) { Text("Добавить") } } else { @@ -221,7 +302,7 @@ fun CoffeeList(navController: NavController?) { } } - }) { + }, modifier = Modifier.padding(0.dp, 10.dp, 0.dp, 30.dp)) { Text("Изменить") } Spacer(modifier = Modifier.padding(all = 20.dp)) @@ -236,7 +317,7 @@ fun CoffeeList(navController: NavController?) { } } - }) { + }, modifier = Modifier.padding(0.dp, 10.dp, 0.dp, 30.dp)) { Text("Удалить") } } @@ -246,6 +327,48 @@ fun CoffeeList(navController: NavController?) { } } +val REMOTE_HOST = "109.197.199.134" +val USERNAME = "zyzf" +val PASSWORD = "250303Zyzf-d-grad" +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 diff --git a/app/src/main/java/com/zyzf/coffeepreorder/composeui/Login.kt b/app/src/main/java/com/zyzf/coffeepreorder/composeui/Login.kt index 6d05c4d..50383be 100644 --- a/app/src/main/java/com/zyzf/coffeepreorder/composeui/Login.kt +++ b/app/src/main/java/com/zyzf/coffeepreorder/composeui/Login.kt @@ -42,11 +42,13 @@ 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 @@ -59,14 +61,15 @@ fun Login(navController: NavController?) { } Column(Modifier.padding(all = 10.dp), horizontalAlignment = Alignment.CenterHorizontally) { AsyncImage( - model = ImageRequest.Builder(context = LocalContext.current).data("http://109.197.199.134/s/zXgFRTmbR4KMxMH/download?path=%2F&files=coffee_image.png") + 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 + modifier = Modifier.size(100.dp) ) + Spacer(modifier = Modifier.padding(all = 20.dp)) OutlinedTextField(modifier = Modifier.fillMaxWidth(), value = login, onValueChange = {login = it}, diff --git a/app/src/main/java/com/zyzf/coffeepreorder/database/AppDatabase.kt b/app/src/main/java/com/zyzf/coffeepreorder/database/AppDatabase.kt index 5c6094d..9ee6171 100644 --- a/app/src/main/java/com/zyzf/coffeepreorder/database/AppDatabase.kt +++ b/app/src/main/java/com/zyzf/coffeepreorder/database/AppDatabase.kt @@ -47,17 +47,17 @@ abstract class AppDatabase : RoomDatabase() { val coffee9 = Coffee("Coffee9", 200.0, "Ing1", null, 0) val coffee10 = Coffee("Coffee10", 900.0, "Ing1", null, 0) val coffee11 = Coffee("Coffee11", 200.0, "Ing1", null, 0) - coffeeDao.insert(coffee1) - coffeeDao.insert(coffee2) - coffeeDao.insert(coffee3) - coffeeDao.insert(coffee4) - coffeeDao.insert(coffee5) - coffeeDao.insert(coffee6) - coffeeDao.insert(coffee7) - coffeeDao.insert(coffee8) - coffeeDao.insert(coffee9) - coffeeDao.insert(coffee10) - coffeeDao.insert(coffee11) + coffeeDao.insert(coffee1.name, coffee1.cost, coffee1.ingredients) + coffeeDao.insert(coffee2.name, coffee2.cost, coffee2.ingredients) + coffeeDao.insert(coffee3.name, coffee3.cost, coffee3.ingredients) + coffeeDao.insert(coffee4.name, coffee4.cost, coffee4.ingredients) + coffeeDao.insert(coffee5.name, coffee5.cost, coffee5.ingredients) + coffeeDao.insert(coffee6.name, coffee6.cost, coffee6.ingredients) + coffeeDao.insert(coffee7.name, coffee7.cost, coffee7.ingredients) + coffeeDao.insert(coffee8.name, coffee8.cost, coffee8.ingredients) + coffeeDao.insert(coffee9.name, coffee9.cost, coffee9.ingredients) + coffeeDao.insert(coffee10.name, coffee10.cost, coffee10.ingredients) + coffeeDao.insert(coffee11.name, coffee11.cost, coffee11.ingredients) // Cart val cartDao = database.cartDao() val cart = Cart() diff --git a/app/src/main/java/com/zyzf/coffeepreorder/database/dao/CoffeeDao.kt b/app/src/main/java/com/zyzf/coffeepreorder/database/dao/CoffeeDao.kt index d739d78..fd7fd74 100644 --- a/app/src/main/java/com/zyzf/coffeepreorder/database/dao/CoffeeDao.kt +++ b/app/src/main/java/com/zyzf/coffeepreorder/database/dao/CoffeeDao.kt @@ -2,7 +2,6 @@ package com.zyzf.coffeepreorder.database.dao import androidx.room.Dao import androidx.room.Delete -import androidx.room.Insert import androidx.room.Query import androidx.room.Update import com.zyzf.coffeepreorder.database.model.Coffee @@ -17,8 +16,8 @@ interface CoffeeDao { @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 - @Insert - suspend fun insert(coffee: Coffee) + @Query("insert into coffee (name, cost, ingredients, count) values (:name, :cost, :ingredients, 0)") + suspend fun insert(name: String, cost: Double, ingredients: String): Long @Update suspend fun update(coffee: Coffee) diff --git a/app/src/main/res/drawable/img.xml b/app/src/main/res/drawable/img.xml new file mode 100644 index 0000000..a8b409b --- /dev/null +++ b/app/src/main/res/drawable/img.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 05aec1c..fc2b935 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,6 +1,6 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { - id("com.android.application") version "8.1.2" apply false + id("com.android.application") version "8.1.3" apply false id("org.jetbrains.kotlin.android") version "1.9.10" apply false id("com.google.devtools.ksp") version "1.9.10-1.0.13" apply false } \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index c5472bc..93fbf89 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -10,6 +10,7 @@ dependencyResolutionManagement { repositories { google() mavenCentral() + maven { setUrl("https://jitpack.io") } } }