Skip to content

Commit

Permalink
Create basic UI for devices
Browse files Browse the repository at this point in the history
  • Loading branch information
rien committed Dec 27, 2021
1 parent cec0311 commit f7743e0
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 38 deletions.
8 changes: 5 additions & 3 deletions app/src/main/java/me/vanpetegem/accentor/devices/Device.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ sealed class Device(
) {

val friendlyName: String = clingDevice.details.friendlyName
val firstCharacter: String = String(intArrayOf(friendlyName.codePointAt(0)), 0, 1)
val displayString: String = clingDevice.displayString
val type: String = clingDevice.type.displayString

val imageURL: String? = clingDevice
Expand All @@ -17,6 +17,10 @@ sealed class Device(
?.let { clingDevice.normalizeURI(it.uri).toString() }


class Ready(clingDevice: RemoteDevice): Device(clingDevice) {}

class Failed(clingDevice: RemoteDevice, val exception: Exception?): Device(clingDevice) {}

class Discovered(clingDevice: RemoteDevice): Device(clingDevice) {
fun failed(exception: Exception?): Failed {
return Failed(clingDevice, exception)
Expand All @@ -26,8 +30,6 @@ sealed class Device(
return Ready(clingDevice)
}
}
class Failed(clingDevice: RemoteDevice, val exception: Exception?): Device(clingDevice) {}
class Ready(clingDevice: RemoteDevice): Device(clingDevice) {}
}


Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,24 @@ import android.content.ComponentName
import android.content.ServiceConnection
import android.os.IBinder
import android.util.Log
import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.snapshots.SnapshotStateMap
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import dagger.Reusable
import org.fourthline.cling.android.AndroidUpnpService
import org.fourthline.cling.model.message.header.ServiceTypeHeader
import org.fourthline.cling.model.meta.RemoteDevice
import org.fourthline.cling.model.types.ServiceType
import org.fourthline.cling.model.types.UDN
import org.fourthline.cling.registry.DefaultRegistryListener
import org.fourthline.cling.registry.Registry
import java.lang.Exception
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class DeviceManager @Inject constructor() {

val devices = MutableLiveData<Map<UDN, Device>>(emptyMap())
val selectedDevice = MutableLiveData<Device?>(null)

val connection = DeviceServiceConnection()

private lateinit var upnp: AndroidUpnpService
Expand Down
85 changes: 55 additions & 30 deletions app/src/main/java/me/vanpetegem/accentor/ui/devices/DevicesView.kt
Original file line number Diff line number Diff line change
@@ -1,60 +1,85 @@
package me.vanpetegem.accentor.ui.devices

import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Card
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import coil.compose.rememberImagePainter
import me.vanpetegem.accentor.R
import me.vanpetegem.accentor.devices.Device
import me.vanpetegem.accentor.ui.util.FastScrollableGrid

@Composable
fun Devices(devicesViewModel: DevicesViewModel = hiltViewModel()) {
val devices: List<Device>? by devicesViewModel.devices().observeAsState()
FastScrollableGrid(devices ?: emptyList(), { it.firstCharacter }) { DeviceCard(it) }
DeviceList(devices ?: emptyList())
}

@Composable
fun DeviceCard(device: Device) {
fun DeviceList(devices: List<Device>) {
Column(Modifier.fillMaxWidth().verticalScroll(rememberScrollState())) {
DeviceCard(
name = stringResource(R.string.local_device),
icon = R.drawable.ic_smartphone_sound,
iconDescription = R.string.local_device_description
)
Spacer(Modifier.size(8.dp))
Text(
stringResource(R.string.devices_available),
modifier = Modifier.padding(8.dp),
style = MaterialTheme.typography.h5
)
devices.forEach { device ->
DeviceCard(
name = device.friendlyName,
icon = R.drawable.ic_menu_devices
)
}
}
}

@Composable
fun DeviceCard(
name: String,
@StringRes
iconDescription: Int = R.string.device_image,
@DrawableRes
icon: Int = R.drawable.ic_menu_devices,
onClick: () -> Unit = {}) {
Card(
modifier = Modifier.padding(8.dp),
modifier = Modifier.padding(8.dp).fillMaxWidth().clickable(onClick = onClick)
) {
Column {
Row(
modifier = Modifier.padding(8.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start
) {
Image(
painter = if (device.imageURL != null) {
rememberImagePainter(device.imageURL) {
placeholder(R.drawable.ic_artist)
}
} else {
painterResource(R.drawable.ic_artist)
},
contentDescription = stringResource(R.string.device_image),
modifier = Modifier.fillMaxWidth().aspectRatio(1f),
contentScale = ContentScale.Crop,
)
Text(
device.friendlyName,
maxLines = 1,
modifier = Modifier.padding(4.dp),
style = MaterialTheme.typography.subtitle1,
)
Text(
device.type
painter = painterResource(icon),
contentDescription = stringResource(iconDescription),
modifier = Modifier.requiredSize(48.dp)
)
Column() {
Text(
name,
maxLines = 1,
modifier = Modifier.padding(16.dp),
style = MaterialTheme.typography.subtitle1
)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ class DevicesViewModel @Inject constructor(
) : AndroidViewModel(application) {

fun devices(): LiveData<List<Device>> = map(deviceManager.devices) { devices ->
devices.values.sortedWith(compareBy({ it.friendlyName }, { it.firstCharacter.uppercase() }))
devices.values.sortedWith(compareBy(
{ when(it) {
is Device.Ready -> 1
is Device.Failed -> 2
is Device.Discovered -> 3
}},
{ it.friendlyName })
)
}
}
8 changes: 8 additions & 0 deletions app/src/main/res/drawable/ic_smartphone_sound.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#000" android:pathData="M19.1,8.7C20.9,10.5 20.9,13.3 19.1,15.2L20.1,16.2C22.6,13.9 22.6,10.1 20.1,7.7L19.1,8.7M18,9.8L17,10.8C17.5,11.5 17.5,12.4 17,13.1L18,14.1C19.2,12.9 19.2,11.1 18,9.8M14,1H4A2,2 0 0,0 2,3V21A2,2 0 0,0 4,23H14A2,2 0 0,0 16,21V3A2,2 0 0,0 14,1M14,20H4V4H14V20Z" />
</vector>
3 changes: 3 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,7 @@
<string name="no_on_this_day">No albums were released on this day</string>
<string name="no_artists">No artists could be found</string>
<string name="title_activity_device_view">DeviceView</string>
<string name="local_device">Play on this device</string>
<string name="local_device_description">Sound coming from your device</string>
<string name="devices_available">Stream to devices</string>
</resources>

0 comments on commit f7743e0

Please sign in to comment.