Add call waiting to Ringer.java.
When an incoming call is in the background, play call waiting instead of
ringer.
Bug: 13674415
Change-Id: I9d450202774c6890794fa318fdab01290b4a7eb2
diff --git a/src/com/android/telecomm/Call.java b/src/com/android/telecomm/Call.java
index 6aae5ca..2db5b4e 100644
--- a/src/com/android/telecomm/Call.java
+++ b/src/com/android/telecomm/Call.java
@@ -461,6 +461,17 @@
}
}
+ boolean isActive() {
+ switch (mState) {
+ case ACTIVE:
+ case POST_DIAL:
+ case POST_DIAL_WAIT:
+ return true;
+ default:
+ return false;
+ }
+ }
+
Bundle getExtras() {
return mExtras;
}
diff --git a/src/com/android/telecomm/CallsManager.java b/src/com/android/telecomm/CallsManager.java
index d3b22be..8e81752 100644
--- a/src/com/android/telecomm/CallsManager.java
+++ b/src/com/android/telecomm/CallsManager.java
@@ -110,7 +110,8 @@
mListeners.add(new CallLogManager(app));
mListeners.add(new PhoneStateBroadcaster());
mListeners.add(new InCallController());
- mListeners.add(new Ringer(mCallAudioManager));
+ mListeners.add(
+ new Ringer(mCallAudioManager, this, playerFactory, TelecommApp.getInstance()));
mListeners.add(new RingbackPlayer(this, playerFactory));
mListeners.add(new InCallToneMonitor(playerFactory, this));
mListeners.add(mCallAudioManager);
@@ -278,6 +279,19 @@
if (!mCalls.contains(call)) {
Log.i(this, "Request to answer a non-existent call %s", call);
} else {
+ // If the foreground call is not the ringing call and it is currently isActive() or
+ // DIALING, put it on hold before answering the call.
+ if (mForegroundCall != null && mForegroundCall != call &&
+ (mForegroundCall.isActive() ||
+ mForegroundCall.getState() == CallState.DIALING)) {
+ Log.v(this, "Holding active/dialing call %s before answering incoming call %s.",
+ mForegroundCall, call);
+ mForegroundCall.hold();
+ // TODO(santoscordon): Wait until we get confirmation of the active call being
+ // on-hold before answering the new call.
+ // TODO(santoscordon): Import logic from CallManager.acceptCall()
+ }
+
for (CallsManagerListener listener : mListeners) {
listener.onIncomingCallAnswered(call);
}
@@ -514,6 +528,9 @@
*/
private void addCall(Call call) {
mCalls.add(call);
+
+ // TODO(santoscordon): Update mForegroundCall prior to invoking
+ // onCallAdded for calls which immediately take the foreground (like the first call).
for (CallsManagerListener listener : mListeners) {
listener.onCallAdded(call);
}
@@ -581,18 +598,25 @@
private void updateForegroundCall() {
Call newForegroundCall = null;
for (Call call : mCalls) {
- // Incoming ringing calls have priority.
- if (call.getState() == CallState.RINGING) {
+ // TODO(santoscordon): Foreground-ness needs to be explicitly set. No call, regardless
+ // of its state will be foreground by default and instead the call service should be
+ // notified when its calls enter and exit foreground state. Foreground will mean that
+ // the call should play audio and listen to microphone if it wants.
+
+ // Active calls have priority.
+ if (call.isActive()) {
newForegroundCall = call;
break;
}
- if (call.isAlive()) {
+
+ if (call.isAlive() || call.getState() == CallState.RINGING) {
newForegroundCall = call;
- // Don't break in case there's a ringing call that has priority.
+ // Don't break in case there's an active call that has priority.
}
}
if (newForegroundCall != mForegroundCall) {
+ Log.v(this, "Updating foreground call, %s -> %s.", mForegroundCall, newForegroundCall);
Call oldForegroundCall = mForegroundCall;
mForegroundCall = newForegroundCall;
diff --git a/src/com/android/telecomm/InCallTonePlayer.java b/src/com/android/telecomm/InCallTonePlayer.java
index 258f8b0..aa9655d 100644
--- a/src/com/android/telecomm/InCallTonePlayer.java
+++ b/src/com/android/telecomm/InCallTonePlayer.java
@@ -127,8 +127,10 @@
// TODO: fill in
throw new IllegalStateException("OTA Call ended NYI.");
case TONE_CALL_WAITING:
- // TODO: fill in.
- throw new IllegalStateException("Call waiting NYI.");
+ toneType = ToneGenerator.TONE_SUP_CALL_WAITING;
+ toneVolume = RELATIVE_VOLUME_HIPRI;
+ toneLengthMillis = Integer.MAX_VALUE - TIMEOUT_BUFFER_MILLIS;
+ break;
case TONE_CDMA_DROP:
toneType = ToneGenerator.TONE_CDMA_CALLDROP_LITE;
toneVolume = RELATIVE_VOLUME_LOPRI;
diff --git a/src/com/android/telecomm/Ringer.java b/src/com/android/telecomm/Ringer.java
index fdc9349..f298081 100644
--- a/src/com/android/telecomm/Ringer.java
+++ b/src/com/android/telecomm/Ringer.java
@@ -49,17 +49,29 @@
private final List<Call> mUnansweredCalls = Lists.newLinkedList();
private final CallAudioManager mCallAudioManager;
-
+ private final CallsManager mCallsManager;
+ private final InCallTonePlayer.Factory mPlayerFactory;
+ private final Context mContext;
private final Vibrator mVibrator;
+ private InCallTonePlayer mCallWaitingPlayer;
+
/**
* Used to track the status of {@link #mVibrator} in the case of simultaneous incoming calls.
*/
private boolean mIsVibrating = false;
- Ringer(CallAudioManager callAudioManager) {
- mCallAudioManager = callAudioManager;
+ /** Initializes the Ringer. */
+ Ringer(
+ CallAudioManager callAudioManager,
+ CallsManager callsManager,
+ InCallTonePlayer.Factory playerFactory,
+ Context context) {
+ mCallAudioManager = callAudioManager;
+ mCallsManager = callsManager;
+ mPlayerFactory = playerFactory;
+ mContext = context;
// We don't rely on getSystemService(Context.VIBRATOR_SERVICE) to make sure this
// vibrator object will be isolated from others.
mVibrator = new SystemVibrator(TelecommApp.getInstance());
@@ -72,14 +84,11 @@
Log.wtf(this, "New ringing call is already in list of unanswered calls");
}
mUnansweredCalls.add(call);
- if (mUnansweredCalls.size() == 1) {
- // Start the ringer if we are the top-most incoming call (the only one in this
- // case).
- startRinging();
- }
+ updateRinging();
}
}
+
@Override
public void onCallRemoved(Call call) {
removeFromUnansweredCall(call);
@@ -102,11 +111,27 @@
onRespondedToIncomingCall(call);
}
+ @Override
+ public void onForegroundCallChanged(Call oldForegroundCall, Call newForegroundCall) {
+ if (mUnansweredCalls.contains(oldForegroundCall) ||
+ mUnansweredCalls.contains(newForegroundCall)) {
+ updateRinging();
+ }
+ }
+
private void onRespondedToIncomingCall(Call call) {
// Only stop the ringer if this call is the top-most incoming call.
- if (!mUnansweredCalls.isEmpty() && mUnansweredCalls.get(0) == call) {
+ if (getTopMostUnansweredCall() == call) {
stopRinging();
+ stopCallWaiting();
}
+
+ // We do not remove the call from mUnansweredCalls until the call state changes from RINGING
+ // or the call is removed. see onCallStateChanged or onCallRemoved.
+ }
+
+ private Call getTopMostUnansweredCall() {
+ return mUnansweredCalls.isEmpty() ? null : mUnansweredCalls.get(0);
}
/**
@@ -115,44 +140,78 @@
* present in the list of incoming calls.
*/
private void removeFromUnansweredCall(Call call) {
- if (mUnansweredCalls.remove(call)) {
- if (mUnansweredCalls.isEmpty()) {
- stopRinging();
- } else {
- startRinging();
- }
+ mUnansweredCalls.remove(call);
+ updateRinging();
+ }
+
+ private void updateRinging() {
+ if (mUnansweredCalls.isEmpty()) {
+ stopRinging();
+ stopCallWaiting();
+ } else {
+ startRingingOrCallWaiting();
}
}
- private void startRinging() {
- AudioManager audioManager = (AudioManager) TelecommApp.getInstance().getSystemService(
- Context.AUDIO_SERVICE);
- if (audioManager.getStreamVolume(AudioManager.STREAM_RING) > 0) {
- Log.v(this, "startRinging");
- mCallAudioManager.setIsRinging(true);
- mRingtonePlayer.play();
- } else {
- Log.v(this, "startRinging, skipping because volume is 0");
- }
+ private void startRingingOrCallWaiting() {
+ Call foregroundCall = mCallsManager.getForegroundCall();
+ Log.v(this, "startRingingOrCallWaiting, foregroundCall: %s.", foregroundCall);
- if (shouldVibrate(TelecommApp.getInstance()) && !mIsVibrating) {
- mVibrator.vibrate(VIBRATION_PATTERN, VIBRATION_PATTERN_REPEAT,
- AudioManager.STREAM_RING);
- mIsVibrating = true;
+ if (mUnansweredCalls.contains(foregroundCall)) {
+ // The foreground call is one of incoming calls so play the ringer out loud.
+ stopCallWaiting();
+
+ AudioManager audioManager =
+ (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ if (audioManager.getStreamVolume(AudioManager.STREAM_RING) > 0) {
+ Log.v(this, "startRingingOrCallWaiting");
+ mCallAudioManager.setIsRinging(true);
+
+ mRingtonePlayer.play();
+
+ if (shouldVibrate(TelecommApp.getInstance()) && !mIsVibrating) {
+ mVibrator.vibrate(VIBRATION_PATTERN, VIBRATION_PATTERN_REPEAT,
+ AudioManager.STREAM_RING);
+ mIsVibrating = true;
+ }
+ } else {
+ Log.v(this, "startRingingOrCallWaiting, skipping because volume is 0");
+ }
+ } else {
+ Log.v(this, "Playing call-waiting tone.");
+
+ // All incoming calls are in background so play call waiting.
+ stopRinging();
+
+ if (mCallWaitingPlayer == null) {
+ mCallWaitingPlayer =
+ mPlayerFactory.createPlayer(InCallTonePlayer.TONE_CALL_WAITING);
+ mCallWaitingPlayer.startTone();
+ }
}
}
private void stopRinging() {
Log.v(this, "stopRinging");
+
mRingtonePlayer.stop();
- // Even though stop is asynchronous it's ok to update the audio manager. Things like audio
- // focus are voluntary so releasing focus too early is not detrimental.
- mCallAudioManager.setIsRinging(false);
if (mIsVibrating) {
mVibrator.cancel();
mIsVibrating = false;
}
+
+ // Even though stop is asynchronous it's ok to update the audio manager. Things like audio
+ // focus are voluntary so releasing focus too early is not detrimental.
+ mCallAudioManager.setIsRinging(false);
+ }
+
+ private void stopCallWaiting() {
+ Log.v(this, "stop call waiting.");
+ if (mCallWaitingPlayer != null) {
+ mCallWaitingPlayer.stopTone();
+ mCallWaitingPlayer = null;
+ }
}
private boolean shouldVibrate(Context context) {