blob: fdc934967e16542a36bc4f25e73c793fdfd6a73a [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;
52
Evan Charlton198fde82014-04-07 10:53:11 -070053 private final Vibrator mVibrator;
54
55 /**
56 * Used to track the status of {@link #mVibrator} in the case of simultaneous incoming calls.
57 */
58 private boolean mIsVibrating = false;
59
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070060 Ringer(CallAudioManager callAudioManager) {
61 mCallAudioManager = callAudioManager;
Evan Charlton198fde82014-04-07 10:53:11 -070062
63 // We don't rely on getSystemService(Context.VIBRATOR_SERVICE) to make sure this
64 // vibrator object will be isolated from others.
65 mVibrator = new SystemVibrator(TelecommApp.getInstance());
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070066 }
67
68 @Override
69 public void onCallAdded(Call call) {
70 if (call.isIncoming() && call.getState() == CallState.RINGING) {
Sailesh Nepale59bb192014-04-01 18:33:59 -070071 if (mUnansweredCalls.contains(call)) {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070072 Log.wtf(this, "New ringing call is already in list of unanswered calls");
73 }
Sailesh Nepale59bb192014-04-01 18:33:59 -070074 mUnansweredCalls.add(call);
75 if (mUnansweredCalls.size() == 1) {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070076 // Start the ringer if we are the top-most incoming call (the only one in this
77 // case).
78 startRinging();
79 }
80 }
81 }
82
83 @Override
84 public void onCallRemoved(Call call) {
Sailesh Nepale59bb192014-04-01 18:33:59 -070085 removeFromUnansweredCall(call);
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070086 }
87
88 @Override
89 public void onCallStateChanged(Call call, CallState oldState, CallState newState) {
90 if (newState != CallState.RINGING) {
Sailesh Nepale59bb192014-04-01 18:33:59 -070091 removeFromUnansweredCall(call);
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070092 }
93 }
94
95 @Override
96 public void onIncomingCallAnswered(Call call) {
97 onRespondedToIncomingCall(call);
98 }
99
100 @Override
101 public void onIncomingCallRejected(Call call) {
102 onRespondedToIncomingCall(call);
103 }
104
105 private void onRespondedToIncomingCall(Call call) {
106 // Only stop the ringer if this call is the top-most incoming call.
Sailesh Nepale59bb192014-04-01 18:33:59 -0700107 if (!mUnansweredCalls.isEmpty() && mUnansweredCalls.get(0) == call) {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700108 stopRinging();
109 }
110 }
111
112 /**
113 * Removes the specified call from the list of unanswered incoming calls and updates the ringer
Sailesh Nepale59bb192014-04-01 18:33:59 -0700114 * based on the new state of {@link #mUnansweredCalls}. Safe to call with a call that is not
115 * present in the list of incoming calls.
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700116 */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700117 private void removeFromUnansweredCall(Call call) {
118 if (mUnansweredCalls.remove(call)) {
119 if (mUnansweredCalls.isEmpty()) {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700120 stopRinging();
121 } else {
122 startRinging();
123 }
124 }
125 }
126
127 private void startRinging() {
128 AudioManager audioManager = (AudioManager) TelecommApp.getInstance().getSystemService(
129 Context.AUDIO_SERVICE);
130 if (audioManager.getStreamVolume(AudioManager.STREAM_RING) > 0) {
131 Log.v(this, "startRinging");
132 mCallAudioManager.setIsRinging(true);
133 mRingtonePlayer.play();
134 } else {
135 Log.v(this, "startRinging, skipping because volume is 0");
136 }
Evan Charlton198fde82014-04-07 10:53:11 -0700137
138 if (shouldVibrate(TelecommApp.getInstance()) && !mIsVibrating) {
139 mVibrator.vibrate(VIBRATION_PATTERN, VIBRATION_PATTERN_REPEAT,
140 AudioManager.STREAM_RING);
141 mIsVibrating = true;
142 }
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700143 }
144
145 private void stopRinging() {
146 Log.v(this, "stopRinging");
147 mRingtonePlayer.stop();
148 // Even though stop is asynchronous it's ok to update the audio manager. Things like audio
149 // focus are voluntary so releasing focus too early is not detrimental.
150 mCallAudioManager.setIsRinging(false);
Evan Charlton198fde82014-04-07 10:53:11 -0700151
152 if (mIsVibrating) {
153 mVibrator.cancel();
154 mIsVibrating = false;
155 }
156 }
157
158 private boolean shouldVibrate(Context context) {
159 AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
160 int ringerMode = audioManager.getRingerMode();
161 if (getVibrateWhenRinging(context)) {
162 return ringerMode != AudioManager.RINGER_MODE_SILENT;
163 } else {
164 return ringerMode == AudioManager.RINGER_MODE_VIBRATE;
165 }
166 }
167
168 private boolean getVibrateWhenRinging(Context context) {
169 if (!mVibrator.hasVibrator()) {
170 return false;
171 }
172 return Settings.System.getInt(context.getContentResolver(),
173 Settings.System.VIBRATE_WHEN_RINGING, 0) != 0;
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700174 }
175}