diff --git a/app/src/main/java/io/homeassistant/companion/android/sensors/AudioSensorManager.kt b/app/src/main/java/io/homeassistant/companion/android/sensors/AudioSensorManager.kt index 8f9b0bb46..7a000f669 100644 --- a/app/src/main/java/io/homeassistant/companion/android/sensors/AudioSensorManager.kt +++ b/app/src/main/java/io/homeassistant/companion/android/sensors/AudioSensorManager.kt @@ -13,9 +13,63 @@ class AudioSensorManager : SensorManager { private val audioSensor = SensorManager.BasicSensor( "audio_sensor", "sensor", - R.string.sensor_name_audio, + R.string.sensor_name_ringer_mode, R.string.sensor_description_audio_sensor ) + private val audioState = SensorManager.BasicSensor( + "audio_mode", + "sensor", + R.string.sensor_name_audio_mode, + R.string.sensor_description_audio_mode + ) + private val headphoneState = SensorManager.BasicSensor( + "headphone_state", + "binary_sensor", + R.string.sensor_name_headphone, + R.string.sensor_description_headphone + ) + private val micMuted = SensorManager.BasicSensor( + "mic_muted", + "binary_sensor", + R.string.sensor_name_mic_muted, + R.string.sensor_description_mic_muted + ) + private val musicActive = SensorManager.BasicSensor( + "music_active", + "binary_sensor", + R.string.sensor_name_music_active, + R.string.sensor_description_music_active + ) + private val speakerphoneState = SensorManager.BasicSensor( + "speakerphone_state", + "binary_sensor", + R.string.sensor_name_speakerphone, + R.string.sensor_description_speakerphone + ) + private val volAlarm = SensorManager.BasicSensor( + "volume_alarm", + "sensor", + R.string.sensor_name_volume_alarm, + R.string.sensor_description_volume_alarm + ) + private val volCall = SensorManager.BasicSensor( + "volume_call", + "sensor", + R.string.sensor_name_volume_call, + R.string.sensor_description_volume_call + ) + private val volMusic = SensorManager.BasicSensor( + "volume_music", + "sensor", + R.string.sensor_name_volume_music, + R.string.sensor_description_volume_music + ) + private val volRing = SensorManager.BasicSensor( + "volume_ring", + "sensor", + R.string.sensor_name_volume_ring, + R.string.sensor_description_volume_ring + ) } override val enabledByDefault: Boolean @@ -25,20 +79,29 @@ class AudioSensorManager : SensorManager { get() = R.string.sensor_name_audio override val availableSensors: List - get() = listOf(audioSensor) + get() = listOf(audioSensor, audioState, headphoneState, micMuted, speakerphoneState, musicActive, volAlarm, volCall, volMusic, volRing) override fun requiredPermissions(): Array { return emptyArray() } override fun requestSensorUpdate(context: Context) { - updateAudioSensor(context) + val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as 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) } - private fun updateAudioSensor(context: Context) { + private fun updateAudioSensor(context: Context, audioManager: AudioManager) { if (!isEnabled(context, audioSensor.id)) return - val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager val audioMode = when (audioManager.mode) { AudioManager.MODE_NORMAL -> "normal" AudioManager.MODE_RINGTONE -> "ringing" @@ -88,16 +151,180 @@ class AudioSensorManager : SensorManager { ringerMode, icon, mapOf( - "audio_mode" to audioMode, - "is_headphones" to isHeadphones, - "is_mic_muted" to isMicMuted, - "is_music_active" to isMusicActive, - "is_speakerphone_on" to isSpeakerOn, - "volume_level_alarm" to volumeLevelAlarm, - "volume_level_call" to volumeLevelCall, - "volume_level_music" to volumeLevelMusic, - "volume_level_ring" to volumeLevelRing + "audio_mode" to audioMode, // Remove after next release + "is_headphones" to isHeadphones, // Remove after next release + "is_mic_muted" to isMicMuted, // Remove after next release + "is_music_active" to isMusicActive, // Remove after next release + "is_speakerphone_on" to isSpeakerOn, // Remove after next release + "volume_level_alarm" to volumeLevelAlarm, // Remove after next release + "volume_level_call" to volumeLevelCall, // Remove after next release + "volume_level_music" to volumeLevelMusic, // Remove after next release + "volume_level_ring" to volumeLevelRing // Remove after next release ) ) } + + private fun updateAudioState(context: Context, audioManager: AudioManager) { + if (!isEnabled(context, audioState.id)) + 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" + else -> "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" + else -> "mdi:volume-low" + } + + onSensorUpdated(context, + audioState, + audioMode, + icon, + mapOf() + ) + } + + private fun updateHeadphoneState(context: Context, audioManager: AudioManager) { + if (!isEnabled(context, headphoneState.id)) + return + + var isHeadphones = false + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + val audioDevices = audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS) + 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 { + // Use deprecated method as getDevices is API 23 and up only and we support API 21 + 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.id)) + 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.id)) + 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.id)) + 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.id)) + return + val volumeLevelAlarm = audioManager.getStreamVolume(AudioManager.STREAM_ALARM) + + val icon = "mdi:alarm" + + onSensorUpdated(context, + volAlarm, + volumeLevelAlarm, + icon, + mapOf() + ) + } + + private fun updateVolumeCall(context: Context, audioManager: AudioManager) { + if (!isEnabled(context, volCall.id)) + return + + val volumeLevelCall = audioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL) + + val icon = "mdi:phone" + + onSensorUpdated(context, + volCall, + volumeLevelCall, + icon, + mapOf() + ) + } + + private fun updateVolumeMusic(context: Context, audioManager: AudioManager) { + if (!isEnabled(context, volMusic.id)) + return + + val volumeLevelMusic = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) + + val icon = "mdi:music" + + onSensorUpdated(context, + volMusic, + volumeLevelMusic, + icon, + mapOf() + ) + } + + private fun updateVolumeRing(context: Context, audioManager: AudioManager) { + if (!isEnabled(context, volRing.id)) + return + + val volumeLevelRing = audioManager.getStreamVolume(AudioManager.STREAM_RING) + + val icon = "mdi:phone-ring" + + onSensorUpdated(context, + volRing, + volumeLevelRing, + icon, + mapOf() + ) + } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7d901af82..e2db07dc5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -141,7 +141,16 @@ for Home Assistant Select the instance you would like to connect to: Description - The state of the devices ringer mode as well as other audio related attributes + The state of the devices ringer mode + The state of the devices audio mode + Whether or not headphones are plugged into the device + Whether or not the microphone is muted on the device + Whether or not music is actively playing on the device + Whether or not speakerphone is enabled on the device + Volume level for alarms on the device + Volume level for calls on the device + Volume level for music on the device + Volume level for the ringer on the device The current battery level of the device The current charging state of the battery Information about currently connected bluetooth devices @@ -164,6 +173,16 @@ like to connect to: Power Sensors Internal Storage External Storage + Ringer Mode + Audio Mode + Headphones + Mic Muted + Music Active + Speakerphone + Volume Level Alarm + Volume Level Call + Volume Level Music + Volume Level Ringer Power Save Interactive Doze Mode @@ -194,7 +213,7 @@ like to connect to: Activity Sensors Geolocation Sensors Location Sensors - Audio Sensor + Audio Sensors Battery Sensors Bluetooth Sensors Do Not Disturb Sensor