feat: disable outgoing video when phone is near ear during calls
Uses the proximity sensor to detect when the phone is held to the ear and automatically pauses the camera/video track. Video resumes when the phone is moved away, but only if the user had video enabled. https://claude.ai/code/session_014espsysob7MrE8X8e75jFX
This commit is contained in:
@@ -24,6 +24,10 @@ import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.hardware.Sensor
|
||||
import android.hardware.SensorEvent
|
||||
import android.hardware.SensorEventListener
|
||||
import android.hardware.SensorManager
|
||||
import android.media.AudioAttributes
|
||||
import android.media.AudioDeviceInfo
|
||||
import android.media.AudioManager
|
||||
@@ -62,6 +66,25 @@ class CallAudioManager(
|
||||
private val _isBluetoothAvailable = MutableStateFlow(false)
|
||||
val isBluetoothAvailable: StateFlow<Boolean> = _isBluetoothAvailable.asStateFlow()
|
||||
|
||||
// Proximity sensor — detects when the phone is held near the ear
|
||||
private val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as? SensorManager
|
||||
private val proximitySensor: Sensor? = sensorManager?.getDefaultSensor(Sensor.TYPE_PROXIMITY)
|
||||
private val _isNearEar = MutableStateFlow(false)
|
||||
val isNearEar: StateFlow<Boolean> = _isNearEar.asStateFlow()
|
||||
|
||||
private val proximityListener =
|
||||
object : SensorEventListener {
|
||||
override fun onSensorChanged(event: SensorEvent) {
|
||||
val maxRange = event.sensor.maximumRange
|
||||
_isNearEar.value = event.values[0] < maxRange
|
||||
}
|
||||
|
||||
override fun onAccuracyChanged(
|
||||
sensor: Sensor?,
|
||||
accuracy: Int,
|
||||
) {}
|
||||
}
|
||||
|
||||
fun startRinging() {
|
||||
startRingtone()
|
||||
startVibration()
|
||||
@@ -137,11 +160,24 @@ class CallAudioManager(
|
||||
"amethyst:call_proximity",
|
||||
)
|
||||
proximityWakeLock?.acquire(60 * 60 * 1000L)
|
||||
registerProximitySensor()
|
||||
}
|
||||
|
||||
fun releaseProximityWakeLock() {
|
||||
proximityWakeLock?.let { if (it.isHeld) it.release() }
|
||||
proximityWakeLock = null
|
||||
unregisterProximitySensor()
|
||||
}
|
||||
|
||||
private fun registerProximitySensor() {
|
||||
proximitySensor?.let {
|
||||
sensorManager?.registerListener(proximityListener, it, SensorManager.SENSOR_DELAY_NORMAL)
|
||||
}
|
||||
}
|
||||
|
||||
private fun unregisterProximitySensor() {
|
||||
sensorManager?.unregisterListener(proximityListener)
|
||||
_isNearEar.value = false
|
||||
}
|
||||
|
||||
fun release() {
|
||||
|
||||
@@ -93,6 +93,9 @@ class CallController(
|
||||
val audioRoute: StateFlow<AudioRoute> = audioManager.audioRoute
|
||||
val isBluetoothAvailable: StateFlow<Boolean> = audioManager.isBluetoothAvailable
|
||||
|
||||
// Tracks whether video is paused because the phone is near the ear
|
||||
private var videoPausedByProximity = false
|
||||
|
||||
init {
|
||||
callManager.onRenegotiationOfferReceived = { event -> onRenegotiationOfferReceived(event) }
|
||||
|
||||
@@ -126,6 +129,22 @@ class CallController(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pause outgoing video when the phone is held near the ear
|
||||
scope.launch {
|
||||
audioManager.isNearEar.collect { nearEar ->
|
||||
val session = webRtcSession ?: return@collect
|
||||
if (nearEar && _isVideoEnabled.value && !videoPausedByProximity) {
|
||||
videoPausedByProximity = true
|
||||
session.setVideoEnabled(false)
|
||||
session.stopCamera()
|
||||
} else if (!nearEar && videoPausedByProximity) {
|
||||
videoPausedByProximity = false
|
||||
session.setVideoEnabled(true)
|
||||
session.startCamera()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun initiateCall(
|
||||
@@ -323,6 +342,7 @@ class CallController(
|
||||
_isAudioMuted.value = false
|
||||
_isVideoEnabled.value = false
|
||||
_isRemoteVideoActive.value = false
|
||||
videoPausedByProximity = false
|
||||
webRtcSession?.dispose()
|
||||
webRtcSession = null
|
||||
remoteDescriptionSet.set(false)
|
||||
|
||||
Reference in New Issue
Block a user