diff --git a/app/build.gradle b/app/build.gradle
index 45569b5b..b39add0a 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -60,6 +60,9 @@ android {
textOutput "stdout"
explainIssues !project.hasProperty("isCI")
}
+ packagingOptions {
+ exclude "META-INF/beans.xml"
+ }
}
tasks.lint.dependsOn(ktlintCheck)
@@ -131,6 +134,13 @@ dependencies {
// Material
implementation 'com.google.android.material:material:1.4.0'
+ // Cling (UPnP/DLNA)
+ implementation "org.fourthline.cling:cling-core:2.1.2"
+ implementation "org.fourthline.cling:cling-support:2.1.2"
+ implementation "org.eclipse.jetty:jetty-servlet:8.2.0.v20160908"
+ implementation "org.eclipse.jetty:jetty-client:8.2.0.v20160908"
+ implementation "org.slf4j:slf4j-android:1.7.32"
+
// Tests
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test:runner:1.4.0'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 100a756a..10327abb 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -8,6 +8,9 @@
+
+
+
+
+
diff --git a/app/src/main/java/me/vanpetegem/accentor/devices/Device.kt b/app/src/main/java/me/vanpetegem/accentor/devices/Device.kt
new file mode 100644
index 00000000..0d0f70a3
--- /dev/null
+++ b/app/src/main/java/me/vanpetegem/accentor/devices/Device.kt
@@ -0,0 +1,63 @@
+package me.vanpetegem.accentor.devices
+
+import androidx.compose.foundation.lazy.rememberLazyListState
+import org.fourthline.cling.model.meta.RemoteDevice
+import org.fourthline.cling.model.meta.RemoteService
+import org.fourthline.cling.model.meta.Service
+import org.fourthline.cling.model.types.ServiceType
+import org.fourthline.cling.model.types.UDN
+import java.lang.Exception
+
+val PLAYER_SERVICE = ServiceType("schemas-upnp-org", "AVTransport", 1);
+
+class Device(
+ protected val clingDevice: RemoteDevice
+) {
+
+ val friendlyName: String = clingDevice.details.friendlyName
+ val displayString: String = clingDevice.displayString
+ val type: String = clingDevice.type.displayString
+ val udn: UDN = clingDevice.identity.udn
+
+ val imageURL: String? = clingDevice
+ .icons
+ .maxWithOrNull(compareBy({ it.height * it.width }, { it.mimeType.subtype == "png" }))
+ ?.let { clingDevice.normalizeURI(it.uri).toString() }
+
+ fun isPlayer(): Boolean {
+ return clingDevice.findServiceTypes().contains(PLAYER_SERVICE)
+ }
+
+ fun isHydrated(): Boolean {
+ return playerService()?.hasActions() == true
+ }
+
+ fun playerService(): RemoteService? {
+ return clingDevice.findService(PLAYER_SERVICE)
+ }
+
+ override fun toString(): String {
+ return "Device($friendlyName, ${clingDevice.findServiceTypes().map { it.type }})"
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (javaClass != other?.javaClass) return false
+
+ other as Device
+
+ if (udn != other.udn) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ return udn.hashCode()
+ }
+
+
+}
+
+
+
+
diff --git a/app/src/main/java/me/vanpetegem/accentor/devices/DeviceManager.kt b/app/src/main/java/me/vanpetegem/accentor/devices/DeviceManager.kt
new file mode 100644
index 00000000..d5b7ea55
--- /dev/null
+++ b/app/src/main/java/me/vanpetegem/accentor/devices/DeviceManager.kt
@@ -0,0 +1,145 @@
+package me.vanpetegem.accentor.devices
+
+
+import android.content.ComponentName
+import android.content.ServiceConnection
+import android.os.IBinder
+import android.util.Log
+import androidx.lifecycle.MutableLiveData
+import org.fourthline.cling.android.AndroidUpnpService
+import org.fourthline.cling.model.action.ActionInvocation
+import org.fourthline.cling.model.message.UpnpResponse
+import org.fourthline.cling.model.message.header.ServiceTypeHeader
+import org.fourthline.cling.model.meta.RemoteDevice
+import org.fourthline.cling.model.meta.Service
+import org.fourthline.cling.model.types.UDN
+import org.fourthline.cling.registry.DefaultRegistryListener
+import org.fourthline.cling.registry.Registry
+import org.fourthline.cling.support.avtransport.callback.GetDeviceCapabilities
+import org.fourthline.cling.support.avtransport.callback.GetMediaInfo
+import org.fourthline.cling.support.avtransport.callback.Play
+import org.fourthline.cling.support.avtransport.callback.SetAVTransportURI
+import org.fourthline.cling.support.avtransport.lastchange.AVTransportVariable
+import org.fourthline.cling.support.model.DIDLContent
+import org.fourthline.cling.support.model.DIDLObject
+import org.fourthline.cling.support.model.DeviceCapabilities
+import org.fourthline.cling.support.model.MediaInfo
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class DeviceManager @Inject constructor() {
+
+ val playerDevices = MutableLiveData