blob: 367a1deb12d6aebb89ba2244f3ea1b7af1acf57e [file] [log] [blame]
Sailesh Nepal6aca10a2014-03-24 16:11:02 -07001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.telecomm;
18
19import android.content.Context;
20import android.media.AudioManager;
Evan Charlton198fde82014-04-07 10:53:11 -070021import android.os.SystemVibrator;
22import android.os.Vibrator;
23import android.provider.Settings;
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070024import android.telecomm.CallState;
25
26import com.google.common.collect.Lists;
27
28import java.util.List;
29
30/**
31 * Controls the ringtone player.
32 */
33final class Ringer extends CallsManagerListenerBase {
Evan Charlton198fde82014-04-07 10:53:11 -070034 private static final long[] VIBRATION_PATTERN = new long[] {
35 0, // No delay before starting
36 1000, // How long to vibrate
37 1000, // How long to wait before vibrating again
38 };
39
40 /** Indicate that we want the pattern to repeat at the step which turns on vibration. */
41 private static final int VIBRATION_PATTERN_REPEAT = 1;
42
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070043 private final AsyncRingtonePlayer mRingtonePlayer = new AsyncRingtonePlayer();
44
45 /**
46 * Used to keep ordering of unanswered incoming calls. There can easily exist multiple incoming
47 * calls and explicit ordering is useful for maintaining the proper state of the ringer.
48 */
Sailesh Nepale59bb192014-04-01 18:33:59 -070049 private final List<Call> mUnansweredCalls = Lists.newLinkedList();
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070050
51 private final CallAudioManager mCallAudioManager;
Santos Cordon40f78c22014-04-07 02:11:42 -070052 private final CallsManager mCallsManager;
53 private final InCallTonePlayer.Factory mPlayerFactory;
54 private final Context mContext;
Evan Charlton198fde82014-04-07 10:53:11 -070055 private final Vibrator mVibrator;
56
Santos Cordon40f78c22014-04-07 02:11:42 -070057 private InCallTonePlayer mCallWaitingPlayer;
58
Evan Charlton198fde82014-04-07 10:53:11 -070059 /**
60 * Used to track the status of {@link #mVibrator} in the case of simultaneous incoming calls.
61 */
62 private boolean mIsVibrating = false;
63
Santos Cordon40f78c22014-04-07 02:11:42 -070064 /** Initializes the Ringer. */
65 Ringer(
66 CallAudioManager callAudioManager,
67 CallsManager callsManager,
68 InCallTonePlayer.Factory playerFactory,
69 Context context) {
Evan Charlton198fde82014-04-07 10:53:11 -070070
Santos Cordon40f78c22014-04-07 02:11:42 -070071 mCallAudioManager = callAudioManager;
72 mCallsManager = callsManager;
73 mPlayerFactory = playerFactory;
74 mContext = context;
Evan Charlton198fde82014-04-07 10:53:11 -070075 // We don't rely on getSystemService(Context.VIBRATOR_SERVICE) to make sure this
76 // vibrator object will be isolated from others.
77 mVibrator = new SystemVibrator(TelecommApp.getInstance());
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070078 }
79
80 @Override
81 public void onCallAdded(Call call) {
82 if (call.isIncoming() && call.getState() == CallState.RINGING) {
Sailesh Nepale59bb192014-04-01 18:33:59 -070083 if (mUnansweredCalls.contains(call)) {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070084 Log.wtf(this, "New ringing call is already in list of unanswered calls");
85 }
Sailesh Nepale59bb192014-04-01 18:33:59 -070086 mUnansweredCalls.add(call);
Santos Cordon40f78c22014-04-07 02:11:42 -070087 updateRinging();
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070088 }
89 }
90
Santos Cordon40f78c22014-04-07 02:11:42 -070091
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070092 @Override
93 public void onCallRemoved(Call call) {
Sailesh Nepale59bb192014-04-01 18:33:59 -070094 removeFromUnansweredCall(call);
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070095 }
96
97 @Override
98 public void onCallStateChanged(Call call, CallState oldState, CallState newState) {
99 if (newState != CallState.RINGING) {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700100 removeFromUnansweredCall(call);
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700101 }
102 }
103
104 @Override
105 public void onIncomingCallAnswered(Call call) {
106 onRespondedToIncomingCall(call);
107 }
108
109 @Override
110 public void onIncomingCallRejected(Call call) {
111 onRespondedToIncomingCall(call);
112 }
113
Santos Cordon40f78c22014-04-07 02:11:42 -0700114 @Override
115 public void onForegroundCallChanged(Call oldForegroundCall, Call newForegroundCall) {
116 if (mUnansweredCalls.contains(oldForegroundCall) ||
117 mUnansweredCalls.contains(newForegroundCall)) {
118 updateRinging();
119 }
120 }
121
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700122 private void onRespondedToIncomingCall(Call call) {
123 // Only stop the ringer if this call is the top-most incoming call.
Santos Cordon40f78c22014-04-07 02:11:42 -0700124 if (getTopMostUnansweredCall() == call) {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700125 stopRinging();
Santos Cordon40f78c22014-04-07 02:11:42 -0700126 stopCallWaiting();
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700127 }
Santos Cordon40f78c22014-04-07 02:11:42 -0700128
129 // We do not remove the call from mUnansweredCalls until the call state changes from RINGING
130 // or the call is removed. see onCallStateChanged or onCallRemoved.
131 }
132
133 private Call getTopMostUnansweredCall() {
134 return mUnansweredCalls.isEmpty() ? null : mUnansweredCalls.get(0);
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700135 }
136
137 /**
138 * Removes the specified call from the list of unanswered incoming calls and updates the ringer
Sailesh Nepale59bb192014-04-01 18:33:59 -0700139 * based on the new state of {@link #mUnansweredCalls}. Safe to call with a call that is not
140 * present in the list of incoming calls.
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700141 */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700142 private void removeFromUnansweredCall(Call call) {
Santos Cordon40f78c22014-04-07 02:11:42 -0700143 mUnansweredCalls.remove(call);
144 updateRinging();
145 }
146
147 private void updateRinging() {
148 if (mUnansweredCalls.isEmpty()) {
149 stopRinging();
150 stopCallWaiting();
151 } else {
152 startRingingOrCallWaiting();
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700153 }
154 }
155
Santos Cordon40f78c22014-04-07 02:11:42 -0700156 private void startRingingOrCallWaiting() {
157 Call foregroundCall = mCallsManager.getForegroundCall();
158 Log.v(this, "startRingingOrCallWaiting, foregroundCall: %s.", foregroundCall);
Evan Charlton198fde82014-04-07 10:53:11 -0700159
Santos Cordon40f78c22014-04-07 02:11:42 -0700160 if (mUnansweredCalls.contains(foregroundCall)) {
161 // The foreground call is one of incoming calls so play the ringer out loud.
162 stopCallWaiting();
163
164 AudioManager audioManager =
165 (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
166 if (audioManager.getStreamVolume(AudioManager.STREAM_RING) > 0) {
167 Log.v(this, "startRingingOrCallWaiting");
168 mCallAudioManager.setIsRinging(true);
169
170 mRingtonePlayer.play();
Santos Cordon40f78c22014-04-07 02:11:42 -0700171 } else {
172 Log.v(this, "startRingingOrCallWaiting, skipping because volume is 0");
173 }
Evan Charlton2186f7f2014-04-10 12:00:37 -0700174
175 if (shouldVibrate(TelecommApp.getInstance()) && !mIsVibrating) {
176 mVibrator.vibrate(VIBRATION_PATTERN, VIBRATION_PATTERN_REPEAT,
177 AudioManager.STREAM_RING);
178 mIsVibrating = true;
179 }
Santos Cordon40f78c22014-04-07 02:11:42 -0700180 } else {
181 Log.v(this, "Playing call-waiting tone.");
182
183 // All incoming calls are in background so play call waiting.
184 stopRinging();
185
186 if (mCallWaitingPlayer == null) {
187 mCallWaitingPlayer =
188 mPlayerFactory.createPlayer(InCallTonePlayer.TONE_CALL_WAITING);
189 mCallWaitingPlayer.startTone();
190 }
Evan Charlton198fde82014-04-07 10:53:11 -0700191 }
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700192 }
193
194 private void stopRinging() {
195 Log.v(this, "stopRinging");
Santos Cordon40f78c22014-04-07 02:11:42 -0700196
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700197 mRingtonePlayer.stop();
Evan Charlton198fde82014-04-07 10:53:11 -0700198
199 if (mIsVibrating) {
200 mVibrator.cancel();
201 mIsVibrating = false;
202 }
Santos Cordon40f78c22014-04-07 02:11:42 -0700203
204 // Even though stop is asynchronous it's ok to update the audio manager. Things like audio
205 // focus are voluntary so releasing focus too early is not detrimental.
206 mCallAudioManager.setIsRinging(false);
207 }
208
209 private void stopCallWaiting() {
210 Log.v(this, "stop call waiting.");
211 if (mCallWaitingPlayer != null) {
212 mCallWaitingPlayer.stopTone();
213 mCallWaitingPlayer = null;
214 }
Evan Charlton198fde82014-04-07 10:53:11 -0700215 }
216
217 private boolean shouldVibrate(Context context) {
218 AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
219 int ringerMode = audioManager.getRingerMode();
220 if (getVibrateWhenRinging(context)) {
221 return ringerMode != AudioManager.RINGER_MODE_SILENT;
222 } else {
223 return ringerMode == AudioManager.RINGER_MODE_VIBRATE;
224 }
225 }
226
227 private boolean getVibrateWhenRinging(Context context) {
228 if (!mVibrator.hasVibrator()) {
229 return false;
230 }
231 return Settings.System.getInt(context.getContentResolver(),
232 Settings.System.VIBRATE_WHEN_RINGING, 0) != 0;
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700233 }
234}