EARHORNMUSIC UPnP · Android TV · Phones · Android Auto · GNU/Linux
Release 0.5.0

UpnpServiceHolder.kt

Each line, with the release that last changed it.

0.5.01package dev.stave.upnp
2
3import dev.stave.domain.model.MediaServer
4import dev.stave.domain.model.RemoteRenderer
5import kotlinx.coroutines.flow.StateFlow
6import org.jupnp.controlpoint.ControlPoint
7import org.jupnp.model.meta.LocalDevice
8import org.jupnp.model.meta.RemoteDevice
9
10/**
11 * Platform-agnostic UPnP service surface.
12 *
13 * Android and Desktop holders differ in *how* they start jUPnP:
14 * - Android binds to [org.jupnp.android.AndroidUpnpServiceImpl] through a bound Service and
15 * holds a WifiManager MulticastLock while the app is foregrounded.
16 * - Desktop constructs a plain [org.jupnp.UpnpServiceImpl] directly and doesn't need any
17 * multicast lock — stock Linux networking handles SSDP fine.
18 *
19 * Everything downstream of this (ContentDirectoryClient, MediaCollector, viewmodels)
20 * depends on this interface, not the platform-specific class.
21 */
22interface UpnpServiceHolder {
23
24 /** UPnP MediaServer devices discovered on the LAN — what we BROWSE from. */
25 val servers: StateFlow<List<MediaServer>>
26
27 /** UPnP MediaRenderer devices discovered on the LAN — what we can DRIVE to.
28 * Other Earhorn instances with broadcast on, plus third-party renderers.
29 * Used by the (future) renderer-picker UI. */
30 val renderers: StateFlow<List<RemoteRenderer>>
31
32 val controlPoint: ControlPoint?
33
34 fun findDevice(udn: String): RemoteDevice?
35
36 /** Start the jUPnP stack and begin SSDP discovery. Idempotent. */
37 fun start()
38
39 /** Shut the jUPnP stack down cleanly. Sends BYEBYE messages. */
40 fun stop()
41
42 /** Force an SSDP M-SEARCH. Called from pull-to-refresh / "Rescan" / window focus. */
43 fun rescan()
44
45 /** Register a [LocalDevice] with the registry so this machine advertises itself
46 * on the LAN. Used by [dev.stave.upnp.renderer.EarhornRendererService] when the
47 * user toggles "act as renderer" on. Returns true on success; false if the stack
48 * isn't up or registration failed. Calling with an already-registered device
49 * (same UDN) is a no-op. */
50 fun registerLocalDevice(device: LocalDevice): Boolean
51
52 /** Remove a previously-registered [LocalDevice]. Sends ssdp:byebye so remote
53 * control points know we've gone away. No-op if the device isn't registered. */
54 fun unregisterLocalDevice(device: LocalDevice)
55}