a shabby viewmodel for addserver screen
This commit is contained in:
parent
a939e6b88a
commit
d19fd84244
@ -21,6 +21,7 @@ import net.blumia.pcmdroid.service.PlaybackService
|
||||
import net.blumia.pcmdroid.ui.NavGraph
|
||||
import net.blumia.pcmdroid.ui.screen.main.MainPlayer
|
||||
import net.blumia.pcmdroid.ui.theme.PrivateCloudMusicTheme
|
||||
import net.blumia.pcmdroid.viewmodel.AddServerViewModel
|
||||
import java.lang.Exception
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
@ -29,7 +30,10 @@ class MainActivity : ComponentActivity() {
|
||||
private val browser: MediaBrowser?
|
||||
get() = if (browserFuture.isDone) browserFuture.get() else null
|
||||
|
||||
private val model: MainViewModel by viewModels {
|
||||
private val mainViewModel: MainViewModel by viewModels {
|
||||
MainViewModelFactory((application as MainApplication).repository)
|
||||
}
|
||||
private val addServerViewModel: AddServerViewModel by viewModels {
|
||||
MainViewModelFactory((application as MainApplication).repository)
|
||||
}
|
||||
|
||||
@ -39,7 +43,7 @@ class MainActivity : ComponentActivity() {
|
||||
PrivateCloudMusicTheme {
|
||||
// A surface container using the 'background' color from the theme
|
||||
Surface(color = MaterialTheme.colors.background) {
|
||||
NavGraph(model, browser)
|
||||
NavGraph(mainViewModel, addServerViewModel, browser)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import net.blumia.pcmdroid.model.Server
|
||||
import net.blumia.pcmdroid.model.Song
|
||||
import net.blumia.pcmdroid.model.parseFromJsonString
|
||||
import net.blumia.pcmdroid.repository.ServerRepository
|
||||
import net.blumia.pcmdroid.viewmodel.AddServerViewModel
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
@ -137,6 +138,9 @@ class MainViewModelFactory(private val repository: ServerRepository) : ViewModel
|
||||
if (modelClass.isAssignableFrom(MainViewModel::class.java)) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return MainViewModel(repository) as T
|
||||
} else if (modelClass.isAssignableFrom(AddServerViewModel::class.java)) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return AddServerViewModel() as T
|
||||
}
|
||||
throw IllegalArgumentException("Unknown ViewModel class")
|
||||
}
|
||||
|
@ -1,8 +1,11 @@
|
||||
package net.blumia.pcmdroid.model
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.room.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
|
||||
@Entity(tableName = "server_table")
|
||||
data class Server (
|
||||
@ -51,4 +54,25 @@ interface ServerDao {
|
||||
|
||||
@Query("DELETE FROM server_table")
|
||||
suspend fun deleteAll()
|
||||
}
|
||||
|
||||
fun parseServerFromJsonString(jsonString: String): Map<String, String> {
|
||||
|
||||
val kv = mutableMapOf<String, String>()
|
||||
|
||||
try {
|
||||
val jsonObj = JSONObject(jsonString)
|
||||
val result = jsonObj.getJSONObject("result")
|
||||
|
||||
kv["serverName"] = result.getString("serverName")
|
||||
kv["serverShortName"] = result.getString("serverShortName")
|
||||
kv["baseFolderNameHint"] = result.getString("baseFolderNameHint")
|
||||
kv["preferredFormatsHint"] = result.getString("preferredFormatsHint")
|
||||
kv["mediaRootUrl"] = result.getString("mediaRootUrl")
|
||||
} catch (e: JSONException) {
|
||||
e.printStackTrace()
|
||||
Log.d("vvv", jsonString)
|
||||
}
|
||||
|
||||
return kv;
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package net.blumia.pcmdroid.ui
|
||||
|
||||
import android.util.Log
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
@ -13,7 +14,9 @@ import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.navArgument
|
||||
import net.blumia.pcmdroid.MainApplication
|
||||
import net.blumia.pcmdroid.MainViewModel
|
||||
import net.blumia.pcmdroid.MainViewModelFactory
|
||||
import net.blumia.pcmdroid.model.Folder
|
||||
import net.blumia.pcmdroid.model.Server
|
||||
import net.blumia.pcmdroid.model.Song
|
||||
@ -21,6 +24,7 @@ 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.settings.SettingsScreen
|
||||
import net.blumia.pcmdroid.viewmodel.AddServerViewModel
|
||||
|
||||
object MainDestinations {
|
||||
const val MAIN_ROUTE = "main"
|
||||
@ -31,7 +35,8 @@ object MainDestinations {
|
||||
|
||||
@Composable
|
||||
fun NavGraph(
|
||||
viewModel: MainViewModel,
|
||||
mainViewModel: MainViewModel,
|
||||
addServerViewModel: AddServerViewModel,
|
||||
browser: MediaBrowser?,
|
||||
navController: NavHostController = rememberNavController(),
|
||||
startDestination: String = MainDestinations.MAIN_ROUTE,
|
||||
@ -42,12 +47,12 @@ fun NavGraph(
|
||||
) {
|
||||
composable(MainDestinations.MAIN_ROUTE) {
|
||||
|
||||
val curServer: Server? by viewModel.currentServer.observeAsState(initial = null)
|
||||
val servers: List<Server> by viewModel.servers.observeAsState(initial = listOf())
|
||||
val drawerFolders: List<Folder> by viewModel.drawerFolders.observeAsState(initial = listOf())
|
||||
val curFolder: Folder? by viewModel.currentFolder.observeAsState(initial = null)
|
||||
val folders: List<Folder> by viewModel.folders.observeAsState(listOf())
|
||||
val songs: List<Song> by viewModel.songs.observeAsState(listOf())
|
||||
val curServer: Server? by mainViewModel.currentServer.observeAsState(initial = null)
|
||||
val servers: List<Server> by mainViewModel.servers.observeAsState(initial = listOf())
|
||||
val drawerFolders: List<Folder> by mainViewModel.drawerFolders.observeAsState(initial = listOf())
|
||||
val curFolder: Folder? by mainViewModel.currentFolder.observeAsState(initial = null)
|
||||
val folders: List<Folder> by mainViewModel.folders.observeAsState(listOf())
|
||||
val songs: List<Song> by mainViewModel.songs.observeAsState(listOf())
|
||||
|
||||
MainPlayer(
|
||||
currentServer = curServer,
|
||||
@ -60,10 +65,10 @@ fun NavGraph(
|
||||
navController.navigate(MainDestinations.ADD_SERVER_ROUTE)
|
||||
},
|
||||
onServerItemIconClicked = { server ->
|
||||
viewModel.fetchServer(server)
|
||||
mainViewModel.fetchServer(server)
|
||||
},
|
||||
onFolderItemClicked = { folder ->
|
||||
viewModel.fetchFolder(folder)
|
||||
mainViewModel.fetchFolder(folder)
|
||||
},
|
||||
onSongItemClicked = { song, songs ->
|
||||
Log.d("vvv", song.url + browser.toString())
|
||||
@ -79,7 +84,7 @@ fun NavGraph(
|
||||
navController.navigate( "${MainDestinations.EDIT_SERVER_ROUTE}?id=${server.serverId}")
|
||||
},
|
||||
onDeleteServerActionTriggered = { server ->
|
||||
viewModel.deleteServer(server)
|
||||
mainViewModel.deleteServer(server)
|
||||
},
|
||||
settingsActionTriggered = {
|
||||
navController.navigate(MainDestinations.SETTINGS_ROUTE)
|
||||
@ -93,9 +98,10 @@ fun NavGraph(
|
||||
navController.navigateUp()
|
||||
},
|
||||
onSubmitServerTriggered = { srv ->
|
||||
viewModel.addServer(srv)
|
||||
mainViewModel.addServer(srv)
|
||||
navController.navigateUp()
|
||||
}
|
||||
},
|
||||
viewModel = addServerViewModel,
|
||||
)
|
||||
}
|
||||
|
||||
@ -111,7 +117,7 @@ fun NavGraph(
|
||||
"${MainDestinations.EDIT_SERVER_ROUTE}?id={id}",
|
||||
arguments = listOf(navArgument("id") { type = NavType.LongType })
|
||||
) {
|
||||
val srv = viewModel.getServerById(it.arguments?.getLong("id")!!)
|
||||
val srv = mainViewModel.getServerById(it.arguments?.getLong("id")!!)
|
||||
|
||||
EditServerScreen(
|
||||
server = srv,
|
||||
@ -119,7 +125,7 @@ fun NavGraph(
|
||||
navController.navigateUp()
|
||||
},
|
||||
onSaveButtonTriggered = { server ->
|
||||
viewModel.editServer(server)
|
||||
mainViewModel.editServer(server)
|
||||
navController.navigateUp()
|
||||
},
|
||||
)
|
||||
|
@ -11,6 +11,7 @@ import androidx.compose.material.icons.filled.AddCircle
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.rounded.AddCircle
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@ -20,11 +21,11 @@ import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import net.blumia.pcmdroid.model.Server
|
||||
import net.blumia.pcmdroid.viewmodel.AddServerViewModel
|
||||
|
||||
@Composable
|
||||
fun StepEnterApiUrl(
|
||||
urlStr: String = "",
|
||||
onUrlStrChange: (String) -> Unit = {},
|
||||
viewModel: AddServerViewModel = AddServerViewModel(),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
@ -51,6 +52,9 @@ fun StepEnterApiUrl(
|
||||
textAlign = TextAlign.Center,
|
||||
style = MaterialTheme.typography.subtitle2,
|
||||
)
|
||||
|
||||
val urlStr: String by viewModel.apiUrl.observeAsState("")
|
||||
|
||||
OutlinedTextField(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@ -58,8 +62,9 @@ fun StepEnterApiUrl(
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Uri,
|
||||
),
|
||||
singleLine = true,
|
||||
value = urlStr,
|
||||
onValueChange = onUrlStrChange,
|
||||
onValueChange = viewModel::setApiUrl,
|
||||
label = { Text("Server API Url") },
|
||||
)
|
||||
}
|
||||
@ -67,16 +72,7 @@ fun StepEnterApiUrl(
|
||||
|
||||
@Composable
|
||||
fun StepOtherDetail(
|
||||
nameStr: String = "",
|
||||
onNameStrChange: (String) -> Unit = {},
|
||||
shortNameStr: String = "",
|
||||
onShortNameStrChange: (String) -> Unit = {},
|
||||
baseFolderNameStr: String = "",
|
||||
onBaseFolderNameStrChange: (String) -> Unit = {},
|
||||
mediaBaseUrlStr: String = "",
|
||||
mediaBaseUrlStrChange: (String) -> Unit = {},
|
||||
preferredFormatsStr: String = "",
|
||||
preferredFormatsStrChange: (String) -> Unit = {},
|
||||
viewModel: AddServerViewModel = AddServerViewModel(),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
@ -84,12 +80,19 @@ fun StepOtherDetail(
|
||||
.verticalScroll(rememberScrollState()),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
|
||||
val nameStr: String by viewModel.nameStr.observeAsState("")
|
||||
val shortNameStr: String by viewModel.shortNameStr.observeAsState("")
|
||||
val baseFolderNameStr: String by viewModel.baseFolderNameStr.observeAsState("")
|
||||
val mediaBaseUrlStr: String by viewModel.mediaBaseUrlStr.observeAsState("")
|
||||
val preferredFormatsStr: String by viewModel.preferredFormatsStr.observeAsState("")
|
||||
|
||||
OutlinedTextField(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 10.dp),
|
||||
value = shortNameStr,
|
||||
onValueChange = onShortNameStrChange,
|
||||
onValueChange = viewModel::setShortNameStr,
|
||||
label = { Text("Server short name") },
|
||||
)
|
||||
|
||||
@ -98,7 +101,7 @@ fun StepOtherDetail(
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 10.dp),
|
||||
value = nameStr,
|
||||
onValueChange = onNameStrChange,
|
||||
onValueChange = viewModel::setNameStr,
|
||||
label = { Text("Server name") },
|
||||
)
|
||||
|
||||
@ -107,7 +110,7 @@ fun StepOtherDetail(
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 10.dp),
|
||||
value = baseFolderNameStr,
|
||||
onValueChange = onBaseFolderNameStrChange,
|
||||
onValueChange = viewModel::setBaseFolderNameStr,
|
||||
label = { Text("Base folder name") },
|
||||
)
|
||||
|
||||
@ -116,7 +119,7 @@ fun StepOtherDetail(
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 10.dp),
|
||||
value = mediaBaseUrlStr,
|
||||
onValueChange = mediaBaseUrlStrChange,
|
||||
onValueChange = viewModel::setMediaBaseUrlStr,
|
||||
label = { Text("Media base url") },
|
||||
)
|
||||
|
||||
@ -125,7 +128,7 @@ fun StepOtherDetail(
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 10.dp),
|
||||
value = preferredFormatsStr,
|
||||
onValueChange = preferredFormatsStrChange,
|
||||
onValueChange = viewModel::setPreferredFormatsStr,
|
||||
label = { Text("Preferred formats") },
|
||||
)
|
||||
}
|
||||
@ -136,6 +139,7 @@ fun StepOtherDetail(
|
||||
fun AddServerScreen(
|
||||
onCloseActionTriggered: () -> Unit = {},
|
||||
onSubmitServerTriggered: (Server) -> Unit = {},
|
||||
viewModel: AddServerViewModel = AddServerViewModel(),
|
||||
) {
|
||||
var stepState by remember { mutableStateOf(0) }
|
||||
val finalStep = 1
|
||||
@ -172,13 +176,6 @@ fun AddServerScreen(
|
||||
.padding(20.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
var urlStr by rememberSaveable { mutableStateOf("") }
|
||||
var nameStr by rememberSaveable { mutableStateOf("") }
|
||||
var shortNameStr by rememberSaveable { mutableStateOf("") }
|
||||
var baseFolderNameStr by rememberSaveable { mutableStateOf("") }
|
||||
var mediaBaseUrlStr by rememberSaveable { mutableStateOf("") }
|
||||
var preferredFormatsStr by rememberSaveable { mutableStateOf("") }
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@ -188,34 +185,12 @@ fun AddServerScreen(
|
||||
when (stepState) {
|
||||
0 -> {
|
||||
StepEnterApiUrl(
|
||||
urlStr,
|
||||
onUrlStrChange = { value ->
|
||||
urlStr = value
|
||||
},
|
||||
viewModel,
|
||||
)
|
||||
}
|
||||
1 -> {
|
||||
StepOtherDetail(
|
||||
nameStr,
|
||||
onNameStrChange = { value ->
|
||||
nameStr = value
|
||||
},
|
||||
shortNameStr,
|
||||
onShortNameStrChange = { value ->
|
||||
shortNameStr = value
|
||||
},
|
||||
baseFolderNameStr,
|
||||
onBaseFolderNameStrChange = { value ->
|
||||
baseFolderNameStr = value
|
||||
},
|
||||
mediaBaseUrlStr,
|
||||
mediaBaseUrlStrChange = { value ->
|
||||
mediaBaseUrlStr = value
|
||||
},
|
||||
preferredFormatsStr,
|
||||
preferredFormatsStrChange = { value ->
|
||||
preferredFormatsStr = value
|
||||
},
|
||||
viewModel = viewModel,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -236,15 +211,16 @@ fun AddServerScreen(
|
||||
Button(
|
||||
onClick = {
|
||||
if (stepState != finalStep) {
|
||||
viewModel.fetchContentFromApiUrl()
|
||||
onNextStepButtonTriggered()
|
||||
} else {
|
||||
val srv = Server(
|
||||
url = urlStr,
|
||||
name = nameStr,
|
||||
shortName = shortNameStr,
|
||||
baseFolderName = baseFolderNameStr,
|
||||
mediaBaseUrl = mediaBaseUrlStr,
|
||||
preferredFormats = preferredFormatsStr,
|
||||
url = viewModel.apiUrl.value!!,
|
||||
name = viewModel.nameStr.value!!,
|
||||
shortName = viewModel.shortNameStr.value!!,
|
||||
baseFolderName = viewModel.baseFolderNameStr.value!!,
|
||||
mediaBaseUrl = viewModel.mediaBaseUrlStr.value!!,
|
||||
preferredFormats = viewModel.preferredFormatsStr.value!!,
|
||||
)
|
||||
onSubmitServerTriggered(srv)
|
||||
}
|
||||
|
@ -0,0 +1,97 @@
|
||||
package net.blumia.pcmdroid.viewmodel
|
||||
|
||||
import android.util.Log
|
||||
import android.webkit.URLUtil
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import net.blumia.pcmdroid.model.Server
|
||||
import net.blumia.pcmdroid.model.parseFromJsonString
|
||||
import net.blumia.pcmdroid.model.parseServerFromJsonString
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
|
||||
class AddServerViewModel : ViewModel() {
|
||||
// private val httpClient: OkHttpClient = OkHttpClient()
|
||||
|
||||
private val _apiUrl: MutableLiveData<String> = MutableLiveData("")
|
||||
val apiUrl: LiveData<String> = _apiUrl
|
||||
fun setApiUrl(url: String) {
|
||||
// FIXME: do we really need to use postValue here?
|
||||
// will the setter for the LiveData object trigger observer?
|
||||
_apiUrl.postValue(url)
|
||||
}
|
||||
|
||||
private val _nameStr: MutableLiveData<String> = MutableLiveData("")
|
||||
val nameStr: LiveData<String> = _nameStr
|
||||
fun setNameStr(name: String) {
|
||||
_nameStr.postValue(name)
|
||||
}
|
||||
|
||||
private val _shortNameStr: MutableLiveData<String> = MutableLiveData("")
|
||||
val shortNameStr: LiveData<String> = _shortNameStr
|
||||
fun setShortNameStr(shortName: String) {
|
||||
_shortNameStr.postValue(shortName)
|
||||
}
|
||||
|
||||
private val _baseFolderNameStr: MutableLiveData<String> = MutableLiveData("")
|
||||
val baseFolderNameStr: LiveData<String> = _baseFolderNameStr
|
||||
fun setBaseFolderNameStr(str: String) {
|
||||
_baseFolderNameStr.postValue(str)
|
||||
}
|
||||
|
||||
private val _mediaBaseUrlStr: MutableLiveData<String> = MutableLiveData("")
|
||||
val mediaBaseUrlStr: LiveData<String> = _mediaBaseUrlStr
|
||||
fun setMediaBaseUrlStr(str: String) {
|
||||
_mediaBaseUrlStr.postValue(str)
|
||||
}
|
||||
|
||||
private val _preferredFormatsStr: MutableLiveData<String> = MutableLiveData("")
|
||||
val preferredFormatsStr: LiveData<String> = _preferredFormatsStr
|
||||
fun setPreferredFormatsStr(str: String) {
|
||||
_preferredFormatsStr.postValue(str)
|
||||
}
|
||||
|
||||
fun isApiUrlValid(): Boolean {
|
||||
return URLUtil.isNetworkUrl(apiUrl.value)
|
||||
}
|
||||
|
||||
fun fetchContentFromApiUrl() {
|
||||
val httpClient: OkHttpClient = OkHttpClient()
|
||||
|
||||
viewModelScope.launch(context = Dispatchers.IO) {
|
||||
|
||||
val formBody = FormBody.Builder()
|
||||
.add("do", "getserverinfo")
|
||||
.build()
|
||||
|
||||
val request = Request.Builder()
|
||||
// .url("http://localhost/")
|
||||
.url(apiUrl.value!!)
|
||||
.post(formBody)
|
||||
.build()
|
||||
|
||||
try {
|
||||
httpClient.newCall(request).execute().use { response ->
|
||||
if (!response.isSuccessful) {
|
||||
// TODO: non-200 response will go to here too.
|
||||
return@use
|
||||
}
|
||||
val kv = parseServerFromJsonString(response.body!!.string())
|
||||
Log.d("vvv", kv.toString())
|
||||
if (kv["serverName"] != null) setNameStr(kv["serverName"]!!)
|
||||
if (kv["serverShortName"] != null) setShortNameStr(kv["serverShortName"]!!)
|
||||
if (kv["baseFolderNameHint"] != null) setBaseFolderNameStr(kv["baseFolderNameHint"]!!)
|
||||
if (kv["preferredFormatsHint"] != null) setPreferredFormatsStr(kv["preferredFormatsHint"]!!)
|
||||
if (kv["mediaRootUrl"] != null) setMediaBaseUrlStr(kv["mediaRootUrl"]!!)
|
||||
}
|
||||
} catch (e: java.io.IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user