-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Clean up device code with LiveData and Hilt
- Loading branch information
Showing
7 changed files
with
155 additions
and
98 deletions.
There are no files selected for viewing
21 changes: 17 additions & 4 deletions
21
app/src/main/java/me/vanpetegem/accentor/devices/Device.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,33 @@ | ||
package me.vanpetegem.accentor.devices | ||
|
||
import org.fourthline.cling.model.meta.Device | ||
import org.fourthline.cling.model.meta.RemoteDevice | ||
import java.lang.Exception | ||
|
||
class Device( | ||
private val clingDevice: RemoteDevice | ||
sealed class Device( | ||
protected val clingDevice: RemoteDevice | ||
) { | ||
|
||
val friendlyName: String = clingDevice.details.friendlyName | ||
val firstCharacter: String = String(intArrayOf(friendlyName.codePointAt(0)), 0, 1) | ||
|
||
val type: String = clingDevice.type.displayString | ||
|
||
val imageURL: String? = clingDevice | ||
.icons | ||
.maxWithOrNull(compareBy({ it.height * it.width }, { it.mimeType.subtype == "png" })) | ||
?.let { clingDevice.normalizeURI(it.uri).toString() } | ||
|
||
|
||
class Discovered(clingDevice: RemoteDevice): Device(clingDevice) { | ||
fun failed(exception: Exception?): Failed { | ||
return Failed(clingDevice, exception) | ||
} | ||
|
||
fun ready(): Ready { | ||
return Ready(clingDevice) | ||
} | ||
} | ||
class Failed(clingDevice: RemoteDevice, val exception: Exception?): Device(clingDevice) {} | ||
class Ready(clingDevice: RemoteDevice): Device(clingDevice) {} | ||
} | ||
|
||
|
109 changes: 109 additions & 0 deletions
109
app/src/main/java/me/vanpetegem/accentor/devices/DeviceManager.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
package me.vanpetegem.accentor.devices | ||
|
||
|
||
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 connection = DeviceServiceConnection() | ||
|
||
private lateinit var upnp: AndroidUpnpService | ||
private val isConnected = MutableLiveData(false) | ||
private val registryListener = DeviceRegistryListener() | ||
|
||
fun search() { | ||
val playerService = ServiceTypeHeader(ServiceType("schemas-upnp-org", "AVTransport", 1)) | ||
upnp.controlPoint.search(playerService) | ||
} | ||
|
||
inner class DeviceServiceConnection() : ServiceConnection { | ||
override fun onServiceConnected(className: ComponentName?, binder: IBinder?) { | ||
upnp = binder!! as AndroidUpnpService | ||
isConnected.value = true | ||
|
||
// clear devices (if any) and collect the known remote devices into a map | ||
devices.value = upnp.registry.devices | ||
.filterIsInstance<RemoteDevice>() | ||
.map { it.identity.udn to Device.Ready(it) } | ||
.toMap() | ||
|
||
upnp.registry.addListener(registryListener) | ||
search() | ||
} | ||
|
||
override fun onServiceDisconnected(className: ComponentName?) { | ||
isConnected.value = false | ||
} | ||
} | ||
|
||
private inner class DeviceRegistryListener(): DefaultRegistryListener() { | ||
|
||
override fun remoteDeviceDiscoveryStarted(registry: Registry?, remote: RemoteDevice?) { | ||
val udn = remote!!.identity.udn | ||
// this will only add a new device if not yet present in the map | ||
devices.postValue(mapOf(udn to Device.Discovered(remote)) + devices.value!!) | ||
} | ||
|
||
override fun remoteDeviceDiscoveryFailed(registry: Registry?, remote: RemoteDevice?, ex: Exception?) { | ||
val udn = remote!!.identity.udn | ||
val known = devices.value!! | ||
when(val dev = known[udn]) { | ||
is Device.Discovered -> devices.postValue(known + (udn to dev.failed(ex))) | ||
else -> Log.e(TAG, "Discovery failed of existing device", ex) | ||
} | ||
} | ||
|
||
override fun remoteDeviceUpdated(registry: Registry?, remote: RemoteDevice?) { | ||
if (devices.value!!.contains(remote!!.identity.udn)) { | ||
// trigger an update | ||
devices.postValue(devices.value) | ||
} else { | ||
Log.e(TAG, "Non-existing device updated") | ||
} | ||
} | ||
|
||
override fun remoteDeviceAdded(registry: Registry?, remote: RemoteDevice?) { | ||
addDevice(remote!!) | ||
} | ||
|
||
override fun remoteDeviceRemoved(registry: Registry?, remote: RemoteDevice?) { | ||
val withRemoved = devices.value!!.minus(remote!!.identity.udn) | ||
devices.postValue(withRemoved) | ||
} | ||
|
||
fun addDevice(remote: RemoteDevice) { | ||
val udn = remote.identity.udn | ||
val known = devices.value!! | ||
if (udn in known) { | ||
when (val dev = known[udn]) { | ||
is Device.Discovered -> devices.postValue(known + (udn to dev.ready())) | ||
else -> Log.e(TAG, "Device added twice, ignoring... ${remote.displayString} ($udn)") | ||
} | ||
} else { | ||
devices.postValue(known + (udn to Device.Ready(remote))) | ||
} | ||
} | ||
} | ||
} | ||
|
||
const val TAG: String = "DeviceManager" |
54 changes: 0 additions & 54 deletions
54
app/src/main/java/me/vanpetegem/accentor/devices/DeviceRegistryListener.kt
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
21 changes: 15 additions & 6 deletions
21
app/src/main/java/me/vanpetegem/accentor/ui/devices/DevicesViewModel.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,21 @@ | ||
package me.vanpetegem.accentor.ui.devices | ||
|
||
import android.app.Application | ||
import android.content.ComponentName | ||
import android.content.ServiceConnection | ||
import android.os.IBinder | ||
import androidx.lifecycle.AndroidViewModel | ||
import androidx.lifecycle.LiveData | ||
import androidx.lifecycle.Transformations.map | ||
import dagger.hilt.android.lifecycle.HiltViewModel | ||
import me.vanpetegem.accentor.devices.Device | ||
import me.vanpetegem.accentor.devices.DeviceRegistryListener | ||
import org.fourthline.cling.android.AndroidUpnpService | ||
import me.vanpetegem.accentor.devices.DeviceManager | ||
import javax.inject.Inject | ||
|
||
class DevicesViewModel(application: Application) : AndroidViewModel(application) {} | ||
@HiltViewModel | ||
class DevicesViewModel @Inject constructor( | ||
application: Application, | ||
private val deviceManager: DeviceManager, | ||
) : AndroidViewModel(application) { | ||
|
||
fun devices(): LiveData<List<Device>> = map(deviceManager.devices) { devices -> | ||
devices.values.sortedWith(compareBy({ it.friendlyName }, { it.firstCharacter.uppercase() })) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters