Compare commits
2 Commits
a6105c9ced
...
6e4aa55266
| Author | SHA1 | Date |
|---|---|---|
|
|
6e4aa55266 | |
|
|
ef1353e82b |
|
|
@ -0,0 +1,55 @@
|
|||
package xyz.pixelatedw.recipe.ui.components
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import xyz.pixelatedw.recipe.MainActivity
|
||||
import xyz.pixelatedw.recipe.utils.sync
|
||||
|
||||
@Composable
|
||||
fun ImportDropdownMenu(ctx: MainActivity, modifier: Modifier) {
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
Box(
|
||||
modifier = modifier,
|
||||
) {
|
||||
IconButton(
|
||||
onClick = { expanded = !expanded }
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Add,
|
||||
contentDescription = "Import recipes"
|
||||
)
|
||||
}
|
||||
DropdownMenu(
|
||||
expanded = expanded,
|
||||
onDismissRequest = { expanded = false }
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = { Text("Import") },
|
||||
onClick = {
|
||||
ctx.sourceChooser.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE))
|
||||
expanded = false
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text("Sync") },
|
||||
onClick = {
|
||||
sync(ctx)
|
||||
expanded = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
package xyz.pixelatedw.recipe.ui.components
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.compose.foundation.background
|
||||
import android.content.Context
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
|
|
@ -10,15 +9,19 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
|
|
@ -27,6 +30,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
|
|
@ -35,9 +39,11 @@ import xyz.pixelatedw.recipe.MainActivity
|
|||
import xyz.pixelatedw.recipe.R
|
||||
import xyz.pixelatedw.recipe.data.RecipeWithTags
|
||||
import xyz.pixelatedw.recipe.data.RecipesView
|
||||
import xyz.pixelatedw.recipe.utils.sync
|
||||
import xyz.pixelatedw.recipe.utils.DEFAULT_SYNC_SERVER_IP
|
||||
import xyz.pixelatedw.recipe.utils.DEFAULT_SYNC_SERVER_PORT
|
||||
import java.io.File
|
||||
|
||||
|
||||
@Composable
|
||||
fun MainScreen(ctx: MainActivity, padding: PaddingValues, view: RecipesView) {
|
||||
val recipes = view.recipes.collectAsState()
|
||||
|
|
@ -45,6 +51,11 @@ fun MainScreen(ctx: MainActivity, padding: PaddingValues, view: RecipesView) {
|
|||
val search = view.search.collectAsState()
|
||||
val filters = view.tagFilters.collectAsState()
|
||||
|
||||
val prefs = ctx.getPreferences(Context.MODE_PRIVATE)
|
||||
|
||||
var syncIp by remember { mutableStateOf(prefs.getString("syncServerIp", DEFAULT_SYNC_SERVER_IP)!!) }
|
||||
var syncPort by remember { mutableIntStateOf(prefs.getInt("syncServerPort", DEFAULT_SYNC_SERVER_PORT)) }
|
||||
|
||||
val navController = rememberNavController()
|
||||
|
||||
var openDeletionDialog by remember { mutableStateOf(false) }
|
||||
|
|
@ -101,13 +112,15 @@ fun MainScreen(ctx: MainActivity, padding: PaddingValues, view: RecipesView) {
|
|||
horizontalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
OutlinedTextField(
|
||||
modifier = Modifier.weight(0.7f),
|
||||
modifier = Modifier
|
||||
.weight(0.7f)
|
||||
.padding(end = 4.dp),
|
||||
value = search.value.orEmpty(),
|
||||
onValueChange = { search -> view.setSearch(search) },
|
||||
)
|
||||
// Tags
|
||||
IconButton(
|
||||
modifier = Modifier.weight(0.15f),
|
||||
modifier = Modifier.weight(0.1f),
|
||||
onClick = { openTagFilterDialog = true },
|
||||
) {
|
||||
Icon(
|
||||
|
|
@ -115,10 +128,10 @@ fun MainScreen(ctx: MainActivity, padding: PaddingValues, view: RecipesView) {
|
|||
contentDescription = "Toggle tags filtering menu"
|
||||
)
|
||||
}
|
||||
// Load / Delete
|
||||
// Import / Delete
|
||||
if (selectedEntries.isNotEmpty()) {
|
||||
IconButton(
|
||||
modifier = Modifier.weight(0.15f),
|
||||
modifier = Modifier.weight(0.1f),
|
||||
onClick = {
|
||||
openDeletionDialog = true
|
||||
},
|
||||
|
|
@ -130,16 +143,17 @@ fun MainScreen(ctx: MainActivity, padding: PaddingValues, view: RecipesView) {
|
|||
)
|
||||
}
|
||||
} else {
|
||||
IconButton(
|
||||
modifier = Modifier.weight(0.15f),
|
||||
onClick = { sync(ctx) },
|
||||
// onClick = { ctx.sourceChooser.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)) },
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Add,
|
||||
contentDescription = "Load recipes from filesystem"
|
||||
)
|
||||
}
|
||||
ImportDropdownMenu(ctx, Modifier.weight(0.1f))
|
||||
}
|
||||
// Options
|
||||
IconButton(
|
||||
modifier = Modifier.weight(0.1f),
|
||||
onClick = { navController.navigate("settings") },
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Settings,
|
||||
contentDescription = "Open settings menu"
|
||||
)
|
||||
}
|
||||
}
|
||||
LazyColumn {
|
||||
|
|
@ -202,5 +216,43 @@ fun MainScreen(ctx: MainActivity, padding: PaddingValues, view: RecipesView) {
|
|||
composable("info") {
|
||||
RecipeInfo(ctx, view, navController, padding, active.value!!)
|
||||
}
|
||||
composable("settings") {
|
||||
Column(modifier = Modifier.padding(start = 12.dp, top = 48.dp)) {
|
||||
OutlinedTextField(
|
||||
label = { Text("Sync Server IP") },
|
||||
singleLine = true,
|
||||
value = syncIp,
|
||||
onValueChange = { syncIp = it },
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
modifier = Modifier.padding(top = 8.dp),
|
||||
label = { Text("Sync Server IP") },
|
||||
singleLine = true,
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
||||
value = syncPort.toString(),
|
||||
onValueChange = {
|
||||
syncPort = when (it.toIntOrNull()) {
|
||||
null -> syncPort
|
||||
else -> it.toInt()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
Button(
|
||||
modifier = Modifier.padding(top = 8.dp),
|
||||
onClick = {
|
||||
with (prefs.edit()) {
|
||||
putString("syncServerIp", syncIp)
|
||||
putInt("syncServerPort", syncPort)
|
||||
apply()
|
||||
}
|
||||
navController.navigate("list")
|
||||
},
|
||||
) {
|
||||
Text("Save")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,10 @@
|
|||
package xyz.pixelatedw.recipe.utils
|
||||
|
||||
import android.os.Build
|
||||
import android.os.FileUtils
|
||||
import android.content.Context
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import xyz.pixelatedw.recipe.MainActivity
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.DataInputStream
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
|
|
@ -16,14 +13,20 @@ import java.nio.ByteBuffer
|
|||
import java.nio.charset.StandardCharsets
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
const val DEFAULT_SYNC_SERVER_IP = "192.168.0.100"
|
||||
const val DEFAULT_SYNC_SERVER_PORT = 9696
|
||||
|
||||
fun sync(ctx: MainActivity) {
|
||||
val executor = Executors.newSingleThreadExecutor()
|
||||
val handler = Handler(Looper.getMainLooper())
|
||||
|
||||
executor.execute(Runnable() {
|
||||
executor.execute {
|
||||
try {
|
||||
val conn = Socket("192.168.0.100", 9696)
|
||||
val prefs = ctx.getPreferences(Context.MODE_PRIVATE)
|
||||
val syncServerIp = prefs.getString("syncServerIp", DEFAULT_SYNC_SERVER_IP)
|
||||
val syncServerPort = prefs.getInt("syncServerPort", DEFAULT_SYNC_SERVER_PORT)
|
||||
|
||||
val conn = Socket(syncServerIp, syncServerPort)
|
||||
val stream = conn.getInputStream()
|
||||
|
||||
val inputStream = DataInputStream(stream)
|
||||
|
|
@ -31,16 +34,16 @@ fun sync(ctx: MainActivity) {
|
|||
var buffer = ByteArray(8)
|
||||
inputStream.read(buffer)
|
||||
val filesSent = ByteBuffer.wrap(buffer).getLong().toInt()
|
||||
// println("files sent: $filesSent")
|
||||
|
||||
for(f in 0..<filesSent) {
|
||||
for (f in 0..<filesSent) {
|
||||
buffer = ByteArray(8)
|
||||
inputStream.read(buffer)
|
||||
val nameBufLen = ByteBuffer.wrap(buffer).getLong().toInt()
|
||||
|
||||
buffer = ByteArray(nameBufLen)
|
||||
inputStream.read(buffer)
|
||||
val fileFullPath = StandardCharsets.UTF_8.decode(ByteBuffer.wrap(buffer)).toString();
|
||||
val fileFullPath =
|
||||
StandardCharsets.UTF_8.decode(ByteBuffer.wrap(buffer)).toString();
|
||||
val fileName = fileFullPath.split("/").last()
|
||||
val filePath = fileFullPath.replace(fileName, "")
|
||||
|
||||
|
|
@ -62,8 +65,7 @@ fun sync(ctx: MainActivity) {
|
|||
while (usedBytes != contentBufLen) {
|
||||
if (usedBytes + blockSize > contentBufLen) {
|
||||
blockSize = contentBufLen - usedBytes
|
||||
}
|
||||
else if (blockSize > contentBufLen) {
|
||||
} else if (blockSize > contentBufLen) {
|
||||
blockSize = contentBufLen
|
||||
}
|
||||
|
||||
|
|
@ -87,10 +89,10 @@ fun sync(ctx: MainActivity) {
|
|||
e.printStackTrace()
|
||||
}
|
||||
|
||||
handler.post(Runnable {
|
||||
handler.post {
|
||||
val recipes = ctx.db.recipeWithTagsDao().getAll()
|
||||
ctx.recipeView.setRecipes(recipes)
|
||||
println("syncing complete")
|
||||
})
|
||||
})
|
||||
// println("syncing complete")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue