now able to edit and delete server

This commit is contained in:
Gary Wang 2021-11-22 00:01:21 +08:00
parent 8ab2f03a1b
commit 9c9bdb9435
9 changed files with 189 additions and 11 deletions

View File

@ -5,7 +5,10 @@ import android.util.Log
import androidx.lifecycle.* import androidx.lifecycle.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import net.blumia.pcmdroid.model.Folder import net.blumia.pcmdroid.model.Folder
import net.blumia.pcmdroid.model.Server import net.blumia.pcmdroid.model.Server
import net.blumia.pcmdroid.model.Song import net.blumia.pcmdroid.model.Song
@ -46,6 +49,7 @@ class MainViewModel(private val repository: ServerRepository) : ViewModel() {
val songs: LiveData<List<Song>> = _songs val songs: LiveData<List<Song>> = _songs
fun fetchServer(srv: Server) { fun fetchServer(srv: Server) {
setCurrentServer(srv)
viewModelScope.launch(context = Dispatchers.IO) { viewModelScope.launch(context = Dispatchers.IO) {
val formBody = FormBody.Builder() val formBody = FormBody.Builder()
@ -53,6 +57,7 @@ class MainViewModel(private val repository: ServerRepository) : ViewModel() {
.build() .build()
val request = Request.Builder() val request = Request.Builder()
// .url("http://localhost/")
.url(srv.url) .url(srv.url)
.post(formBody) .post(formBody)
.build() .build()
@ -65,7 +70,7 @@ class MainViewModel(private val repository: ServerRepository) : ViewModel() {
} }
val pair = parseFromJsonString(response.body!!.string()) val pair = parseFromJsonString(response.body!!.string())
_drawerFolders.postValue(pair.first) _drawerFolders.postValue(pair.first)
setCurrentServer(srv) // setCurrentServer(srv)
Log.d("vvv", drawerFolders.value.toString()) Log.d("vvv", drawerFolders.value.toString())
} }
} catch (e: java.io.IOException) { } catch (e: java.io.IOException) {
@ -107,6 +112,24 @@ class MainViewModel(private val repository: ServerRepository) : ViewModel() {
} }
} }
} }
fun getServerById(id: Long): Server {
return runBlocking {
repository.serverById(id).first()
}
}
fun editServer(srv: Server) {
runBlocking {
repository.update(srv)
}
}
fun deleteServer(srv: Server) {
runBlocking {
repository.delete(srv)
}
}
} }
class MainViewModelFactory(private val repository: ServerRepository) : ViewModelProvider.Factory { class MainViewModelFactory(private val repository: ServerRepository) : ViewModelProvider.Factory {

View File

@ -1,20 +1,30 @@
package net.blumia.pcmdroid.model package net.blumia.pcmdroid.model
import androidx.lifecycle.LiveData
import androidx.room.* import androidx.room.*
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@Entity(tableName = "server_table") @Entity(tableName = "server_table")
data class Server ( data class Server (
@PrimaryKey @ColumnInfo(name = "url") @PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "serverId")
val serverId: Long? = null,
@ColumnInfo(name = "url")
val url: String, val url: String,
@ColumnInfo(name = "name") @ColumnInfo(name = "name")
val name: String, val name: String,
@ColumnInfo(name = "shortName")
val shortName: String,
@ColumnInfo(name = "baseFolderName") @ColumnInfo(name = "baseFolderName")
val baseFolderName: String, val baseFolderName: String,
) { ) {
fun displayName(): String { fun displayName(): String {
return name return name
} }
fun shortDisplayName(): String {
return if (shortName.isNotEmpty()) shortName else name
}
} }
@Dao @Dao
@ -23,9 +33,15 @@ interface ServerDao {
@Query("SELECT * FROM server_table") // ORDER BY wtf ASC @Query("SELECT * FROM server_table") // ORDER BY wtf ASC
fun getServers(): Flow<List<Server>> fun getServers(): Flow<List<Server>>
@Query("SELECT * FROM server_table WHERE serverId=:id")
fun getServerById(id: Long): Flow<Server>
@Insert(onConflict = OnConflictStrategy.IGNORE) @Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(server: Server) suspend fun insert(server: Server)
@Update
suspend fun update(server: Server)
@Delete @Delete
suspend fun delete(server: Server) suspend fun delete(server: Server)

View File

@ -21,4 +21,22 @@ class ServerRepository(private val serverDao: ServerDao) {
suspend fun insert(server: Server) { suspend fun insert(server: Server) {
serverDao.insert(server) serverDao.insert(server)
} }
@Suppress("RedundantSuspendModifier")
@WorkerThread
suspend fun update(server: Server) {
serverDao.update(server)
}
@Suppress("RedundantSuspendModifier")
@WorkerThread
suspend fun delete(server: Server) {
serverDao.delete(server)
}
@Suppress("RedundantSuspendModifier")
@WorkerThread
fun serverById(id: Long): Flow<Server> {
return serverDao.getServerById(id)
}
} }

View File

@ -1,23 +1,29 @@
package net.blumia.pcmdroid.ui package net.blumia.pcmdroid.ui
import android.util.Log
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.lifecycle.MutableLiveData
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.rememberNavController import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import net.blumia.pcmdroid.MainViewModel import net.blumia.pcmdroid.MainViewModel
import net.blumia.pcmdroid.model.Folder import net.blumia.pcmdroid.model.Folder
import net.blumia.pcmdroid.model.Server import net.blumia.pcmdroid.model.Server
import net.blumia.pcmdroid.model.Song import net.blumia.pcmdroid.model.Song
import net.blumia.pcmdroid.ui.screen.addserver.AddServerScreen import net.blumia.pcmdroid.ui.screen.addserver.AddServerScreen
import net.blumia.pcmdroid.ui.screen.editserver.EditServerScreen
import net.blumia.pcmdroid.ui.screen.main.MainPlayer import net.blumia.pcmdroid.ui.screen.main.MainPlayer
import net.blumia.pcmdroid.ui.screen.settings.SettingsScreen import net.blumia.pcmdroid.ui.screen.settings.SettingsScreen
object MainDestinations { object MainDestinations {
const val MAIN_ROUTE = "main" const val MAIN_ROUTE = "main"
const val ADD_SERVER_ROUTE = "add_server" const val ADD_SERVER_ROUTE = "add_server"
const val EDIT_SERVER_ROUTE = "edit_server"
const val SETTINGS_ROUTE = "settings" const val SETTINGS_ROUTE = "settings"
} }
@ -56,9 +62,15 @@ fun NavGraph(
onFolderItemClicked = { folder -> onFolderItemClicked = { folder ->
viewModel.fetchFolder(folder) viewModel.fetchFolder(folder)
}, },
onEditServerActionTriggered = { server ->
navController.navigate( "${MainDestinations.EDIT_SERVER_ROUTE}?id=${server.serverId}")
},
onDeleteServerActionTriggered = { server ->
viewModel.deleteServer(server)
},
settingsActionTriggered = { settingsActionTriggered = {
navController.navigate(MainDestinations.SETTINGS_ROUTE) navController.navigate(MainDestinations.SETTINGS_ROUTE)
} },
) )
} }
@ -81,5 +93,23 @@ fun NavGraph(
} }
) )
} }
composable(
"${MainDestinations.EDIT_SERVER_ROUTE}?id={id}",
arguments = listOf(navArgument("id") { type = NavType.LongType })
) {
val srv = viewModel.getServerById(it.arguments?.getLong("id")!!)
EditServerScreen(
server = srv,
onCloseActionTriggered = {
navController.navigateUp()
},
onSaveButtonTriggered = { server ->
viewModel.editServer(server)
navController.navigateUp()
},
)
}
} }
} }

View File

@ -67,6 +67,8 @@ fun StepEnterApiUrl(
fun StepOtherDetail( fun StepOtherDetail(
nameStr: String = "", nameStr: String = "",
onNameStrChange: (String) -> Unit = {}, onNameStrChange: (String) -> Unit = {},
shortNameStr: String = "",
onShortNameStrChange: (String) -> Unit = {},
baseFolderNameStr: String = "", baseFolderNameStr: String = "",
onBaseFolderNameStrChange: (String) -> Unit = {}, onBaseFolderNameStrChange: (String) -> Unit = {},
) { ) {
@ -74,6 +76,15 @@ fun StepOtherDetail(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
) { ) {
OutlinedTextField(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 10.dp),
value = shortNameStr,
onValueChange = onShortNameStrChange,
label = { Text("Server short name") },
)
OutlinedTextField( OutlinedTextField(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@ -137,6 +148,7 @@ fun AddServerScreen(
) { ) {
var urlStr by rememberSaveable { mutableStateOf("") } var urlStr by rememberSaveable { mutableStateOf("") }
var nameStr by rememberSaveable { mutableStateOf("") } var nameStr by rememberSaveable { mutableStateOf("") }
var shortNameStr by rememberSaveable { mutableStateOf("") }
var baseFolderNameStr by rememberSaveable { mutableStateOf("") } var baseFolderNameStr by rememberSaveable { mutableStateOf("") }
Column( Column(
@ -160,6 +172,10 @@ fun AddServerScreen(
onNameStrChange = { value -> onNameStrChange = { value ->
nameStr = value nameStr = value
}, },
shortNameStr,
onShortNameStrChange = { value ->
shortNameStr = value
},
baseFolderNameStr, baseFolderNameStr,
onBaseFolderNameStrChange = { value -> onBaseFolderNameStrChange = { value ->
baseFolderNameStr = value baseFolderNameStr = value
@ -189,6 +205,7 @@ fun AddServerScreen(
val srv = Server( val srv = Server(
url = urlStr, url = urlStr,
name = nameStr, name = nameStr,
shortName = shortNameStr,
baseFolderName = baseFolderNameStr, baseFolderName = baseFolderNameStr,
) )
onSubmitServerTriggered(srv) onSubmitServerTriggered(srv)

View File

@ -19,11 +19,13 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import net.blumia.pcmdroid.model.Server
@Preview(showBackground = true)
@Composable @Composable
fun EditServerScreen( fun EditServerScreen(
server: Server,
onCloseActionTriggered: () -> Unit = {}, onCloseActionTriggered: () -> Unit = {},
onSaveButtonTriggered: (Server) -> Unit = {},
) )
{ {
Scaffold( Scaffold(
@ -68,9 +70,19 @@ fun EditServerScreen(
style = MaterialTheme.typography.subtitle2, style = MaterialTheme.typography.subtitle2,
) )
var nameStr by rememberSaveable { mutableStateOf("") } var shortNameStr by rememberSaveable { mutableStateOf(server.shortName) }
var urlStr by rememberSaveable { mutableStateOf("") } var nameStr by rememberSaveable { mutableStateOf(server.name) }
var baseFolderNameStr by rememberSaveable { mutableStateOf("") } var urlStr by rememberSaveable { mutableStateOf(server.url) }
var baseFolderNameStr by rememberSaveable { mutableStateOf(server.baseFolderName) }
OutlinedTextField(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 10.dp),
value = shortNameStr,
onValueChange = { value -> shortNameStr = value},
label = { Text("Server Short Name") },
)
OutlinedTextField( OutlinedTextField(
modifier = Modifier modifier = Modifier
@ -101,6 +113,38 @@ fun EditServerScreen(
onValueChange = { value -> baseFolderNameStr = value}, onValueChange = { value -> baseFolderNameStr = value},
label = { Text("Base folder name") }, label = { Text("Base folder name") },
) )
Button(
modifier = Modifier.fillMaxWidth(),
onClick = {
onSaveButtonTriggered(
Server(
serverId = server.serverId,
name = nameStr,
shortName = shortNameStr,
url = urlStr,
baseFolderName = baseFolderNameStr,
)
)
},
) {
Text("Save")
}
} }
} }
} }
@Preview(showBackground = true)
@Composable
fun EditServerScreenPreview()
{
EditServerScreen(
Server(
-1,
"http://localhost/api.cgi",
"Demo server",
"Demo",
"demo",
)
)
}

View File

@ -82,6 +82,8 @@ fun MainPlayer(
addServerActionTriggered: () -> Unit = {}, addServerActionTriggered: () -> Unit = {},
onServerItemIconClicked: (Server) -> Unit = {}, onServerItemIconClicked: (Server) -> Unit = {},
onFolderItemClicked: (Folder) -> Unit = {}, onFolderItemClicked: (Folder) -> Unit = {},
onEditServerActionTriggered: (Server) -> Unit = {},
onDeleteServerActionTriggered: (Server) -> Unit = {},
settingsActionTriggered: () -> Unit = {}, settingsActionTriggered: () -> Unit = {},
) { ) {
val bottomDrawerState = rememberBottomDrawerState(initialValue = BottomDrawerValue.Closed) val bottomDrawerState = rememberBottomDrawerState(initialValue = BottomDrawerValue.Closed)
@ -91,7 +93,21 @@ fun MainPlayer(
gesturesEnabled = bottomDrawerState.isOpen, gesturesEnabled = bottomDrawerState.isOpen,
drawerContent = { drawerContent = {
if (currentServer != null) { if (currentServer != null) {
ServerSummaryDrawer(currentServer) ServerSummaryDrawer(
server = currentServer,
onEditActionTriggered = {
onEditServerActionTriggered(currentServer)
bottomDrawerScope.launch {
bottomDrawerState.close()
}
},
onDeleteActionTriggered = {
onDeleteServerActionTriggered(currentServer)
bottomDrawerScope.launch {
bottomDrawerState.close()
}
},
)
} else { } else {
Text( Text(
"TODO: play control and playlist?", "TODO: play control and playlist?",

View File

@ -39,11 +39,10 @@ fun RowScope.ActionIcon(
} }
} }
@Preview(showBackground = true)
@OptIn(ExperimentalMaterialApi::class) @OptIn(ExperimentalMaterialApi::class)
@Composable @Composable
fun ServerSummaryDrawer( fun ServerSummaryDrawer(
server: Server = Server("http://localhost/", "Demo server", "pcm"), server: Server,
onShareActionTriggered: (Server) -> Unit = {}, onShareActionTriggered: (Server) -> Unit = {},
onEditActionTriggered: (Server) -> Unit = {}, onEditActionTriggered: (Server) -> Unit = {},
onDeleteActionTriggered: (Server) -> Unit = {}, onDeleteActionTriggered: (Server) -> Unit = {},
@ -115,3 +114,18 @@ fun ServerSummaryDrawer(
) )
} }
} }
@Preview(showBackground = true)
@Composable
fun ServerSummaryDrawerPreview()
{
ServerSummaryDrawer(
Server(
-1,
"http://localhost/",
"Demo server",
"Demo",
"pcm"
)
)
}

View File

@ -46,7 +46,7 @@ fun SidebarContent(
) )
}, },
label = { label = {
Text(server.displayName()) Text(server.shortDisplayName())
}, },
selected = server.url == currentServerApiUrl, selected = server.url == currentServerApiUrl,
onClick = { onClick = {