1package eu.siacs.conversations.xmpp.jingle;
2
3import android.media.AudioManager;
4import android.media.ToneGenerator;
5import android.util.Log;
6
7import java.util.Collections;
8import java.util.Set;
9import java.util.concurrent.ScheduledFuture;
10import java.util.concurrent.TimeUnit;
11
12import eu.siacs.conversations.Config;
13
14import static java.util.Arrays.asList;
15
16public class ToneManager {
17
18 private final ToneGenerator toneGenerator;
19
20 private ToneState state = null;
21 private ScheduledFuture<?> currentTone;
22
23 public ToneManager() {
24 this.toneGenerator = new ToneGenerator(AudioManager.STREAM_VOICE_CALL, 35);
25 }
26
27 public void transition(final boolean isInitiator, final RtpEndUserState state) {
28 transition(of(isInitiator, state, Collections.emptySet()));
29 }
30
31 public void transition(final boolean isInitiator, final RtpEndUserState state, final Set<Media> media) {
32 transition(of(isInitiator, state, media));
33 }
34
35 private static ToneState of(final boolean isInitiator, final RtpEndUserState state, final Set<Media> media) {
36 if (isInitiator) {
37 if (asList(RtpEndUserState.RINGING, RtpEndUserState.CONNECTING).contains(state)) {
38 return ToneState.RINGING;
39 }
40 if (state == RtpEndUserState.DECLINED_OR_BUSY) {
41 return ToneState.BUSY;
42 }
43 }
44 if (state == RtpEndUserState.ENDING_CALL) {
45 if (media.contains(Media.VIDEO)) {
46 return ToneState.NULL;
47 } else {
48 return ToneState.ENDING_CALL;
49 }
50 }
51 return ToneState.NULL;
52 }
53
54 private synchronized void transition(ToneState state) {
55 if (this.state == state) {
56 return;
57 }
58 if (state == ToneState.NULL && this.state == ToneState.ENDING_CALL) {
59 return;
60 }
61 cancelCurrentTone();
62 Log.d(Config.LOGTAG, getClass().getName() + ".transition(" + state + ")");
63 switch (state) {
64 case RINGING:
65 scheduleWaitingTone();
66 break;
67 case BUSY:
68 scheduleBusy();
69 break;
70 case ENDING_CALL:
71 scheduleEnding();
72 break;
73 }
74 this.state = state;
75 }
76
77 private void scheduleEnding() {
78 this.currentTone = JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE.schedule(() -> {
79 this.toneGenerator.startTone(ToneGenerator.TONE_CDMA_CONFIRM, 600);
80 }, 0, TimeUnit.SECONDS);
81 }
82
83 private void scheduleBusy() {
84 this.currentTone = JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE.schedule(() -> {
85 this.toneGenerator.startTone(ToneGenerator.TONE_CDMA_NETWORK_BUSY, 2500);
86 }, 0, TimeUnit.SECONDS);
87 }
88
89 private void scheduleWaitingTone() {
90 this.currentTone = JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE.scheduleAtFixedRate(() -> {
91 this.toneGenerator.startTone(ToneGenerator.TONE_CDMA_DIAL_TONE_LITE, 750);
92 }, 0, 3, TimeUnit.SECONDS);
93 }
94
95 private void cancelCurrentTone() {
96 if (currentTone != null) {
97 currentTone.cancel(true);
98 }
99 toneGenerator.stopTone();
100 }
101
102 private enum ToneState {
103 NULL, RINGING, BUSY, ENDING_CALL
104 }
105}