Skip to content

Commit

Permalink
Reformat & Improve Documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
Xerus committed Mar 22, 2019
1 parent 28cb2e4 commit 639ed1e
Show file tree
Hide file tree
Showing 16 changed files with 369 additions and 343 deletions.
13 changes: 3 additions & 10 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
kotlin("jvm") version "1.3.21"
}

sourceSets.main.get().java.srcDir("src")
sourceSets.main.get().java.srcDir("src/main")
sourceSets.test.get().java.srcDir("src/test")

allprojects {
group = "xerus.mpris"
Expand All @@ -20,10 +19,4 @@ allprojects {
dependencies {
compile("com.github.hypfvieh", "dbus-java", "2.7.5")
compile(kotlin("stdlib"))
}

tasks {
withType<KotlinCompile> {
kotlinOptions.jvmTarget = "1.8"
}
}
}
11 changes: 5 additions & 6 deletions extensions/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@

plugins {
application
kotlin("jvm")
application
kotlin("jvm")
}

sourceSets.main.get().java.srcDir("src")
sourceSets.main.get().java.srcDir("test")

application {
mainClassName = "xerus.mpris.DBusPropertyDelegateKt"
mainClassName = "xerus.mpris.DBusPropertyDelegateKt"
}

dependencies {
compile(rootProject)
compile(kotlin("stdlib"))
compile(rootProject)
compile(kotlin("stdlib"))
}
186 changes: 94 additions & 92 deletions extensions/src/xerus/mpris/AbstractMPRISPlayer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,105 +8,107 @@ import org.mpris.MediaPlayer2.*
/** Provides a typesafe foundation for implementing an MPRISPlayer.
*
* Every property inherited from an interface must either be null (if it is nullable and you don't want to implement it)
* or delegated by a [DBusProperty]
* or delegated by a [DBusProperty].
*
* A val represents a Read-only field as declared by MPRIS, it is perfectly valid to implement it as var */
abstract class AbstractMPRISPlayer : MediaPlayerX, PlayerX, DefaultDBus {

val connection: DBusConnection = DBusConnection.getConnection(DBusConnection.SESSION)
val properties = HashMap<String, MutableMap<String, Variant<*>>>()
internal val propertyListeners = HashMap<String, (Any) -> Unit>()

override fun GetAll(interface_name: String) = properties[interface_name]

override fun <A : Any> Set(interface_name: String, property_name: String, value: A) {
super.Set(interface_name, property_name, value)
propertyListeners[property_name]?.invoke(value)
}

fun exportAs(playerName: String) {
connection.requestBusName("org.mpris.MediaPlayer2.$playerName")
connection.exportObject("/org/mpris/MediaPlayer2", this)
}

/** sends a [DBus.Properties.PropertiesChanged] signal via [connection] */
override fun propertyChanged(interface_name: String, property_name: String) =
super.propertyChanged(interface_name, property_name).also { connection.sendSignal(it) }

override val hasTrackList by DBusConstant(this is TrackList)

* A val represents a Read-only field as declared by MPRIS, it is perfectly valid to implement it as var.
* */
abstract class AbstractMPRISPlayer: MediaPlayerX, PlayerX, DefaultDBus {

val connection: DBusConnection = DBusConnection.getConnection(DBusConnection.SESSION)
val properties = HashMap<String, MutableMap<String, Variant<*>>>()
internal val propertyListeners = HashMap<String, (Any) -> Unit>()

override fun GetAll(interface_name: String) = properties[interface_name]

override fun <A: Any> Set(interface_name: String, property_name: String, value: A) {
super.Set(interface_name, property_name, value)
propertyListeners[property_name]?.invoke(value)
}

fun exportAs(playerName: String) {
connection.requestBusName("org.mpris.MediaPlayer2.$playerName")
connection.exportObject("/org/mpris/MediaPlayer2", this)
}

/** sends a [DBus.Properties.PropertiesChanged] signal via [connection] */
override fun propertyChanged(interface_name: String, property_name: String) =
super.propertyChanged(interface_name, property_name).also { connection.sendSignal(it) }

override val hasTrackList by DBusConstant(this is TrackList)

}

interface PlayerX : Player {
/** Whether the media player may be controlled over this interface.
* Setting this to false assumes all properties as Read-only */
val canControl: Boolean
val canGoNext: Boolean
val canGoPrevious: Boolean
val canPlay: Boolean
val canPause: Boolean
val canSeek: Boolean

val playbackStatus: PlaybackStatus
/** _Optional_ */
var loopStatus: LoopStatus
/** A value of false indicates that playback is progressing linearly through a playlist,
* while true means playback is progressing through a playlist in some other order.
*
* _Optional_ */
var shuffle: Boolean
var volume: Double
/** The current track position in microseconds, between 0 and the 'mpris:length' metadata entry (see [metadata]. */
val position: Long
/** Metadata of the current Track.
* [https://www.freedesktop.org/wiki/Specifications/mpris-spec/metadata/#index2h2] */
val metadata: Map<String, Variant<*>>

/** The current playback rate.
*
* The value must fall in the range described by [minimumRate] and [maximumRate], and must not be 0.0 */
var rate: Double
/** The minimum value which the [rate] property can take. */
val minimumRate: Double
/** The maximum value which the [rate] property can take. */
val maximumRate: Double

interface PlayerX: Player {
/** Whether the media player may be controlled over this interface.
* Setting this to false assumes all properties as Read-only */
val canControl: Boolean
val canGoNext: Boolean
val canGoPrevious: Boolean
val canPlay: Boolean
val canPause: Boolean
val canSeek: Boolean
val playbackStatus: PlaybackStatus
/** _Optional_ */
var loopStatus: LoopStatus
/** A value of false indicates that playback is progressing linearly through a playlist,
* while true means playback is progressing through a playlist in some other order.
*
* _Optional_ */
var shuffle: Boolean
var volume: Double
/** The current track position in microseconds, between 0 and the 'mpris:length' metadata entry (see [metadata]. */
val position: Long
/** Metadata of the current Track.
* [https://www.freedesktop.org/wiki/Specifications/mpris-spec/metadata/#index2h2] */
val metadata: Map<String, Variant<*>>
/** The current playback rate.
*
* The value must fall in the range described by [minimumRate] and [maximumRate], and must not be 0.0 */
var rate: Double
/** The minimum value which the [rate] property can take. */
val minimumRate: Double
/** The maximum value which the [rate] property can take. */
val maximumRate: Double
}

interface MediaPlayerX : MediaPlayer2 {
val supportedUriSchemes: Array<String>
val supportedMimeTypes: Array<String>
/** Indicates whether this object implements the org.mpris.MediaPlayer2.TrackList interface. */
val hasTrackList: Boolean

val canRaise: Boolean
val canQuit: Boolean
/** _Optional_ */
val canSetFullscreen: Boolean

/** _Optional_ */
var fullscreen: Boolean
/** A friendly name to identify the media player to users.
* This should usually match the name found in the [desktopEntry]. */
val identity: String
/** The basename of an installed .desktop file which complies with the Desktop entry
* specification, with the ".desktop" extension stripped.
*
* _Optional_ */
val desktopEntry: String
interface MediaPlayerX: MediaPlayer2 {
val supportedUriSchemes: Array<String>
val supportedMimeTypes: Array<String>
/** Indicates whether this object implements the org.mpris.MediaPlayer2.TrackList interface. */
val hasTrackList: Boolean
val canRaise: Boolean
val canQuit: Boolean
/** _Optional_ */
val canSetFullscreen: Boolean
/** _Optional_ */
var fullscreen: Boolean
/** A friendly name to identify the media player to users.
* This should usually match the name found in the [desktopEntry]. */
val identity: String
/** The basename of an installed .desktop file which complies with the Desktop entry
* specification, with the ".desktop" extension stripped.
*
* _Optional_ */
val desktopEntry: String
}

interface TrackListX : TrackList {
/** If false, calling AddTrack or RemoveTrack will have no effect, and may raise a NotSupported error. */
val canEditTracks: Boolean

/** An array which contains the identifier of each track in this [TrackList], in order. */
val tracks: Array<TrackId>
/** Extension of the [TrackList] interface which adds its properties. */
interface TrackListX: TrackList {
/** If false, calling AddTrack or RemoveTrack will have no effect, and may raise a NotSupported error. */
val canEditTracks: Boolean

/** An array which contains the identifier of each track in this [TrackList], in order. */
val tracks: Array<TrackId>
}

interface PlaylistsX : Playlists {
val orderings: Array<PlaylistOrdering>

val playlistCount: Int
val activePlaylist: MaybePlaylist
/** Extension of the [Playlists] interface which adds its properties. */
interface PlaylistsX: Playlists {
val orderings: Array<PlaylistOrdering>
val playlistCount: Int
val activePlaylist: MaybePlaylist
}
51 changes: 26 additions & 25 deletions extensions/src/xerus/mpris/DBusPropertyDelegate.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,89 +19,90 @@ val logger = LoggerFactory.getLogger("xerus.mpris.properties")
private fun findInterface(clazz: Class<*>, name: String): Class<*>? =
(clazz.interfaces + clazz.superclass).firstOrNull {
if(it == null) return@firstOrNull false
if (it.declaredMethods.any { it.name.contains(name) }) {
if(it.declaredMethods.any { it.name.contains(name) }) {
logger.trace("Found $it for property $name")
return it.interfaces.first()
}
findInterface(it, name) != null
}

class DBusProperty<T : Any>(private val initial: T, private val observable: ObservableValue<T>? = null, private val onSet: ((T) -> Unit)? = null) {

constructor(observable: ObservableValue<T>, onSet: ((T) -> Unit)? = null) : this(observable.value, observable, onSet)

class DBusProperty<T: Any>(private val initial: T, private val observable: ObservableValue<T>? = null, private val onSet: ((T) -> Unit)? = null) {
constructor(observable: ObservableValue<T>, onSet: ((T) -> Unit)? = null): this(observable.value, observable, onSet)
operator fun provideDelegate(
thisRef: AbstractMPRISPlayer,
prop: KProperty<*>
thisRef: AbstractMPRISPlayer,
prop: KProperty<*>
): ReadWriteProperty<AbstractMPRISPlayer, T> {
val name = prop.name.capitalize()
val clazz = findInterface(thisRef::class.java, name)
?: throw RuntimeException("No interface found for Property $name")
?: throw RuntimeException("No interface found for Property $name")
val interfaceName = (clazz.annotations.find { it is DBusInterfaceName } as? DBusInterfaceName)?.value
?: clazz.name
?: clazz.name
logger.trace("Registered Property ${prop.name} for $interfaceName")
thisRef.properties.getOrPut(interfaceName) { HashMap() }[name] = initial.variant()
val property = Property<T>(interfaceName, name)
if (onSet != null)
if(onSet != null)
thisRef.propertyListeners[name] = onSet as ((Any) -> Unit)
observable?.addListener { _, _, new -> property.setValue(thisRef, prop, new) }
return property
}

}

class DBusMapProperty<K, V>(private val initial: Map<K, V> = HashMap<K, V>(), private val observable: ObservableValue<Map<K, V>>? = null, private val onSet: ((Map<K, V>) -> Unit)? = null) {

constructor(observable: ObservableValue<Map<K, V>>, onSet: ((Map<K, V>) -> Unit)? = null) : this(observable.value, observable, onSet)
constructor(observable: ObservableValue<Map<K, V>>, onSet: ((Map<K, V>) -> Unit)? = null): this(observable.value, observable, onSet)

operator fun provideDelegate(
thisRef: AbstractMPRISPlayer,
prop: KProperty<*>
thisRef: AbstractMPRISPlayer,
prop: KProperty<*>
): ReadWriteProperty<AbstractMPRISPlayer, Map<K, V>> {
val name = prop.name.capitalize()
val clazz = findInterface(thisRef::class.java, name)
?: throw RuntimeException("No interface found for Property $name")
?: throw RuntimeException("No interface found for Property $name")
val interfaceName = (clazz.annotations.find { it is DBusInterfaceName } as? DBusInterfaceName)?.value
?: clazz.name
?: clazz.name
logger.trace("Registered Property ${prop.name} for $interfaceName")
//thisRef.properties.getOrPut(interfaceName) { HashMap() }[name] = initial.variant()
val property = Property<Map<K, V>>(interfaceName, name)
if (onSet != null)
if(onSet != null)
thisRef.propertyListeners[name] = onSet as ((Any) -> Unit)
observable?.addListener { _, _, new -> property.setValue(thisRef, prop, new) }
return property
}

}

class DBusConstant<out T : Any>(private val value: T) {
class DBusConstant<out T: Any>(private val value: T) {

operator fun provideDelegate(
thisRef: AbstractMPRISPlayer,
prop: KProperty<*>
thisRef: AbstractMPRISPlayer,
prop: KProperty<*>
): ReadOnlyProperty<AbstractMPRISPlayer, T> {
val name = prop.name.capitalize()
val clazz = findInterface(thisRef::class.java, name)
?: throw RuntimeException("No interface found for Property $name")
?: throw RuntimeException("No interface found for Property $name")
val interfaceName = (clazz.annotations.find { it is DBusInterfaceName } as? DBusInterfaceName)?.value
?: clazz.name
?: clazz.name
logger.trace("Registered Constant ${prop.name} for $interfaceName")
thisRef.properties.getOrPut(interfaceName) { HashMap() }.put(name, value.variant())
return Constant(value)
}
}

private class Property<T : Any>(private val interfaceName: String, private val name: String) : ReadWriteProperty<AbstractMPRISPlayer, T> {
private class Property<T: Any>(private val interfaceName: String, private val name: String): ReadWriteProperty<AbstractMPRISPlayer, T> {

override fun setValue(thisRef: AbstractMPRISPlayer, property: KProperty<*>, value: T) {
if (thisRef.properties.getValue(interfaceName).put(name, value.variant())?.value != value)
if(thisRef.properties.getValue(interfaceName).put(name, value.variant())?.value != value)
thisRef.connection.sendSignal(thisRef.propertyChanged(interfaceName, name))
}

override fun getValue(thisRef: AbstractMPRISPlayer, property: KProperty<*>) =
thisRef.properties.getValue(interfaceName).getValue(name).value as T
thisRef.properties.getValue(interfaceName).getValue(name).value as T

}

private class Constant<out T : Any>(private val value: T) : ReadOnlyProperty<AbstractMPRISPlayer, T> {
private class Constant<out T: Any>(private val value: T): ReadOnlyProperty<AbstractMPRISPlayer, T> {
override fun getValue(thisRef: AbstractMPRISPlayer, property: KProperty<*>) = value
}
Loading

0 comments on commit 639ed1e

Please sign in to comment.