ha-android/common/src/main/java/io/homeassistant/companion/android/common/sensors/AudioSensorManager.kt

465 lines
16 KiB
Kotlin

package io.homeassistant.companion.android.common.sensors
import android.content.Context
import android.media.AudioDeviceInfo
import android.media.AudioManager
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.core.content.getSystemService
import io.homeassistant.companion.android.common.R as commonR
import io.homeassistant.companion.android.common.util.STATE_UNKNOWN
class AudioSensorManager : SensorManager {
companion object {
private const val TAG = "AudioSensor"
const val VOLUME_CHANGED_ACTION = "android.media.VOLUME_CHANGED_ACTION"
val audioSensor = SensorManager.BasicSensor(
"audio_sensor",
"sensor",
commonR.string.sensor_name_ringer_mode,
commonR.string.sensor_description_audio_sensor,
"mdi:volume-high",
updateType = SensorManager.BasicSensor.UpdateType.INTENT
)
private val audioState = SensorManager.BasicSensor(
"audio_mode",
"sensor",
commonR.string.sensor_name_audio_mode,
commonR.string.sensor_description_audio_mode,
"mdi:volume-high"
)
private val headphoneState = SensorManager.BasicSensor(
"headphone_state",
"binary_sensor",
commonR.string.sensor_name_headphone,
commonR.string.sensor_description_headphone,
"mdi:headphones",
updateType = SensorManager.BasicSensor.UpdateType.INTENT
)
val micMuted = SensorManager.BasicSensor(
"mic_muted",
"binary_sensor",
commonR.string.sensor_name_mic_muted,
commonR.string.sensor_description_mic_muted,
"mdi:microphone-off",
updateType =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
SensorManager.BasicSensor.UpdateType.INTENT
} else {
SensorManager.BasicSensor.UpdateType.WORKER
}
)
private val musicActive = SensorManager.BasicSensor(
"music_active",
"binary_sensor",
commonR.string.sensor_name_music_active,
commonR.string.sensor_description_music_active,
"mdi:music"
)
val speakerphoneState = SensorManager.BasicSensor(
"speakerphone_state",
"binary_sensor",
commonR.string.sensor_name_speakerphone,
commonR.string.sensor_description_speakerphone,
"mdi:volume-high",
updateType =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
SensorManager.BasicSensor.UpdateType.INTENT
} else {
SensorManager.BasicSensor.UpdateType.WORKER
}
)
val volAlarm = SensorManager.BasicSensor(
"volume_alarm",
"sensor",
commonR.string.sensor_name_volume_alarm,
commonR.string.sensor_description_volume_alarm,
"mdi:alarm",
entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC,
updateType = SensorManager.BasicSensor.UpdateType.INTENT
)
val volCall = SensorManager.BasicSensor(
"volume_call",
"sensor",
commonR.string.sensor_name_volume_call,
commonR.string.sensor_description_volume_call,
"mdi:phone",
entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC,
updateType = SensorManager.BasicSensor.UpdateType.INTENT
)
val volMusic = SensorManager.BasicSensor(
"volume_music",
"sensor",
commonR.string.sensor_name_volume_music,
commonR.string.sensor_description_volume_music,
"mdi:music",
entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC,
updateType = SensorManager.BasicSensor.UpdateType.INTENT
)
val volRing = SensorManager.BasicSensor(
"volume_ring",
"sensor",
commonR.string.sensor_name_volume_ring,
commonR.string.sensor_description_volume_ring,
"mdi:phone-ring",
entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC,
updateType = SensorManager.BasicSensor.UpdateType.INTENT
)
val volNotification = SensorManager.BasicSensor(
"volume_notification",
"sensor",
commonR.string.sensor_name_volume_notification,
commonR.string.sensor_description_volume_notification,
"mdi:bell-ring",
entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC,
updateType = SensorManager.BasicSensor.UpdateType.INTENT
)
val volSystem = SensorManager.BasicSensor(
"volume_system",
"sensor",
commonR.string.sensor_name_volume_system,
commonR.string.sensor_description_volume_system,
"mdi:cellphone-sound",
entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC,
updateType = SensorManager.BasicSensor.UpdateType.INTENT
)
val volAccessibility = SensorManager.BasicSensor(
"volume_accessibility",
"sensor",
commonR.string.sensor_name_volume_accessibility,
commonR.string.sensor_description_volume_accessibility,
"mdi:human",
entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC,
updateType = SensorManager.BasicSensor.UpdateType.INTENT
)
val volDTMF = SensorManager.BasicSensor(
"volume_dtmf",
"sensor",
commonR.string.sensor_name_volume_dtmf,
commonR.string.sensor_description_volume_dtmf,
"mdi:volume-high",
entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC,
updateType = SensorManager.BasicSensor.UpdateType.INTENT
)
}
override fun docsLink(): String {
return "https://companion.home-assistant.io/docs/core/sensors#audio-sensors"
}
override val name: Int
get() = commonR.string.sensor_name_audio
override suspend fun getAvailableSensors(context: Context): List<SensorManager.BasicSensor> {
val allSupportedSensors = listOf(
audioSensor, audioState, headphoneState, micMuted, speakerphoneState,
musicActive, volAlarm, volCall, volMusic, volRing, volNotification, volSystem,
volDTMF
)
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
allSupportedSensors.plus(volAccessibility)
} else {
allSupportedSensors
}
}
override fun requiredPermissions(sensorId: String): Array<String> {
return emptyArray()
}
override fun requestSensorUpdate(context: Context) {
val audioManager = context.getSystemService<AudioManager>()!!
updateAudioSensor(context, audioManager)
updateAudioState(context, audioManager)
updateHeadphoneState(context, audioManager)
updateMicMuted(context, audioManager)
updateMusicActive(context, audioManager)
updateSpeakerphoneState(context, audioManager)
updateVolumeAlarm(context, audioManager)
updateVolumeCall(context, audioManager)
updateVolumeMusic(context, audioManager)
updateVolumeRing(context, audioManager)
updateVolumeNotification(context, audioManager)
updateVolumeSystem(context, audioManager)
updateVolumeDTMF(context, audioManager)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
updateVolumeAccessibility(context, audioManager)
}
}
private fun updateAudioSensor(context: Context, audioManager: AudioManager) {
if (!isEnabled(context, audioSensor)) {
return
}
val ringerMode = when (audioManager.ringerMode) {
AudioManager.RINGER_MODE_NORMAL -> "normal"
AudioManager.RINGER_MODE_SILENT -> "silent"
AudioManager.RINGER_MODE_VIBRATE -> "vibrate"
else -> STATE_UNKNOWN
}
val icon = when (audioManager.ringerMode) {
AudioManager.RINGER_MODE_NORMAL -> "mdi:volume-high"
AudioManager.RINGER_MODE_SILENT -> "mdi:volume-off"
AudioManager.RINGER_MODE_VIBRATE -> "mdi:vibrate"
else -> "mdi:volume-low"
}
onSensorUpdated(
context,
audioSensor,
ringerMode,
icon,
mapOf()
)
}
private fun updateAudioState(context: Context, audioManager: AudioManager) {
if (!isEnabled(context, audioState)) {
return
}
val audioMode = when (audioManager.mode) {
AudioManager.MODE_NORMAL -> "normal"
AudioManager.MODE_RINGTONE -> "ringing"
AudioManager.MODE_IN_CALL -> "in_call"
AudioManager.MODE_IN_COMMUNICATION -> "in_communication"
AudioManager.MODE_CALL_SCREENING -> "call_screening"
AudioManager.MODE_CALL_REDIRECT -> "call_redirect"
AudioManager.MODE_COMMUNICATION_REDIRECT -> "communication_redirect"
else -> STATE_UNKNOWN
}
val icon = when (audioManager.mode) {
AudioManager.MODE_NORMAL -> "mdi:volume-high"
AudioManager.MODE_RINGTONE -> "mdi:phone-ring"
AudioManager.MODE_IN_CALL -> "mdi:phone"
AudioManager.MODE_IN_COMMUNICATION -> "mdi:message-video"
AudioManager.MODE_CALL_SCREENING -> "mdi:microphone-message"
AudioManager.MODE_CALL_REDIRECT -> "mdi:phone"
AudioManager.MODE_COMMUNICATION_REDIRECT -> "mdi:message-video"
else -> "mdi:volume-low"
}
onSensorUpdated(
context,
audioState,
audioMode,
icon,
mapOf()
)
}
private fun updateHeadphoneState(context: Context, audioManager: AudioManager) {
if (!isEnabled(context, headphoneState)) {
return
}
var isHeadphones = false
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val audioDevices = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)
for (deviceInfo in audioDevices) {
if (deviceInfo.type == AudioDeviceInfo.TYPE_WIRED_HEADPHONES || deviceInfo.type == AudioDeviceInfo.TYPE_WIRED_HEADSET || deviceInfo.type == AudioDeviceInfo.TYPE_USB_HEADSET) {
isHeadphones = true
}
}
} else {
@Suppress("DEPRECATION")
isHeadphones = audioManager.isWiredHeadsetOn
}
val icon = if (isHeadphones) "mdi:headphones" else "mdi:headphones-off"
onSensorUpdated(
context,
headphoneState,
isHeadphones,
icon,
mapOf()
)
}
private fun updateMicMuted(context: Context, audioManager: AudioManager) {
if (!isEnabled(context, micMuted)) {
return
}
val isMicMuted = audioManager.isMicrophoneMute
val icon = if (!isMicMuted) "mdi:microphone" else "mdi:microphone-off"
onSensorUpdated(
context,
micMuted,
isMicMuted,
icon,
mapOf()
)
}
private fun updateMusicActive(context: Context, audioManager: AudioManager) {
if (!isEnabled(context, musicActive)) {
return
}
val isMusicActive = audioManager.isMusicActive
val icon = if (isMusicActive) "mdi:music" else "mdi:music-off"
onSensorUpdated(
context,
musicActive,
isMusicActive,
icon,
mapOf()
)
}
private fun updateSpeakerphoneState(context: Context, audioManager: AudioManager) {
if (!isEnabled(context, speakerphoneState)) {
return
}
val isSpeakerOn = audioManager.isSpeakerphoneOn
val icon = if (isSpeakerOn) "mdi:volume-high" else "mdi:volume-off"
onSensorUpdated(
context,
speakerphoneState,
isSpeakerOn,
icon,
mapOf()
)
}
private fun updateVolumeAlarm(context: Context, audioManager: AudioManager) {
if (!isEnabled(context, volAlarm)) {
return
}
val volumeLevelAlarm = audioManager.getStreamVolume(AudioManager.STREAM_ALARM)
onSensorUpdated(
context,
volAlarm,
volumeLevelAlarm,
volAlarm.statelessIcon,
mapOf()
)
}
private fun updateVolumeCall(context: Context, audioManager: AudioManager) {
if (!isEnabled(context, volCall)) {
return
}
val volumeLevelCall = audioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL)
onSensorUpdated(
context,
volCall,
volumeLevelCall,
volCall.statelessIcon,
mapOf()
)
}
private fun updateVolumeMusic(context: Context, audioManager: AudioManager) {
if (!isEnabled(context, volMusic)) {
return
}
val volumeLevelMusic = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
onSensorUpdated(
context,
volMusic,
volumeLevelMusic,
volMusic.statelessIcon,
mapOf()
)
}
private fun updateVolumeRing(context: Context, audioManager: AudioManager) {
if (!isEnabled(context, volRing)) {
return
}
val volumeLevelRing = audioManager.getStreamVolume(AudioManager.STREAM_RING)
onSensorUpdated(
context,
volRing,
volumeLevelRing,
volRing.statelessIcon,
mapOf()
)
}
private fun updateVolumeNotification(context: Context, audioManager: AudioManager) {
if (!isEnabled(context, volNotification)) {
return
}
val volumeLevelNotification = audioManager.getStreamVolume(AudioManager.STREAM_NOTIFICATION)
onSensorUpdated(
context,
volNotification,
volumeLevelNotification,
volNotification.statelessIcon,
mapOf()
)
}
private fun updateVolumeSystem(context: Context, audioManager: AudioManager) {
if (!isEnabled(context, volSystem)) {
return
}
val volumeLevelSystem = audioManager.getStreamVolume(AudioManager.STREAM_SYSTEM)
onSensorUpdated(
context,
volSystem,
volumeLevelSystem,
volSystem.statelessIcon,
mapOf()
)
}
@RequiresApi(Build.VERSION_CODES.O)
private fun updateVolumeAccessibility(context: Context, audioManager: AudioManager) {
if (!isEnabled(context, volAccessibility)) {
return
}
val volumeLevelAccessibility = audioManager.getStreamVolume(AudioManager.STREAM_ACCESSIBILITY)
onSensorUpdated(
context,
volAccessibility,
volumeLevelAccessibility,
volAccessibility.statelessIcon,
mapOf()
)
}
private fun updateVolumeDTMF(context: Context, audioManager: AudioManager) {
if (!isEnabled(context, volDTMF)) {
return
}
val volumeLevelDTMF = audioManager.getStreamVolume(AudioManager.STREAM_DTMF)
onSensorUpdated(
context,
volDTMF,
volumeLevelDTMF,
volDTMF.statelessIcon,
mapOf()
)
}
}