fix livedata not updated, playlist not scrollable
This commit is contained in:
parent
4312d1fdea
commit
8edcb91242
|
@ -52,6 +52,7 @@ dependencies {
|
|||
implementation 'androidx.appcompat:appcompat:1.3.1'
|
||||
implementation 'com.google.android.material:material:1.4.0'
|
||||
implementation "androidx.compose.ui:ui:$compose_version"
|
||||
implementation "androidx.compose.runtime:runtime-livedata:$compose_version"
|
||||
implementation "androidx.compose.material:material:$compose_version"
|
||||
implementation "androidx.compose.material:material-icons-extended:$compose_version"
|
||||
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
|
||||
|
@ -59,6 +60,9 @@ dependencies {
|
|||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'
|
||||
implementation 'androidx.activity:activity-compose:1.4.0'
|
||||
implementation "androidx.datastore:datastore-preferences:1.0.0"
|
||||
|
||||
implementation("com.squareup.okhttp3:okhttp:4.9.0")
|
||||
|
||||
testImplementation 'junit:junit:4.+'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||
|
|
|
@ -1,63 +1,132 @@
|
|||
package net.blumia.pcmdroid
|
||||
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.launch
|
||||
import net.blumia.pcmdroid.model.Folder
|
||||
import net.blumia.pcmdroid.model.Server
|
||||
import net.blumia.pcmdroid.model.Song
|
||||
import net.blumia.pcmdroid.model.parseFromJsonString
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
|
||||
class MainViewModel : ViewModel() {
|
||||
|
||||
private val httpClient: OkHttpClient = OkHttpClient()
|
||||
|
||||
val servers: LiveData<List<Server>> = MutableLiveData<List<Server>>(
|
||||
listOf(
|
||||
Server(
|
||||
url = Uri.parse("https://localhost/api.php"),
|
||||
url = "http://localhost/api.php",
|
||||
name = "PCM",
|
||||
baseFolderName = "pcm",
|
||||
),
|
||||
Server(
|
||||
url = Uri.parse("https://localhost/api.php"),
|
||||
url = "http://localhost/api.php",
|
||||
name = "PCM2",
|
||||
baseFolderName = "pcm2",
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val currentPath: LiveData<String> = MutableLiveData("")
|
||||
private val _currentServer: MutableLiveData<Server> = MutableLiveData(null)
|
||||
val currentServer: LiveData<Server> = _currentServer;
|
||||
fun setCurrentServer(srv: Server) {
|
||||
_currentServer.postValue(srv)
|
||||
}
|
||||
|
||||
val drawerFolders: LiveData<List<Folder>> = MutableLiveData(
|
||||
private val _currentPath: MutableLiveData<String> = MutableLiveData("")
|
||||
val currentPath: LiveData<String> = _currentPath
|
||||
|
||||
private val _drawerFolders: MutableLiveData<List<Folder>> = MutableLiveData(
|
||||
listOf(
|
||||
Folder("Test/"),
|
||||
Folder("Folder/")
|
||||
)
|
||||
)
|
||||
val drawerFolders: LiveData<List<Folder>> = _drawerFolders
|
||||
|
||||
val folders: LiveData<List<Folder>> = MutableLiveData(
|
||||
private val _folders: MutableLiveData<List<Folder>> = MutableLiveData(
|
||||
listOf(
|
||||
Folder("Test/Converted-Modules/")
|
||||
)
|
||||
)
|
||||
val folders: LiveData<List<Folder>> = _folders
|
||||
|
||||
val songs: LiveData<List<Song>> = MutableLiveData(
|
||||
private val _songs: MutableLiveData<List<Song>> = MutableLiveData(
|
||||
listOf(
|
||||
Song(
|
||||
filePath = "Test/Rick Astley - Never Gonna Give You Up.mp3",
|
||||
url = Uri.parse("http://localhost/rickroll.mp3")
|
||||
url = "http://localhost/rickroll.mp3"
|
||||
),
|
||||
Song(
|
||||
filePath = "Test/哇 好东西一堆.xm",
|
||||
url = Uri.parse("http://localhost/rickroll.mp3")
|
||||
url = "http://localhost/rickroll.mp3"
|
||||
)
|
||||
)
|
||||
)
|
||||
val songs: LiveData<List<Song>> = _songs
|
||||
|
||||
fun fetchServer(srv: Server) {
|
||||
//
|
||||
viewModelScope.launch(context = Dispatchers.IO) {
|
||||
|
||||
val formBody = FormBody.Builder()
|
||||
.add("do", "getfilelist")
|
||||
.build()
|
||||
|
||||
val request = Request.Builder()
|
||||
.url(srv.url)
|
||||
.post(formBody)
|
||||
.build()
|
||||
|
||||
httpClient.newCall(request).execute().use { response ->
|
||||
if (!response.isSuccessful) {
|
||||
// TODO: non-200 response will go to here too.
|
||||
return@use
|
||||
}
|
||||
val pair = parseFromJsonString(response.body!!.string())
|
||||
_drawerFolders.postValue(pair.first)
|
||||
setCurrentServer(srv)
|
||||
Log.d("vvv", drawerFolders.value.toString())
|
||||
}
|
||||
// Thread.sleep(5_000)
|
||||
}
|
||||
}
|
||||
|
||||
fun fetchFolder(folder: Folder) {
|
||||
//
|
||||
if (currentServer.value == null) return
|
||||
val srv = currentServer.value!!
|
||||
|
||||
viewModelScope.launch(context = Dispatchers.IO) {
|
||||
|
||||
val formBody = FormBody.Builder()
|
||||
.add("do", "getfilelist")
|
||||
.add("folder", folder.folderPath)
|
||||
.build()
|
||||
|
||||
val request = Request.Builder()
|
||||
.url(srv.url)
|
||||
.post(formBody)
|
||||
.build()
|
||||
|
||||
httpClient.newCall(request).execute().use { response ->
|
||||
if (!response.isSuccessful) {
|
||||
// TODO: non-200 response will go to here too.
|
||||
return@use
|
||||
}
|
||||
val pair = parseFromJsonString(response.body!!.string())
|
||||
_folders.postValue(pair.first)
|
||||
_songs.postValue(pair.second)
|
||||
Log.d("vvv", folders.value.toString())
|
||||
}
|
||||
// Thread.sleep(5_000)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,7 @@
|
|||
package net.blumia.pcmdroid.model
|
||||
|
||||
import android.net.Uri
|
||||
|
||||
data class Server (
|
||||
val url: Uri,
|
||||
val url: String,
|
||||
val name: String,
|
||||
val baseFolderName: String,
|
||||
)
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
package net.blumia.pcmdroid.model
|
||||
|
||||
import android.net.Uri
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import java.net.URLDecoder
|
||||
|
||||
data class Song (
|
||||
val filePath: String, // with file name
|
||||
val modifiedTime: Int = 0,
|
||||
val modifiedTime: Long = 0,
|
||||
val fileSize: Int = 0,
|
||||
val additionalInfo: Boolean = false, // if sidecar meta-info json file exist.
|
||||
val url: Uri,
|
||||
val url: String,
|
||||
) {
|
||||
fun displayName(): String {
|
||||
return Uri.parse(filePath).lastPathSegment ?: "???"
|
||||
|
@ -18,6 +21,41 @@ data class Folder (
|
|||
val folderPath: String,
|
||||
) {
|
||||
fun displayName(): String {
|
||||
return Uri.parse(folderPath).lastPathSegment ?: "???"
|
||||
return URLDecoder.decode(Uri.parse(folderPath).lastPathSegment ?: "???", "UTF-8")
|
||||
}
|
||||
}
|
||||
|
||||
fun parseFromJsonString(jsonString: String): Pair<List<Folder>, List<Song>> {
|
||||
val folders = mutableListOf<Folder>()
|
||||
val songs = mutableListOf<Song>()
|
||||
|
||||
try {
|
||||
val jsonObj = JSONObject(jsonString)
|
||||
val result = jsonObj.getJSONObject("result")
|
||||
val data = result.getJSONObject("data")
|
||||
val subFolders = data.getJSONArray("subFolderList")
|
||||
val musicList = data.getJSONArray("musicList")
|
||||
for (i in 0 until subFolders.length()) {
|
||||
val rawFolderName = subFolders.getString(i)
|
||||
folders.add(Folder(rawFolderName))
|
||||
}
|
||||
for (i in 0 until musicList.length()) {
|
||||
val fileObject = musicList.getJSONObject(i)
|
||||
val rawFileName = fileObject.getString("fileName")
|
||||
val fileName = URLDecoder.decode(rawFileName, "UTF-8")
|
||||
val modifiedTime = fileObject.getLong("modifiedTime")
|
||||
val fileSize = fileObject.getLong("fileSize")
|
||||
songs.add(
|
||||
Song(
|
||||
rawFileName,
|
||||
modifiedTime = modifiedTime,
|
||||
url = rawFileName
|
||||
)
|
||||
)
|
||||
}
|
||||
} catch (e: JSONException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
return Pair(folders, songs)
|
||||
}
|
|
@ -1,11 +1,16 @@
|
|||
package net.blumia.pcmdroid.ui
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import net.blumia.pcmdroid.MainViewModel
|
||||
import net.blumia.pcmdroid.model.Folder
|
||||
import net.blumia.pcmdroid.model.Server
|
||||
import net.blumia.pcmdroid.model.Song
|
||||
import net.blumia.pcmdroid.ui.screen.addserver.AddServerScreen
|
||||
import net.blumia.pcmdroid.ui.screen.main.MainPlayer
|
||||
import net.blumia.pcmdroid.ui.screen.settings.SettingsScreen
|
||||
|
@ -27,11 +32,26 @@ fun NavGraph(
|
|||
startDestination = startDestination
|
||||
) {
|
||||
composable(MainDestinations.MAIN_ROUTE) {
|
||||
|
||||
val servers: List<Server> by viewModel.servers.observeAsState(initial = listOf())
|
||||
val drawerFolders: List<Folder> by viewModel.drawerFolders.observeAsState(initial = listOf())
|
||||
val folders: List<Folder> by viewModel.folders.observeAsState(listOf())
|
||||
val songs: List<Song> by viewModel.songs.observeAsState(listOf())
|
||||
|
||||
MainPlayer(
|
||||
viewModel = viewModel,
|
||||
servers = servers,
|
||||
drawerFolders = drawerFolders,
|
||||
folders = folders,
|
||||
songs = songs,
|
||||
addServerActionTriggered = {
|
||||
navController.navigate(MainDestinations.ADD_SERVER_ROUTE)
|
||||
},
|
||||
onServerItemIconClicked = { server ->
|
||||
viewModel.fetchServer(server)
|
||||
},
|
||||
onDrawerFolderItemClicked = { folder ->
|
||||
viewModel.fetchFolder(folder)
|
||||
},
|
||||
settingsActionTriggered = {
|
||||
navController.navigate(MainDestinations.SETTINGS_ROUTE)
|
||||
}
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
package net.blumia.pcmdroid.ui.screen.main
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.gestures.scrollable
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.*
|
||||
|
@ -15,6 +12,9 @@ import androidx.compose.material.icons.rounded.PlayArrow
|
|||
import androidx.compose.material.icons.rounded.PlayCircle
|
||||
import androidx.compose.material.icons.rounded.PlayCircleOutline
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
|
@ -24,7 +24,6 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlinx.coroutines.launch
|
||||
import net.blumia.pcmdroid.MainViewModel
|
||||
import net.blumia.pcmdroid.R
|
||||
import net.blumia.pcmdroid.model.Folder
|
||||
import net.blumia.pcmdroid.model.Server
|
||||
|
@ -74,15 +73,17 @@ fun NowPlaying() {
|
|||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun DrawerContent(
|
||||
viewModel: MainViewModel = MainViewModel(),
|
||||
servers: List<Server> = listOf(),
|
||||
drawerFolders: List<Folder> = listOf(),
|
||||
addServerActionTriggered: () -> Unit = {},
|
||||
onServerItemIconClicked: (Server) -> Unit = {},
|
||||
onDrawerFolderItemClicked: (Folder) -> Unit = {},
|
||||
settingsActionTriggered: () -> Unit = {},
|
||||
) {
|
||||
Row(modifier = Modifier.fillMaxSize()) {
|
||||
var selectedItem by remember { mutableStateOf<Int?>(0) }
|
||||
val servers = viewModel.servers.value
|
||||
NavigationRail {
|
||||
servers?.forEachIndexed { index, server ->
|
||||
servers.forEachIndexed { index, server ->
|
||||
NavigationRailItem(
|
||||
icon = {
|
||||
Icon(
|
||||
|
@ -94,7 +95,7 @@ fun DrawerContent(
|
|||
},
|
||||
selected = selectedItem == index,
|
||||
onClick = {
|
||||
viewModel.fetchServer(server)
|
||||
onServerItemIconClicked(server)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -133,11 +134,10 @@ fun DrawerContent(
|
|||
|
||||
Divider()
|
||||
|
||||
val drawerFolders = viewModel.drawerFolders.value
|
||||
drawerFolders?.forEach { folder ->
|
||||
drawerFolders.forEach { folder ->
|
||||
ListItem(
|
||||
modifier = Modifier.clickable{
|
||||
viewModel.fetchFolder(folder)
|
||||
onDrawerFolderItemClicked(folder)
|
||||
},
|
||||
text = {
|
||||
Text(
|
||||
|
@ -155,8 +155,13 @@ fun DrawerContent(
|
|||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun MainPlayer(
|
||||
viewModel: MainViewModel = MainViewModel(),
|
||||
servers: List<Server> = listOf(),
|
||||
drawerFolders: List<Folder> = listOf(),
|
||||
folders: List<Folder> = listOf(),
|
||||
songs: List<Song> = listOf(),
|
||||
addServerActionTriggered: () -> Unit = {},
|
||||
onServerItemIconClicked: (Server) -> Unit = {},
|
||||
onDrawerFolderItemClicked: (Folder) -> Unit = {},
|
||||
settingsActionTriggered: () -> Unit = {},
|
||||
) {
|
||||
val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
|
||||
|
@ -165,8 +170,11 @@ fun MainPlayer(
|
|||
drawerState = drawerState,
|
||||
drawerContent = {
|
||||
DrawerContent(
|
||||
viewModel = viewModel,
|
||||
servers = servers,
|
||||
drawerFolders = drawerFolders,
|
||||
addServerActionTriggered = addServerActionTriggered,
|
||||
onServerItemIconClicked = onServerItemIconClicked,
|
||||
onDrawerFolderItemClicked = onDrawerFolderItemClicked,
|
||||
settingsActionTriggered = settingsActionTriggered,
|
||||
)
|
||||
}
|
||||
|
@ -195,7 +203,11 @@ fun MainPlayer(
|
|||
Column(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1f, fill = true)) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f, fill = true)
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
|
||||
val iconLeftPadding = 16.dp
|
||||
val iconVerticalPadding = 12.dp
|
||||
|
@ -229,7 +241,7 @@ fun MainPlayer(
|
|||
}
|
||||
}
|
||||
|
||||
viewModel.folders.value?.forEach { folder ->
|
||||
folders.forEach { folder ->
|
||||
ListItem(
|
||||
modifier = Modifier.clickable { },
|
||||
icon = {
|
||||
|
@ -244,7 +256,7 @@ fun MainPlayer(
|
|||
)
|
||||
}
|
||||
|
||||
viewModel.songs.value?.forEach { song ->
|
||||
songs.forEach { song ->
|
||||
ListItem(
|
||||
modifier = Modifier.clickable { },
|
||||
icon = {
|
||||
|
|
Loading…
Reference in New Issue
Block a user