play busy and dial tones
This commit is contained in:
parent
07911b2094
commit
fc4397e5b9
|
@ -50,9 +50,10 @@ import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
|
||||||
import rocks.xmpp.addr.Jid;
|
import rocks.xmpp.addr.Jid;
|
||||||
|
|
||||||
public class JingleConnectionManager extends AbstractConnectionManager {
|
public class JingleConnectionManager extends AbstractConnectionManager {
|
||||||
private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
|
public static final ScheduledExecutorService SCHEDULED_EXECUTOR_SERVICE = Executors.newSingleThreadScheduledExecutor();
|
||||||
|
public final ToneManager toneManager = new ToneManager();
|
||||||
private final HashMap<RtpSessionProposal, DeviceDiscoveryState> rtpSessionProposals = new HashMap<>();
|
private final HashMap<RtpSessionProposal, DeviceDiscoveryState> rtpSessionProposals = new HashMap<>();
|
||||||
private final Map<AbstractJingleConnection.Id, AbstractJingleConnection> connections = new ConcurrentHashMap<>();
|
private final ConcurrentHashMap<AbstractJingleConnection.Id, AbstractJingleConnection> connections = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private final Cache<PersistableSessionId, JingleRtpConnection.State> endedSessions = CacheBuilder.newBuilder()
|
private final Cache<PersistableSessionId, JingleRtpConnection.State> endedSessions = CacheBuilder.newBuilder()
|
||||||
.expireAfterWrite(30, TimeUnit.MINUTES)
|
.expireAfterWrite(30, TimeUnit.MINUTES)
|
||||||
|
@ -141,7 +142,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
ScheduledFuture<?> schedule(final Runnable runnable, final long delay, final TimeUnit timeUnit) {
|
ScheduledFuture<?> schedule(final Runnable runnable, final long delay, final TimeUnit timeUnit) {
|
||||||
return this.scheduledExecutorService.schedule(runnable, delay, timeUnit);
|
return this.SCHEDULED_EXECUTOR_SERVICE.schedule(runnable, delay, timeUnit);
|
||||||
}
|
}
|
||||||
|
|
||||||
void respondWithJingleError(final Account account, final IqPacket original, String jingleCondition, String condition, String conditionType) {
|
void respondWithJingleError(final Account account, final IqPacket original, String jingleCondition, String condition, String conditionType) {
|
||||||
|
@ -268,6 +269,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
|
||||||
synchronized (rtpSessionProposals) {
|
synchronized (rtpSessionProposals) {
|
||||||
if (rtpSessionProposals.remove(proposal) != null) {
|
if (rtpSessionProposals.remove(proposal) != null) {
|
||||||
writeLogMissedOutgoing(account, proposal.with, proposal.sessionId, serverMsgId, timestamp);
|
writeLogMissedOutgoing(account, proposal.with, proposal.sessionId, serverMsgId, timestamp);
|
||||||
|
toneManager.transition(true, RtpEndUserState.DECLINED_OR_BUSY);
|
||||||
mXmppConnectionService.notifyJingleRtpConnectionUpdate(account, proposal.with, proposal.sessionId, RtpEndUserState.DECLINED_OR_BUSY);
|
mXmppConnectionService.notifyJingleRtpConnectionUpdate(account, proposal.with, proposal.sessionId, RtpEndUserState.DECLINED_OR_BUSY);
|
||||||
} else {
|
} else {
|
||||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": no rtp session proposal found for " + from + " to deliver reject");
|
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": no rtp session proposal found for " + from + " to deliver reject");
|
||||||
|
@ -352,7 +354,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<AbstractJingleConnection.Id> getOngoingRtpConnection(final Contact contact) {
|
public Optional<AbstractJingleConnection.Id> getOngoingRtpConnection(final Contact contact) {
|
||||||
for(final Map.Entry<AbstractJingleConnection.Id,AbstractJingleConnection> entry : this.connections.entrySet()) {
|
for (final Map.Entry<AbstractJingleConnection.Id, AbstractJingleConnection> entry : this.connections.entrySet()) {
|
||||||
if (entry.getValue() instanceof JingleRtpConnection) {
|
if (entry.getValue() instanceof JingleRtpConnection) {
|
||||||
final AbstractJingleConnection.Id id = entry.getKey();
|
final AbstractJingleConnection.Id id = entry.getKey();
|
||||||
if (id.account == contact.getAccount() && id.with.asBareJid().equals(contact.getJid().asBareJid())) {
|
if (id.account == contact.getAccount() && id.with.asBareJid().equals(contact.getJid().asBareJid())) {
|
||||||
|
@ -423,6 +425,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (matchingProposal != null) {
|
if (matchingProposal != null) {
|
||||||
|
toneManager.transition(true, RtpEndUserState.ENDED);
|
||||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": retracting rtp session proposal with " + with);
|
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": retracting rtp session proposal with " + with);
|
||||||
this.rtpSessionProposals.remove(matchingProposal);
|
this.rtpSessionProposals.remove(matchingProposal);
|
||||||
final MessagePacket messagePacket = mXmppConnectionService.getMessageGenerator().sessionRetract(matchingProposal);
|
final MessagePacket messagePacket = mXmppConnectionService.getMessageGenerator().sessionRetract(matchingProposal);
|
||||||
|
@ -439,11 +442,13 @@ public class JingleConnectionManager extends AbstractConnectionManager {
|
||||||
if (proposal.account == account && with.asBareJid().equals(proposal.with)) {
|
if (proposal.account == account && with.asBareJid().equals(proposal.with)) {
|
||||||
final DeviceDiscoveryState preexistingState = entry.getValue();
|
final DeviceDiscoveryState preexistingState = entry.getValue();
|
||||||
if (preexistingState != null && preexistingState != DeviceDiscoveryState.FAILED) {
|
if (preexistingState != null && preexistingState != DeviceDiscoveryState.FAILED) {
|
||||||
|
final RtpEndUserState endUserState = preexistingState.toEndUserState();
|
||||||
|
toneManager.transition(true, endUserState);
|
||||||
mXmppConnectionService.notifyJingleRtpConnectionUpdate(
|
mXmppConnectionService.notifyJingleRtpConnectionUpdate(
|
||||||
account,
|
account,
|
||||||
with,
|
with,
|
||||||
proposal.sessionId,
|
proposal.sessionId,
|
||||||
preexistingState.toEndUserState()
|
endUserState
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -529,7 +534,9 @@ public class JingleConnectionManager extends AbstractConnectionManager {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.rtpSessionProposals.put(sessionProposal, target);
|
this.rtpSessionProposals.put(sessionProposal, target);
|
||||||
mXmppConnectionService.notifyJingleRtpConnectionUpdate(account, sessionProposal.with, sessionProposal.sessionId, target.toEndUserState());
|
final RtpEndUserState endUserState = target.toEndUserState();
|
||||||
|
toneManager.transition(true, endUserState);
|
||||||
|
mXmppConnectionService.notifyJingleRtpConnectionUpdate(account, sessionProposal.with, sessionProposal.sessionId, endUserState);
|
||||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": flagging session " + sessionId + " as " + target);
|
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": flagging session " + sessionId + " as " + target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1027,7 +1027,11 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateEndUserState() {
|
private void updateEndUserState() {
|
||||||
xmppConnectionService.notifyJingleRtpConnectionUpdate(id.account, id.with, id.sessionId, getEndUserState());
|
final RtpEndUserState endUserState = getEndUserState();
|
||||||
|
final RtpContentMap contentMap = initiatorRtpContentMap;
|
||||||
|
final Set<Media> media = contentMap == null ? Collections.emptySet() : contentMap.getMedia();
|
||||||
|
jingleConnectionManager.toneManager.transition(isInitiator(), endUserState, media);
|
||||||
|
xmppConnectionService.notifyJingleRtpConnectionUpdate(id.account, id.with, id.sessionId, endUserState);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateOngoingCallNotification() {
|
private void updateOngoingCallNotification() {
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
package eu.siacs.conversations.xmpp.jingle;
|
||||||
|
|
||||||
|
import android.media.AudioManager;
|
||||||
|
import android.media.ToneGenerator;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.Config;
|
||||||
|
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
|
|
||||||
|
public class ToneManager {
|
||||||
|
|
||||||
|
private final ToneGenerator toneGenerator;
|
||||||
|
|
||||||
|
private ToneState state = null;
|
||||||
|
private ScheduledFuture<?> currentTone;
|
||||||
|
|
||||||
|
public ToneManager() {
|
||||||
|
this.toneGenerator = new ToneGenerator(AudioManager.STREAM_VOICE_CALL, 35);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void transition(final boolean isInitiator, final RtpEndUserState state) {
|
||||||
|
transition(of(isInitiator, state, Collections.emptySet()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void transition(final boolean isInitiator, final RtpEndUserState state, final Set<Media> media) {
|
||||||
|
transition(of(isInitiator, state, media));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ToneState of(final boolean isInitiator, final RtpEndUserState state, final Set<Media> media) {
|
||||||
|
if (isInitiator) {
|
||||||
|
if (asList(RtpEndUserState.RINGING, RtpEndUserState.CONNECTING).contains(state)) {
|
||||||
|
return ToneState.RINGING;
|
||||||
|
}
|
||||||
|
if (state == RtpEndUserState.DECLINED_OR_BUSY) {
|
||||||
|
return ToneState.BUSY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (state == RtpEndUserState.ENDING_CALL) {
|
||||||
|
if (media.contains(Media.VIDEO)) {
|
||||||
|
return ToneState.NULL;
|
||||||
|
} else {
|
||||||
|
return ToneState.ENDING_CALL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ToneState.NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void transition(ToneState state) {
|
||||||
|
if (this.state == state) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (state == ToneState.NULL && this.state == ToneState.ENDING_CALL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cancelCurrentTone();
|
||||||
|
Log.d(Config.LOGTAG, getClass().getName() + ".transition(" + state + ")");
|
||||||
|
switch (state) {
|
||||||
|
case RINGING:
|
||||||
|
scheduleWaitingTone();
|
||||||
|
break;
|
||||||
|
case BUSY:
|
||||||
|
scheduleBusy();
|
||||||
|
break;
|
||||||
|
case ENDING_CALL:
|
||||||
|
scheduleEnding();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scheduleEnding() {
|
||||||
|
this.currentTone = JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE.schedule(() -> {
|
||||||
|
this.toneGenerator.startTone(ToneGenerator.TONE_CDMA_CONFIRM, 600);
|
||||||
|
}, 0, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scheduleBusy() {
|
||||||
|
this.currentTone = JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE.schedule(() -> {
|
||||||
|
this.toneGenerator.startTone(ToneGenerator.TONE_CDMA_NETWORK_BUSY, 2500);
|
||||||
|
}, 0, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scheduleWaitingTone() {
|
||||||
|
this.currentTone = JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE.scheduleAtFixedRate(() -> {
|
||||||
|
this.toneGenerator.startTone(ToneGenerator.TONE_CDMA_DIAL_TONE_LITE, 750);
|
||||||
|
}, 0, 3, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cancelCurrentTone() {
|
||||||
|
if (currentTone != null) {
|
||||||
|
currentTone.cancel(true);
|
||||||
|
}
|
||||||
|
toneGenerator.stopTone();
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum ToneState {
|
||||||
|
NULL, RINGING, BUSY, ENDING_CALL
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue