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 }