ToneManager.java

  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}