Warning, /network/kdeconnect-android/src/org/kde/kdeconnect/Plugins/SystemVolumePlugin/SystemVolumeProvider.kt is written in an unsupported language. File is not indexed.

0001 /*
0002  * SPDX-FileCopyrightText: 2021 Art Pinch <leonardo90690@gmail.com>
0003  *
0004  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005  */
0006 
0007 package org.kde.kdeconnect.Plugins.SystemVolumePlugin
0008 
0009 import android.media.AudioManager
0010 import androidx.media.VolumeProviderCompat
0011 import org.kde.kdeconnect.Helpers.DEFAULT_MAX_VOLUME
0012 import org.kde.kdeconnect.Helpers.DEFAULT_VOLUME_STEP
0013 import org.kde.kdeconnect.Helpers.calculateNewVolume
0014 import org.kde.kdeconnect.Plugins.SystemVolumePlugin.Sink.UpdateListener
0015 import org.kde.kdeconnect.Plugins.SystemVolumePlugin.SystemVolumePlugin.SinkListener
0016 import kotlin.math.ceil
0017 import kotlin.math.floor
0018 
0019 internal class SystemVolumeProvider private constructor(plugin: SystemVolumePlugin) :
0020         VolumeProviderCompat(VOLUME_CONTROL_ABSOLUTE, DEFAULT_MAX_VOLUME, 0),
0021         SinkListener,
0022         UpdateListener {
0023 
0024     interface ProviderStateListener {
0025         fun onProviderStateChanged(systemVolumeProvider: SystemVolumeProvider, isActive: Boolean)
0026     }
0027 
0028     companion object {
0029         @JvmStatic
0030         var currentProvider: SystemVolumeProvider? = null
0031             private set
0032 
0033         @JvmStatic
0034         fun fromPlugin(systemVolumePlugin: SystemVolumePlugin): SystemVolumeProvider {
0035             val currentProvider = currentProvider ?: SystemVolumeProvider(systemVolumePlugin)
0036 
0037             currentProvider.update(systemVolumePlugin)
0038 
0039             return currentProvider
0040         }
0041 
0042         private fun scale(value: Int, maxValue: Int, maxScaled: Int): Int {
0043             val floatingResult = value * maxScaled / maxValue.toDouble()
0044             return if (maxScaled > maxValue) {
0045                 ceil(floatingResult).toInt()
0046             } else {
0047                 floor(floatingResult).toInt()
0048             }
0049         }
0050     }
0051 
0052     private val stateListeners: MutableList<ProviderStateListener>
0053 
0054     private var defaultSink: Sink? = null
0055 
0056     private var systemVolumePlugin: SystemVolumePlugin
0057 
0058     init {
0059         systemVolumePlugin = plugin
0060         stateListeners = mutableListOf()
0061     }
0062 
0063     private fun update(plugin: SystemVolumePlugin) {
0064         if (plugin === systemVolumePlugin) return
0065 
0066         propagateState(false)
0067         defaultSink = null
0068         stopListeningForSinks()
0069         systemVolumePlugin = plugin
0070         startListeningForSinks()
0071     }
0072 
0073     override fun sinksChanged() {
0074         for (sink in systemVolumePlugin.sinks) {
0075             sink.addListener(this)
0076         }
0077 
0078         val newDefaultSink = getDefaultSink(systemVolumePlugin)
0079 
0080         newDefaultSink?.also {
0081             updateLocalVolume(it)
0082         }
0083 
0084         if ((newDefaultSink == null) xor (defaultSink == null)) {
0085             val volumeAdjustSupported = isVolumeAdjustSupported(newDefaultSink)
0086             propagateState(volumeAdjustSupported)
0087         }
0088         defaultSink = newDefaultSink
0089     }
0090 
0091     override fun updateSink(sink: Sink) {
0092         if (!sink.isDefault) return
0093         defaultSink = sink
0094         updateLocalVolume(sink)
0095     }
0096 
0097     override fun onAdjustVolume(direction: Int) {
0098         val step = when (direction) {
0099             AudioManager.ADJUST_RAISE -> DEFAULT_VOLUME_STEP
0100             AudioManager.ADJUST_LOWER -> -DEFAULT_VOLUME_STEP
0101             else -> return
0102         }
0103         val newVolume = calculateNewVolume(currentVolume, maxVolume, step)
0104         onSetVolumeTo(newVolume)
0105     }
0106 
0107     override fun onSetVolumeTo(volume: Int) {
0108         updateLocalAndRemoteVolume(defaultSink, volume)
0109     }
0110 
0111     private fun updateLocalAndRemoteVolume(sink: Sink?, volume: Int) {
0112         val shouldUpdateRemote = updateLocalVolume(volume)
0113         if (!shouldUpdateRemote || sink == null) return
0114         val remoteVolume = scaleFromLocal(volume, sink.maxVolume)
0115         systemVolumePlugin.sendVolume(sink.name, remoteVolume)
0116     }
0117 
0118     private fun updateLocalVolume(volume: Int): Boolean {
0119         if (currentVolume == volume) return false
0120         currentVolume = volume
0121         return true
0122     }
0123 
0124     private fun updateLocalVolume(sink: Sink) {
0125         val localVolume = scaleToLocal(sink.volume, sink.maxVolume)
0126         updateLocalVolume(localVolume)
0127     }
0128 
0129     private fun scaleToLocal(value: Int, maxValue: Int): Int {
0130         return scale(value, maxValue, maxVolume)
0131     }
0132 
0133     private fun scaleFromLocal(value: Int, maxScaled: Int): Int {
0134         return scale(value, maxVolume, maxScaled)
0135     }
0136 
0137     fun addStateListener(l: ProviderStateListener) {
0138         if (!stateListeners.contains(l)) {
0139             stateListeners.add(l)
0140             l.onProviderStateChanged(this, isVolumeAdjustSupported(defaultSink))
0141         }
0142     }
0143 
0144     fun removeStateListener(l: ProviderStateListener) {
0145         stateListeners.remove(l)
0146     }
0147 
0148     private fun propagateState(state: Boolean) {
0149         for (listener in stateListeners) {
0150             listener.onProviderStateChanged(this, state)
0151         }
0152     }
0153 
0154     private fun isVolumeAdjustSupported(sink: Sink?): Boolean {
0155         return sink != null
0156     }
0157 
0158     fun startListeningForSinks() {
0159         systemVolumePlugin.addSinkListener(this)
0160         systemVolumePlugin.requestSinkList()
0161     }
0162 
0163     fun release() {
0164         stopListeningForSinks()
0165         stateListeners.clear()
0166         currentProvider = null
0167     }
0168 
0169     private fun stopListeningForSinks() {
0170         for (sink in systemVolumePlugin.sinks) {
0171             sink.removeListener(this)
0172         }
0173         systemVolumePlugin.removeSinkListener(this)
0174     }
0175 }