mirror of
https://github.com/oxen-io/session-android.git
synced 2023-12-14 02:53:01 +01:00
feat: more commands handled, adding lock manager and bluetooth permissions
This commit is contained in:
parent
de4d8e9be4
commit
f069d35b14
|
@ -31,6 +31,7 @@
|
||||||
android:required="false" />
|
android:required="false" />
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||||
|
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
||||||
<uses-permission android:name="network.loki.messenger.ACCESS_SESSION_SECRETS" />
|
<uses-permission android:name="network.loki.messenger.ACCESS_SESSION_SECRETS" />
|
||||||
|
|
|
@ -19,7 +19,6 @@ import org.session.libsession.utilities.Util
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
import org.session.libsignal.utilities.Log
|
import org.session.libsignal.utilities.Log
|
||||||
import org.thoughtcrime.securesms.calls.WebRtcCallActivity
|
import org.thoughtcrime.securesms.calls.WebRtcCallActivity
|
||||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
|
||||||
import org.thoughtcrime.securesms.util.CallNotificationBuilder
|
import org.thoughtcrime.securesms.util.CallNotificationBuilder
|
||||||
import org.thoughtcrime.securesms.util.CallNotificationBuilder.Companion.TYPE_ESTABLISHED
|
import org.thoughtcrime.securesms.util.CallNotificationBuilder.Companion.TYPE_ESTABLISHED
|
||||||
import org.thoughtcrime.securesms.util.CallNotificationBuilder.Companion.TYPE_INCOMING_CONNECTING
|
import org.thoughtcrime.securesms.util.CallNotificationBuilder.Companion.TYPE_INCOMING_CONNECTING
|
||||||
|
@ -28,6 +27,8 @@ import org.thoughtcrime.securesms.util.CallNotificationBuilder.Companion.TYPE_OU
|
||||||
import org.thoughtcrime.securesms.webrtc.*
|
import org.thoughtcrime.securesms.webrtc.*
|
||||||
import org.thoughtcrime.securesms.webrtc.CallManager.CallState.*
|
import org.thoughtcrime.securesms.webrtc.CallManager.CallState.*
|
||||||
import org.thoughtcrime.securesms.webrtc.audio.OutgoingRinger
|
import org.thoughtcrime.securesms.webrtc.audio.OutgoingRinger
|
||||||
|
import org.thoughtcrime.securesms.webrtc.locks.LockManager
|
||||||
|
import org.webrtc.SessionDescription
|
||||||
import java.lang.AssertionError
|
import java.lang.AssertionError
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ExecutionException
|
import java.util.concurrent.ExecutionException
|
||||||
|
@ -52,6 +53,7 @@ class WebRtcCallService: Service() {
|
||||||
const val ACTION_SET_MUTE_AUDIO = "SET_MUTE_AUDIO"
|
const val ACTION_SET_MUTE_AUDIO = "SET_MUTE_AUDIO"
|
||||||
const val ACTION_SET_MUTE_VIDEO = "SET_MUTE_VIDEO"
|
const val ACTION_SET_MUTE_VIDEO = "SET_MUTE_VIDEO"
|
||||||
const val ACTION_FLIP_CAMERA = "FLIP_CAMERA"
|
const val ACTION_FLIP_CAMERA = "FLIP_CAMERA"
|
||||||
|
const val ACTION_BLUETOOTH_CHANGE = "BLUETOOTH_CHANGE"
|
||||||
const val ACTION_UPDATE_AUDIO = "UPDATE_AUDIO"
|
const val ACTION_UPDATE_AUDIO = "UPDATE_AUDIO"
|
||||||
const val ACTION_WIRED_HEADSET_CHANGE = "WIRED_HEADSET_CHANGE"
|
const val ACTION_WIRED_HEADSET_CHANGE = "WIRED_HEADSET_CHANGE"
|
||||||
const val ACTION_SCREEN_OFF = "SCREEN_OFF"
|
const val ACTION_SCREEN_OFF = "SCREEN_OFF"
|
||||||
|
@ -107,6 +109,7 @@ class WebRtcCallService: Service() {
|
||||||
private var lastNotificationId: Int = INVALID_NOTIFICATION_ID
|
private var lastNotificationId: Int = INVALID_NOTIFICATION_ID
|
||||||
private var lastNotification: Notification? = null
|
private var lastNotification: Notification? = null
|
||||||
|
|
||||||
|
private val lockManager by lazy { LockManager(this) }
|
||||||
private val serviceExecutor = Executors.newSingleThreadExecutor()
|
private val serviceExecutor = Executors.newSingleThreadExecutor()
|
||||||
private val timeoutExecutor = Executors.newScheduledThreadPool(1)
|
private val timeoutExecutor = Executors.newScheduledThreadPool(1)
|
||||||
private val hangupOnCallAnswered = HangUpRtcOnPstnCallAnsweredListener {
|
private val hangupOnCallAnswered = HangUpRtcOnPstnCallAnsweredListener {
|
||||||
|
@ -145,9 +148,9 @@ class WebRtcCallService: Service() {
|
||||||
action == ACTION_REMOTE_HANGUP -> handleRemoteHangup(intent)
|
action == ACTION_REMOTE_HANGUP -> handleRemoteHangup(intent)
|
||||||
action == ACTION_SET_MUTE_AUDIO -> handleSetMuteAudio(intent)
|
action == ACTION_SET_MUTE_AUDIO -> handleSetMuteAudio(intent)
|
||||||
action == ACTION_SET_MUTE_VIDEO -> handleSetMuteVideo(intent)
|
action == ACTION_SET_MUTE_VIDEO -> handleSetMuteVideo(intent)
|
||||||
action == ACTION_FLIP_CAMERA -> handlesetCameraFlip(intent)
|
action == ACTION_FLIP_CAMERA -> handleSetCameraFlip(intent)
|
||||||
// action == ACTION_BLUETOOTH_CHANGE -> handleBluetoothChange(intent)
|
action == ACTION_BLUETOOTH_CHANGE -> handleBluetoothChange(intent)
|
||||||
// action == ACTION_WIRED_HEADSET_CHANGE -> handleWiredHeadsetChange(intent)
|
action == ACTION_WIRED_HEADSET_CHANGE -> handleWiredHeadsetChanged(intent)
|
||||||
action == ACTION_SCREEN_OFF -> handleScreenOffChange(intent)
|
action == ACTION_SCREEN_OFF -> handleScreenOffChange(intent)
|
||||||
action == ACTION_REMOTE_VIDEO_MUTE -> handleRemoteVideoMute(intent)
|
action == ACTION_REMOTE_VIDEO_MUTE -> handleRemoteVideoMute(intent)
|
||||||
action == ACTION_RESPONSE_MESSAGE -> handleResponseMessage(intent)
|
action == ACTION_RESPONSE_MESSAGE -> handleResponseMessage(intent)
|
||||||
|
@ -369,9 +372,64 @@ class WebRtcCallService: Service() {
|
||||||
|
|
||||||
private fun handleSetMuteVideo(intent: Intent) {
|
private fun handleSetMuteVideo(intent: Intent) {
|
||||||
val muted = intent.getBooleanExtra(EXTRA_MUTE, false)
|
val muted = intent.getBooleanExtra(EXTRA_MUTE, false)
|
||||||
callManager.handleSetMuteVideo(muted)
|
callManager.handleSetMuteVideo(muted, lockManager)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleSetCameraFlip(intent: Intent) {
|
||||||
|
callManager.handleSetCameraFlip()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleBluetoothChange(intent: Intent) {
|
||||||
|
val bluetoothAvailable = intent.getBooleanExtra(EXTRA_AVAILABLE, false)
|
||||||
|
callManager.postBluetoothAvailable(bluetoothAvailable)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleWiredHeadsetChanged(intent: Intent) {
|
||||||
|
callManager.handleWiredHeadsetChanged(intent.getBooleanExtra(EXTRA_AVAILABLE, false))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleScreenOffChange(intent: Intent) {
|
||||||
|
callManager.handleScreenOffChange()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleRemoteVideoMute(intent: Intent) {
|
||||||
|
val muted = intent.getBooleanExtra(EXTRA_MUTE, false)
|
||||||
|
val callId = intent.getSerializableExtra(EXTRA_CALL_ID) as UUID
|
||||||
|
|
||||||
|
callManager.handleRemoteVideoMute(muted, callId)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun handleResponseMessage(intent: Intent) {
|
||||||
|
try {
|
||||||
|
val recipient = getRemoteRecipient(intent)
|
||||||
|
val callId = getCallId(intent)
|
||||||
|
val description = intent.getStringExtra(EXTRA_REMOTE_DESCRIPTION)
|
||||||
|
callManager.handleResponseMessage(recipient, callId, SessionDescription(SessionDescription.Type.ANSWER, description))
|
||||||
|
} catch (e: PeerConnectionException) {
|
||||||
|
terminate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleRemoteIceCandidate(intent: Intent) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleLocalIceCandidate(intent: Intent) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleCallConnected(intent: Intent) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleIsInCallQuery(intent: Intent) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private fun handleCheckTimeout(intent: Intent) {
|
private fun handleCheckTimeout(intent: Intent) {
|
||||||
val callId = callManager.callId ?: return
|
val callId = callManager.callId ?: return
|
||||||
val callState = callManager.currentConnectionState
|
val callState = callManager.currentConnectionState
|
||||||
|
|
|
@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.webrtc
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
||||||
|
import org.thoughtcrime.securesms.webrtc.audio.OutgoingRinger
|
||||||
import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager
|
import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
|
@ -10,7 +11,7 @@ open class AudioManagerCommand: Parcelable {
|
||||||
object Initialize: AudioManagerCommand()
|
object Initialize: AudioManagerCommand()
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
object StartOutgoingRinger: AudioManagerCommand()
|
data class StartOutgoingRinger(val type: OutgoingRinger.Type): AudioManagerCommand()
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
object SilenceIncomingRinger: AudioManagerCommand()
|
object SilenceIncomingRinger: AudioManagerCommand()
|
||||||
|
|
|
@ -5,6 +5,8 @@ import android.content.Intent
|
||||||
import android.telephony.TelephonyManager
|
import android.telephony.TelephonyManager
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asSharedFlow
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
|
import kotlinx.serialization.json.buildJsonObject
|
||||||
|
import kotlinx.serialization.json.put
|
||||||
import nl.komponents.kovenant.Promise
|
import nl.komponents.kovenant.Promise
|
||||||
import org.session.libsession.messaging.messages.control.CallMessage
|
import org.session.libsession.messaging.messages.control.CallMessage
|
||||||
import org.session.libsession.messaging.sending_receiving.MessageSender
|
import org.session.libsession.messaging.sending_receiving.MessageSender
|
||||||
|
@ -15,10 +17,13 @@ import org.session.libsignal.utilities.Log
|
||||||
import org.thoughtcrime.securesms.webrtc.audio.AudioManagerCompat
|
import org.thoughtcrime.securesms.webrtc.audio.AudioManagerCompat
|
||||||
import org.thoughtcrime.securesms.webrtc.audio.OutgoingRinger
|
import org.thoughtcrime.securesms.webrtc.audio.OutgoingRinger
|
||||||
import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager
|
import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager
|
||||||
|
import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager.AudioDevice
|
||||||
|
import org.thoughtcrime.securesms.webrtc.locks.LockManager
|
||||||
import org.thoughtcrime.securesms.webrtc.video.CameraEventListener
|
import org.thoughtcrime.securesms.webrtc.video.CameraEventListener
|
||||||
import org.thoughtcrime.securesms.webrtc.video.CameraState
|
import org.thoughtcrime.securesms.webrtc.video.CameraState
|
||||||
import org.webrtc.*
|
import org.webrtc.*
|
||||||
import java.lang.NullPointerException
|
import java.lang.NullPointerException
|
||||||
|
import java.nio.ByteBuffer
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
|
@ -37,6 +42,10 @@ class CallManager(context: Context, audioManager: AudioManagerCompat): PeerConne
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
val VIDEO_DISABLED_JSON by lazy { buildJsonObject { put("video", false) } }
|
||||||
|
val VIDEO_ENABLED_JSON by lazy { buildJsonObject { put("video", true) } }
|
||||||
|
|
||||||
private val TAG = Log.tag(CallManager::class.java)
|
private val TAG = Log.tag(CallManager::class.java)
|
||||||
val CONNECTED_STATES = arrayOf(CallState.STATE_CONNECTED)
|
val CONNECTED_STATES = arrayOf(CallState.STATE_CONNECTED)
|
||||||
val PENDING_CONNECTION_STATES = arrayOf(
|
val PENDING_CONNECTION_STATES = arrayOf(
|
||||||
|
@ -50,11 +59,9 @@ class CallManager(context: Context, audioManager: AudioManagerCompat): PeerConne
|
||||||
CallState.STATE_REMOTE_RINGING,
|
CallState.STATE_REMOTE_RINGING,
|
||||||
CallState.STATE_CONNECTED
|
CallState.STATE_CONNECTED
|
||||||
)
|
)
|
||||||
val DISCONNECTED_STATES = arrayOf(CallState.STATE_IDLE)
|
|
||||||
private const val DATA_CHANNEL_NAME = "signaling"
|
private const val DATA_CHANNEL_NAME = "signaling"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private val signalAudioManager: SignalAudioManager = SignalAudioManager(context, this, audioManager)
|
private val signalAudioManager: SignalAudioManager = SignalAudioManager(context, this, audioManager)
|
||||||
|
|
||||||
private val _audioEvents = MutableStateFlow(StateEvent.AudioEnabled(false))
|
private val _audioEvents = MutableStateFlow(StateEvent.AudioEnabled(false))
|
||||||
|
@ -97,11 +104,11 @@ class CallManager(context: Context, audioManager: AudioManagerCompat): PeerConne
|
||||||
}
|
}
|
||||||
|
|
||||||
fun initializeAudioForCall() {
|
fun initializeAudioForCall() {
|
||||||
signalAudioManager.initializeAudioForCall()
|
signalAudioManager.handleCommand(AudioManagerCommand.Initialize)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startOutgoingRinger(ringerType: OutgoingRinger.Type) {
|
fun startOutgoingRinger(ringerType: OutgoingRinger.Type) {
|
||||||
signalAudioManager.startOutgoingRinger(ringerType)
|
signalAudioManager.handleCommand(AudioManagerCommand.StartOutgoingRinger(ringerType))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun postConnectionEvent(newState: CallState) {
|
fun postConnectionEvent(newState: CallState) {
|
||||||
|
@ -112,36 +119,6 @@ class CallManager(context: Context, audioManager: AudioManagerCompat): PeerConne
|
||||||
_callStateEvents.value = newState
|
_callStateEvents.value = newState
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createCameraCapturer(enumerator: CameraEnumerator): CameraVideoCapturer? {
|
|
||||||
val deviceNames = enumerator.deviceNames
|
|
||||||
|
|
||||||
// First, try to find front facing camera
|
|
||||||
Log.d("Loki-RTC-vid", "Looking for front facing cameras.")
|
|
||||||
for (deviceName in deviceNames) {
|
|
||||||
if (enumerator.isFrontFacing(deviceName)) {
|
|
||||||
Log.d("Loki-RTC-vid", "Creating front facing camera capturer.")
|
|
||||||
val videoCapturer = enumerator.createCapturer(deviceName, null)
|
|
||||||
if (videoCapturer != null) {
|
|
||||||
return videoCapturer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Front facing camera not found, try something else
|
|
||||||
Log.d("Loki-RTC-vid", "Looking for other cameras.")
|
|
||||||
for (deviceName in deviceNames) {
|
|
||||||
if (!enumerator.isFrontFacing(deviceName)) {
|
|
||||||
Log.d("Loki-RTC-vid", "Creating other camera capturer.")
|
|
||||||
val videoCapturer = enumerator.createCapturer(deviceName, null)
|
|
||||||
if (videoCapturer != null) {
|
|
||||||
return videoCapturer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun newCallMessage(callMessage: SignalServiceProtos.CallMessage) {
|
override fun newCallMessage(callMessage: SignalServiceProtos.CallMessage) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -262,7 +239,7 @@ class CallManager(context: Context, audioManager: AudioManagerCompat): PeerConne
|
||||||
TODO("interpret the data channel buffer and check for signals")
|
TODO("interpret the data channel buffer and check for signals")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAudioDeviceChanged(activeDevice: SignalAudioManager.AudioDevice, devices: Set<SignalAudioManager.AudioDevice>) {
|
override fun onAudioDeviceChanged(activeDevice: AudioDevice, devices: Set<AudioDevice>) {
|
||||||
signalAudioManager.handleCommand(AudioManagerCommand())
|
signalAudioManager.handleCommand(AudioManagerCommand())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,7 +256,7 @@ class CallManager(context: Context, audioManager: AudioManagerCompat): PeerConne
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stop() {
|
fun stop() {
|
||||||
signalAudioManager.stop(currentConnectionState in OUTGOING_STATES)
|
signalAudioManager.handleCommand(AudioManagerCommand.Stop(currentConnectionState in OUTGOING_STATES))
|
||||||
peerConnection?.dispose()
|
peerConnection?.dispose()
|
||||||
peerConnection = null
|
peerConnection = null
|
||||||
|
|
||||||
|
@ -295,8 +272,8 @@ class CallManager(context: Context, audioManager: AudioManagerCompat): PeerConne
|
||||||
localCameraState = CameraState.UNKNOWN
|
localCameraState = CameraState.UNKNOWN
|
||||||
recipient = null
|
recipient = null
|
||||||
callId = null
|
callId = null
|
||||||
microphoneEnabled = true
|
_audioEvents.value = StateEvent.AudioEnabled(false)
|
||||||
remoteVideoEnabled = false
|
_videoEvents.value = StateEvent.VideoEnabled(false)
|
||||||
pendingOutgoingIceUpdates.clear()
|
pendingOutgoingIceUpdates.clear()
|
||||||
pendingIncomingIceUpdates.clear()
|
pendingIncomingIceUpdates.clear()
|
||||||
}
|
}
|
||||||
|
@ -410,10 +387,93 @@ class CallManager(context: Context, audioManager: AudioManagerCompat): PeerConne
|
||||||
peerConnection?.setAudioEnabled(_audioEvents.value.isEnabled)
|
peerConnection?.setAudioEnabled(_audioEvents.value.isEnabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun handleSetMuteVideo(muted: Boolean) {
|
fun handleSetMuteVideo(muted: Boolean, lockManager: LockManager) {
|
||||||
_videoEvents.value = StateEvent.VideoEnabled(!muted)
|
_videoEvents.value = StateEvent.VideoEnabled(!muted)
|
||||||
peerConnection?.setVideoEnabled(_videoEvents.value.isEnabled)
|
peerConnection?.setVideoEnabled(_videoEvents.value.isEnabled)
|
||||||
TODO()
|
dataChannel?.let { channel ->
|
||||||
|
val toSend = if (muted) VIDEO_DISABLED_JSON else VIDEO_ENABLED_JSON
|
||||||
|
val buffer = DataChannel.Buffer(ByteBuffer.wrap(toSend.toString().encodeToByteArray()), false)
|
||||||
|
channel.send(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentConnectionState == CallState.STATE_CONNECTED) {
|
||||||
|
if (localCameraState.enabled) lockManager.updatePhoneState(LockManager.PhoneState.IN_VIDEO)
|
||||||
|
else lockManager.updatePhoneState(LockManager.PhoneState.IN_CALL)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (localCameraState.enabled
|
||||||
|
&& !signalAudioManager.isSpeakerphoneOn()
|
||||||
|
&& !signalAudioManager.isBluetoothScoOn()
|
||||||
|
&& !signalAudioManager.isWiredHeadsetOn()
|
||||||
|
) {
|
||||||
|
signalAudioManager.handleCommand(AudioManagerCommand.SetUserDevice(AudioDevice.SPEAKER_PHONE))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun handleSetCameraFlip() {
|
||||||
|
if (!localCameraState.enabled) return
|
||||||
|
peerConnection?.let { connection ->
|
||||||
|
connection.flipCamera()
|
||||||
|
localCameraState = connection.getCameraState()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun postBluetoothAvailable(available: Boolean) {
|
||||||
|
// TODO: _bluetoothEnabled.value = available
|
||||||
|
}
|
||||||
|
|
||||||
|
fun handleWiredHeadsetChanged(present: Boolean) {
|
||||||
|
if (currentConnectionState in arrayOf(CallState.STATE_CONNECTED,
|
||||||
|
CallState.STATE_DIALING,
|
||||||
|
CallState.STATE_REMOTE_RINGING)) {
|
||||||
|
if (present && signalAudioManager.isSpeakerphoneOn()) {
|
||||||
|
signalAudioManager.handleCommand(AudioManagerCommand.SetUserDevice(AudioDevice.WIRED_HEADSET))
|
||||||
|
} else if (!present && !signalAudioManager.isSpeakerphoneOn() && !signalAudioManager.isBluetoothScoOn() && localCameraState.enabled) {
|
||||||
|
signalAudioManager.handleCommand(AudioManagerCommand.SetUserDevice(AudioDevice.SPEAKER_PHONE))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun handleScreenOffChange() {
|
||||||
|
if (currentConnectionState in arrayOf(CallState.STATE_ANSWERING, CallState.STATE_LOCAL_RINGING)) {
|
||||||
|
signalAudioManager.handleCommand(AudioManagerCommand.SilenceIncomingRinger)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun handleRemoteVideoMute(muted: Boolean, intentCallId: UUID) {
|
||||||
|
val recipient = recipient ?: return
|
||||||
|
val callId = callId ?: return
|
||||||
|
if (currentConnectionState != CallState.STATE_CONNECTED || callId != intentCallId) {
|
||||||
|
Log.w(TAG,"Got video toggle for inactive call, ignoring..")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_remoteVideoEvents.value = StateEvent.VideoEnabled(!muted)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun handleResponseMessage(recipient: Recipient, callId: UUID, answer: SessionDescription) {
|
||||||
|
if (currentConnectionState != CallState.STATE_DIALING || recipient != this.recipient || callId != this.callId) {
|
||||||
|
Log.w(TAG,"Got answer for recipient and call ID we're not currently dialing")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val connection = peerConnection ?: throw AssertionError("assert")
|
||||||
|
|
||||||
|
connection.setRemoteDescription(answer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun handleRemoteIceCandidate(iceCandidates: List<IceCandidate>, callId: UUID) {
|
||||||
|
if (callId != this.callId) {
|
||||||
|
Log.w(TAG, "Got remote ice candidates for a call that isn't active")
|
||||||
|
}
|
||||||
|
|
||||||
|
peerConnection?.let { connection ->
|
||||||
|
iceCandidates.forEach { candidate ->
|
||||||
|
connection.addIceCandidate(candidate)
|
||||||
|
}
|
||||||
|
} ?: run {
|
||||||
|
pendingIncomingIceUpdates.addAll(iceCandidates)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -81,7 +81,10 @@ class PeerConnectionWrapper(context: Context,
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createDataChannel(channelName: String): DataChannel {
|
fun createDataChannel(channelName: String): DataChannel {
|
||||||
|
val dataChannelConfiguration = DataChannel.Init().apply {
|
||||||
|
ordered = true
|
||||||
|
}
|
||||||
|
return peerConnection.createDataChannel(channelName, dataChannelConfiguration)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addIceCandidate(candidate: IceCandidate) {
|
fun addIceCandidate(candidate: IceCandidate) {
|
||||||
|
@ -228,4 +231,8 @@ class PeerConnectionWrapper(context: Context,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun flipCamera() {
|
||||||
|
camera.flip()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -75,7 +75,7 @@ class SignalAudioManager(private val context: Context,
|
||||||
is AudioManagerCommand.SetUserDevice -> selectAudioDevice(command.device)
|
is AudioManagerCommand.SetUserDevice -> selectAudioDevice(command.device)
|
||||||
is AudioManagerCommand.StartIncomingRinger -> startIncomingRinger(command.vibrate)
|
is AudioManagerCommand.StartIncomingRinger -> startIncomingRinger(command.vibrate)
|
||||||
is AudioManagerCommand.SilenceIncomingRinger -> silenceIncomingRinger()
|
is AudioManagerCommand.SilenceIncomingRinger -> silenceIncomingRinger()
|
||||||
is AudioManagerCommand.StartOutgoingRinger -> startOutgoingRinger()
|
is AudioManagerCommand.StartOutgoingRinger -> startOutgoingRinger(command.type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,7 +128,7 @@ class SignalAudioManager(private val context: Context,
|
||||||
Log.d(TAG, "Started")
|
Log.d(TAG, "Started")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stop(playDisconnect: Boolean) {
|
private fun stop(playDisconnect: Boolean) {
|
||||||
Log.d(TAG, "Stopping. state: $state")
|
Log.d(TAG, "Stopping. state: $state")
|
||||||
if (state == State.UNINITIALIZED) {
|
if (state == State.UNINITIALIZED) {
|
||||||
Log.i(TAG, "Trying to stop AudioManager in incorrect state: $state")
|
Log.i(TAG, "Trying to stop AudioManager in incorrect state: $state")
|
||||||
|
@ -166,7 +166,7 @@ class SignalAudioManager(private val context: Context,
|
||||||
Log.d(TAG, "Stopped")
|
Log.d(TAG, "Stopped")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun shutdown() {
|
private fun shutdown() {
|
||||||
handler.post {
|
handler.post {
|
||||||
stop(false)
|
stop(false)
|
||||||
if (commandAndControlThread != null) {
|
if (commandAndControlThread != null) {
|
||||||
|
@ -177,7 +177,7 @@ class SignalAudioManager(private val context: Context,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateAudioDeviceState() {
|
private fun updateAudioDeviceState() {
|
||||||
handler.assertHandlerThread()
|
handler.assertHandlerThread()
|
||||||
|
|
||||||
Log.i(
|
Log.i(
|
||||||
|
@ -342,13 +342,13 @@ class SignalAudioManager(private val context: Context,
|
||||||
incomingRinger.stop()
|
incomingRinger.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startOutgoingRinger(type: OutgoingRinger.Type) {
|
private fun startOutgoingRinger(type: OutgoingRinger.Type) {
|
||||||
Log.i(TAG, "startOutgoingRinger(): currentDevice: $selectedAudioDevice")
|
Log.i(TAG, "startOutgoingRinger(): currentDevice: $selectedAudioDevice")
|
||||||
|
|
||||||
androidAudioManager.mode = AudioManager.MODE_IN_COMMUNICATION
|
androidAudioManager.mode = AudioManager.MODE_IN_COMMUNICATION
|
||||||
setMicrophoneMute(false)
|
setMicrophoneMute(false)
|
||||||
|
|
||||||
outgoingRinger.start(OutgoingRinger.Type.RINGING)
|
outgoingRinger.start(type)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onWiredHeadsetChange(pluggedIn: Boolean, hasMic: Boolean) {
|
private fun onWiredHeadsetChange(pluggedIn: Boolean, hasMic: Boolean) {
|
||||||
|
@ -357,10 +357,11 @@ class SignalAudioManager(private val context: Context,
|
||||||
updateAudioDeviceState()
|
updateAudioDeviceState()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun initializeAudioForCall() {
|
fun isSpeakerphoneOn(): Boolean = androidAudioManager.isSpeakerphoneOn
|
||||||
val audioManager: AudioManager = ServiceUtil.getAudioManager(context)
|
|
||||||
audioManager.requestAudioFocus(null, AudioManager.STREAM_VOICE_CALL, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)
|
fun isBluetoothScoOn(): Boolean = androidAudioManager.isBluetoothScoOn
|
||||||
}
|
|
||||||
|
fun isWiredHeadsetOn(): Boolean = androidAudioManager.isWiredHeadsetOn
|
||||||
|
|
||||||
private inner class WiredHeadsetReceiver : BroadcastReceiver() {
|
private inner class WiredHeadsetReceiver : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
|
|
@ -0,0 +1,162 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2009 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.thoughtcrime.securesms.webrtc.locks;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.hardware.Sensor;
|
||||||
|
import android.hardware.SensorEvent;
|
||||||
|
import android.hardware.SensorEventListener;
|
||||||
|
import android.hardware.SensorManager;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Message;
|
||||||
|
|
||||||
|
import org.session.libsignal.utilities.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is used to listen to the accelerometer to monitor the
|
||||||
|
* orientation of the phone. The client of this class is notified when
|
||||||
|
* the orientation changes between horizontal and vertical.
|
||||||
|
*/
|
||||||
|
public final class AccelerometerListener {
|
||||||
|
private static final String TAG = "AccelerometerListener";
|
||||||
|
private static final boolean DEBUG = true;
|
||||||
|
private static final boolean VDEBUG = false;
|
||||||
|
|
||||||
|
private SensorManager mSensorManager;
|
||||||
|
private Sensor mSensor;
|
||||||
|
|
||||||
|
// mOrientation is the orientation value most recently reported to the client.
|
||||||
|
private int mOrientation;
|
||||||
|
|
||||||
|
// mPendingOrientation is the latest orientation computed based on the sensor value.
|
||||||
|
// This is sent to the client after a rebounce delay, at which point it is copied to
|
||||||
|
// mOrientation.
|
||||||
|
private int mPendingOrientation;
|
||||||
|
|
||||||
|
private OrientationListener mListener;
|
||||||
|
|
||||||
|
// Device orientation
|
||||||
|
public static final int ORIENTATION_UNKNOWN = 0;
|
||||||
|
public static final int ORIENTATION_VERTICAL = 1;
|
||||||
|
public static final int ORIENTATION_HORIZONTAL = 2;
|
||||||
|
|
||||||
|
private static final int ORIENTATION_CHANGED = 1234;
|
||||||
|
|
||||||
|
private static final int VERTICAL_DEBOUNCE = 100;
|
||||||
|
private static final int HORIZONTAL_DEBOUNCE = 500;
|
||||||
|
private static final double VERTICAL_ANGLE = 50.0;
|
||||||
|
|
||||||
|
public interface OrientationListener {
|
||||||
|
public void orientationChanged(int orientation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccelerometerListener(Context context, OrientationListener listener) {
|
||||||
|
mListener = listener;
|
||||||
|
mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
|
||||||
|
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void enable(boolean enable) {
|
||||||
|
if (DEBUG) Log.d(TAG, "enable(" + enable + ")");
|
||||||
|
synchronized (this) {
|
||||||
|
if (enable) {
|
||||||
|
mOrientation = ORIENTATION_UNKNOWN;
|
||||||
|
mPendingOrientation = ORIENTATION_UNKNOWN;
|
||||||
|
mSensorManager.registerListener(mSensorListener, mSensor,
|
||||||
|
SensorManager.SENSOR_DELAY_NORMAL);
|
||||||
|
} else {
|
||||||
|
mSensorManager.unregisterListener(mSensorListener);
|
||||||
|
mHandler.removeMessages(ORIENTATION_CHANGED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setOrientation(int orientation) {
|
||||||
|
synchronized (this) {
|
||||||
|
if (mPendingOrientation == orientation) {
|
||||||
|
// Pending orientation has not changed, so do nothing.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cancel any pending messages.
|
||||||
|
// We will either start a new timer or cancel alltogether
|
||||||
|
// if the orientation has not changed.
|
||||||
|
mHandler.removeMessages(ORIENTATION_CHANGED);
|
||||||
|
|
||||||
|
if (mOrientation != orientation) {
|
||||||
|
// Set timer to send an event if the orientation has changed since its
|
||||||
|
// previously reported value.
|
||||||
|
mPendingOrientation = orientation;
|
||||||
|
Message m = mHandler.obtainMessage(ORIENTATION_CHANGED);
|
||||||
|
// set delay to our debounce timeout
|
||||||
|
int delay = (orientation == ORIENTATION_VERTICAL ? VERTICAL_DEBOUNCE
|
||||||
|
: HORIZONTAL_DEBOUNCE);
|
||||||
|
mHandler.sendMessageDelayed(m, delay);
|
||||||
|
} else {
|
||||||
|
// no message is pending
|
||||||
|
mPendingOrientation = ORIENTATION_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onSensorEvent(double x, double y, double z) {
|
||||||
|
if (VDEBUG) Log.d(TAG, "onSensorEvent(" + x + ", " + y + ", " + z + ")");
|
||||||
|
|
||||||
|
// If some values are exactly zero, then likely the sensor is not powered up yet.
|
||||||
|
// ignore these events to avoid false horizontal positives.
|
||||||
|
if (x == 0.0 || y == 0.0 || z == 0.0) return;
|
||||||
|
|
||||||
|
// magnitude of the acceleration vector projected onto XY plane
|
||||||
|
double xy = Math.sqrt(x * x + y * y);
|
||||||
|
// compute the vertical angle
|
||||||
|
double angle = Math.atan2(xy, z);
|
||||||
|
// convert to degrees
|
||||||
|
angle = angle * 180.0 / Math.PI;
|
||||||
|
int orientation = (angle > VERTICAL_ANGLE ? ORIENTATION_VERTICAL : ORIENTATION_HORIZONTAL);
|
||||||
|
if (VDEBUG) Log.d(TAG, "angle: " + angle + " orientation: " + orientation);
|
||||||
|
setOrientation(orientation);
|
||||||
|
}
|
||||||
|
|
||||||
|
SensorEventListener mSensorListener = new SensorEventListener() {
|
||||||
|
public void onSensorChanged(SensorEvent event) {
|
||||||
|
onSensorEvent(event.values[0], event.values[1], event.values[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Handler mHandler = new Handler() {
|
||||||
|
public void handleMessage(Message msg) {
|
||||||
|
switch (msg.what) {
|
||||||
|
case ORIENTATION_CHANGED:
|
||||||
|
synchronized (this) {
|
||||||
|
mOrientation = mPendingOrientation;
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "orientation: " +
|
||||||
|
(mOrientation == ORIENTATION_HORIZONTAL ? "horizontal"
|
||||||
|
: (mOrientation == ORIENTATION_VERTICAL ? "vertical"
|
||||||
|
: "unknown")));
|
||||||
|
}
|
||||||
|
mListener.orientationChanged(mOrientation);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,148 @@
|
||||||
|
package org.thoughtcrime.securesms.webrtc.locks;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.wifi.WifiManager;
|
||||||
|
import android.os.PowerManager;
|
||||||
|
import android.provider.Settings;
|
||||||
|
|
||||||
|
import org.session.libsignal.utilities.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maintains wake lock state.
|
||||||
|
*
|
||||||
|
* @author Stuart O. Anderson
|
||||||
|
*/
|
||||||
|
public class LockManager {
|
||||||
|
|
||||||
|
private static final String TAG = LockManager.class.getSimpleName();
|
||||||
|
|
||||||
|
private final PowerManager.WakeLock fullLock;
|
||||||
|
private final PowerManager.WakeLock partialLock;
|
||||||
|
private final WifiManager.WifiLock wifiLock;
|
||||||
|
private final ProximityLock proximityLock;
|
||||||
|
|
||||||
|
private final AccelerometerListener accelerometerListener;
|
||||||
|
private final boolean wifiLockEnforced;
|
||||||
|
|
||||||
|
|
||||||
|
private int orientation = AccelerometerListener.ORIENTATION_UNKNOWN;
|
||||||
|
private boolean proximityDisabled = false;
|
||||||
|
|
||||||
|
public enum PhoneState {
|
||||||
|
IDLE,
|
||||||
|
PROCESSING, //used when the phone is active but before the user should be alerted.
|
||||||
|
INTERACTIVE,
|
||||||
|
IN_CALL,
|
||||||
|
IN_VIDEO
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum LockState {
|
||||||
|
FULL,
|
||||||
|
PARTIAL,
|
||||||
|
SLEEP,
|
||||||
|
PROXIMITY
|
||||||
|
}
|
||||||
|
|
||||||
|
public LockManager(Context context) {
|
||||||
|
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
||||||
|
fullLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, "signal:full");
|
||||||
|
partialLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "signal:partial");
|
||||||
|
proximityLock = new ProximityLock(pm);
|
||||||
|
|
||||||
|
WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
|
||||||
|
wifiLock = wm.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "signal:wifi");
|
||||||
|
|
||||||
|
fullLock.setReferenceCounted(false);
|
||||||
|
partialLock.setReferenceCounted(false);
|
||||||
|
wifiLock.setReferenceCounted(false);
|
||||||
|
|
||||||
|
accelerometerListener = new AccelerometerListener(context, new AccelerometerListener.OrientationListener() {
|
||||||
|
@Override
|
||||||
|
public void orientationChanged(int newOrientation) {
|
||||||
|
orientation = newOrientation;
|
||||||
|
Log.d(TAG, "Orentation Update: " + newOrientation);
|
||||||
|
updateInCallLockState();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
wifiLockEnforced = isWifiPowerActiveModeEnabled(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isWifiPowerActiveModeEnabled(Context context) {
|
||||||
|
int wifi_pwr_active_mode = Settings.Secure.getInt(context.getContentResolver(), "wifi_pwr_active_mode", -1);
|
||||||
|
Log.d(TAG, "Wifi Activity Policy: " + wifi_pwr_active_mode);
|
||||||
|
|
||||||
|
if (wifi_pwr_active_mode == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateInCallLockState() {
|
||||||
|
if (orientation != AccelerometerListener.ORIENTATION_HORIZONTAL && wifiLockEnforced && !proximityDisabled) {
|
||||||
|
setLockState(LockState.PROXIMITY);
|
||||||
|
} else {
|
||||||
|
setLockState(LockState.FULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updatePhoneState(PhoneState state) {
|
||||||
|
switch(state) {
|
||||||
|
case IDLE:
|
||||||
|
setLockState(LockState.SLEEP);
|
||||||
|
accelerometerListener.enable(false);
|
||||||
|
break;
|
||||||
|
case PROCESSING:
|
||||||
|
setLockState(LockState.PARTIAL);
|
||||||
|
accelerometerListener.enable(false);
|
||||||
|
break;
|
||||||
|
case INTERACTIVE:
|
||||||
|
setLockState(LockState.FULL);
|
||||||
|
accelerometerListener.enable(false);
|
||||||
|
break;
|
||||||
|
case IN_VIDEO:
|
||||||
|
proximityDisabled = true;
|
||||||
|
accelerometerListener.enable(false);
|
||||||
|
updateInCallLockState();
|
||||||
|
break;
|
||||||
|
case IN_CALL:
|
||||||
|
proximityDisabled = false;
|
||||||
|
accelerometerListener.enable(true);
|
||||||
|
updateInCallLockState();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void setLockState(LockState newState) {
|
||||||
|
switch(newState) {
|
||||||
|
case FULL:
|
||||||
|
fullLock.acquire();
|
||||||
|
partialLock.acquire();
|
||||||
|
wifiLock.acquire();
|
||||||
|
proximityLock.release();
|
||||||
|
break;
|
||||||
|
case PARTIAL:
|
||||||
|
partialLock.acquire();
|
||||||
|
wifiLock.acquire();
|
||||||
|
fullLock.release();
|
||||||
|
proximityLock.release();
|
||||||
|
break;
|
||||||
|
case SLEEP:
|
||||||
|
fullLock.release();
|
||||||
|
partialLock.release();
|
||||||
|
wifiLock.release();
|
||||||
|
proximityLock.release();
|
||||||
|
break;
|
||||||
|
case PROXIMITY:
|
||||||
|
partialLock.acquire();
|
||||||
|
proximityLock.acquire();
|
||||||
|
wifiLock.acquire();
|
||||||
|
fullLock.release();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Unhandled Mode: " + newState);
|
||||||
|
}
|
||||||
|
Log.d(TAG, "Entered Lock State: " + newState);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package org.thoughtcrime.securesms.webrtc.locks;
|
||||||
|
|
||||||
|
import android.os.PowerManager;
|
||||||
|
|
||||||
|
import org.session.libsignal.utilities.Log;
|
||||||
|
import org.session.libsignal.utilities.guava.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controls access to the proximity lock.
|
||||||
|
* The proximity lock is not part of the public API.
|
||||||
|
*
|
||||||
|
* @author Stuart O. Anderson
|
||||||
|
*/
|
||||||
|
class ProximityLock {
|
||||||
|
|
||||||
|
private static final String TAG = ProximityLock.class.getSimpleName();
|
||||||
|
|
||||||
|
private final Optional<PowerManager.WakeLock> proximityLock;
|
||||||
|
|
||||||
|
ProximityLock(PowerManager pm) {
|
||||||
|
proximityLock = getProximityLock(pm);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<PowerManager.WakeLock> getProximityLock(PowerManager pm) {
|
||||||
|
if (pm.isWakeLockLevelSupported(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)) {
|
||||||
|
return Optional.fromNullable(pm.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, "signal:proximity"));
|
||||||
|
} else {
|
||||||
|
return Optional.absent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void acquire() {
|
||||||
|
if (!proximityLock.isPresent() || proximityLock.get().isHeld()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
proximityLock.get().acquire();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void release() {
|
||||||
|
if (!proximityLock.isPresent() || !proximityLock.get().isHeld()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
proximityLock.get().release(PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY);
|
||||||
|
|
||||||
|
Log.d(TAG, "Released proximity lock:" + proximityLock.get().isHeld());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue