CallList.java revision 14dbc3f0ca3a7998c9c5d28aba637487724f529f
196b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon/* 296b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon * Copyright (C) 2013 The Android Open Source Project 396b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon * 496b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon * Licensed under the Apache License, Version 2.0 (the "License"); 596b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon * you may not use this file except in compliance with the License. 696b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon * You may obtain a copy of the License at 796b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon * 896b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon * http://www.apache.org/licenses/LICENSE-2.0 996b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon * 1096b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon * Unless required by applicable law or agreed to in writing, software 1196b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon * distributed under the License is distributed on an "AS IS" BASIS, 1296b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1396b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon * See the License for the specific language governing permissions and 1496b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon * limitations under the License 1596b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon */ 1696b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon 1796b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordonpackage com.android.incallui; 1896b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon 198b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Chengimport com.google.android.collect.Lists; 2096b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordonimport com.google.android.collect.Maps; 21a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordonimport com.google.android.collect.Sets; 2296b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordonimport com.google.common.base.Preconditions; 23671b221ccadea34fb9327ef5342b26419eb5ca99Santos Cordon 24a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordonimport android.os.Handler; 25a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordonimport android.os.Message; 26a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon 2796b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordonimport com.android.services.telephony.common.Call; 2896b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon 2910196fd2f7cc922693e2a7c6c932725a52157943Christine Chenimport java.util.ArrayList; 3096b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordonimport java.util.HashMap; 3196b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordonimport java.util.List; 32a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordonimport java.util.Set; 3396b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon 3496b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon/** 35671b221ccadea34fb9327ef5342b26419eb5ca99Santos Cordon * Maintains the list of active calls received from CallHandlerService and notifies interested 36671b221ccadea34fb9327ef5342b26419eb5ca99Santos Cordon * classes of changes to the call list as they are received from the telephony stack. 37671b221ccadea34fb9327ef5342b26419eb5ca99Santos Cordon * Primary lister of changes to this class is InCallPresenter. 3896b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon */ 3996b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordonpublic class CallList { 40671b221ccadea34fb9327ef5342b26419eb5ca99Santos Cordon 41ea83891eb10811d5020cc5fdaf34b711012dab32Santos Cordon private static final int DISCONNECTED_CALL_SHORT_TIMEOUT_MS = 200; 42ea83891eb10811d5020cc5fdaf34b711012dab32Santos Cordon private static final int DISCONNECTED_CALL_MEDIUM_TIMEOUT_MS = 2000; 43ea83891eb10811d5020cc5fdaf34b711012dab32Santos Cordon private static final int DISCONNECTED_CALL_LONG_TIMEOUT_MS = 5000; 44a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon 45a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon private static final int EVENT_DISCONNECTED_TIMEOUT = 1; 46a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon 478b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng private static CallList sInstance = new CallList(); 48a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon 49a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon private final HashMap<Integer, Call> mCallMap = Maps.newHashMap(); 5010196fd2f7cc922693e2a7c6c932725a52157943Christine Chen private final HashMap<Integer, ArrayList<String>> mCallTextReponsesMap = 5110196fd2f7cc922693e2a7c6c932725a52157943Christine Chen Maps.newHashMap(); 52a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon private final Set<Listener> mListeners = Sets.newArraySet(); 538b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng private final HashMap<Integer, List<CallUpdateListener>> mCallUpdateListenerMap = Maps 548b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng .newHashMap(); 558b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng 56a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon 57a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon /** 58a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon * Static singleton accessor method. 59a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon */ 608b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng public static CallList getInstance() { 61a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon return sInstance; 628b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng } 6396b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon 64a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon /** 65a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon * Private constructor. Instance should only be acquired through getInstance(). 66a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon */ 678b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng private CallList() { 68a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon } 6996b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon 70a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon /** 71a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon * Called when a single call has changed. 72a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon */ 7396b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon public void onUpdate(Call call) { 741a7f2bcab2d2023f2ee4cfb0bc57bc265b5aab87Chiao Cheng Log.d(this, "onUpdate - ", call); 75a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon 76a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon updateCallInMap(call); 77a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon notifyListenersOfChange(); 78a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon } 79a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon 80a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon /** 81a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon * Called when a single call disconnects. 82a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon */ 83a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon public void onDisconnect(Call call) { 841a7f2bcab2d2023f2ee4cfb0bc57bc265b5aab87Chiao Cheng Log.d(this, "onDisconnect: ", call); 85a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon 86671b221ccadea34fb9327ef5342b26419eb5ca99Santos Cordon updateCallInMap(call); 8710196fd2f7cc922693e2a7c6c932725a52157943Christine Chen 88e0b102c5caa81f2717da1ac951a45699c7f319beSantos Cordon notifyCallUpdateListeners(call); 8910196fd2f7cc922693e2a7c6c932725a52157943Christine Chen notifyListenersOfChange(); 9010196fd2f7cc922693e2a7c6c932725a52157943Christine Chen } 9110196fd2f7cc922693e2a7c6c932725a52157943Christine Chen 9210196fd2f7cc922693e2a7c6c932725a52157943Christine Chen /** 9310196fd2f7cc922693e2a7c6c932725a52157943Christine Chen * Called when a single call has changed. 9410196fd2f7cc922693e2a7c6c932725a52157943Christine Chen */ 958b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng public void onIncoming(Call call, List<String> textMessages) { 968b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng Log.d(this, "onIncoming - " + call); 9710196fd2f7cc922693e2a7c6c932725a52157943Christine Chen 988b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng updateCallInMap(call); 998b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng updateCallTextMap(call, textMessages); 10010196fd2f7cc922693e2a7c6c932725a52157943Christine Chen 1018b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng for (Listener listener : mListeners) { 1028b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng listener.onIncomingCall(call); 1038b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng } 10496b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon } 10596b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon 106a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon /** 107a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon * Called when multiple calls have changed. 108a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon */ 10996b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon public void onUpdate(List<Call> callsToUpdate) { 1101a7f2bcab2d2023f2ee4cfb0bc57bc265b5aab87Chiao Cheng Log.d(this, "onUpdate(...)"); 111671b221ccadea34fb9327ef5342b26419eb5ca99Santos Cordon 11296b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon Preconditions.checkNotNull(callsToUpdate); 11396b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon for (Call call : callsToUpdate) { 1141a7f2bcab2d2023f2ee4cfb0bc57bc265b5aab87Chiao Cheng Log.d(this, "\t" + call); 115671b221ccadea34fb9327ef5342b26419eb5ca99Santos Cordon 11696b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon updateCallInMap(call); 11710196fd2f7cc922693e2a7c6c932725a52157943Christine Chen updateCallTextMap(call, null); 1188b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng 1198b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng notifyCallUpdateListeners(call); 12096b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon } 121a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon 122a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon notifyListenersOfChange(); 123a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon } 124a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon 1258b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng public void notifyCallUpdateListeners(Call call) { 1268b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng final List<CallUpdateListener> listeners = mCallUpdateListenerMap.get(call.getCallId()); 1278b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng if (listeners != null) { 1288b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng for (CallUpdateListener listener : listeners) { 1298b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng listener.onCallStateChanged(call); 1308b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng } 1318b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng } 1328b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng } 1338b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng 1348b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng /** 1358b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng * Add a call update listener for a call id. 1368b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng * 1378b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng * @param callId The call id to get updates for. 1388b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng * @param listener The listener to add. 1398b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng */ 1408b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng public void addCallUpdateListener(int callId, CallUpdateListener listener) { 1418b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng List<CallUpdateListener> listeners = mCallUpdateListenerMap.get(callId); 1428b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng if (listeners == null) { 1438b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng listeners = Lists.newArrayList(); 1448b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng mCallUpdateListenerMap.put(callId, listeners); 1458b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng } 1468b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng listeners.add(listener); 1478b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng } 1488b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng 1498b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng /** 1508b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng * Remove a call update listener for a call id. 1518b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng * 1528b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng * @param callId The call id to remove the listener for. 1538b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng * @param listener The listener to remove. 1548b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng */ 1558b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng public void removeCallUpdateListener(int callId, CallUpdateListener listener) { 1568b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng List<CallUpdateListener> listeners = mCallUpdateListenerMap.get(callId); 1578b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng if (listeners != null) { 1588b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng listeners.remove(listener); 1598b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng } 1608b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng } 1618b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng 162a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon public void addListener(Listener listener) { 163a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon Preconditions.checkNotNull(listener); 164150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon 165a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon mListeners.add(listener); 166150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon 167150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon // Let the listener know about the active calls immediately. 168150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon listener.onCallListChange(this); 169a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon } 170a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon 171a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon public void removeListener(Listener listener) { 172a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon Preconditions.checkNotNull(listener); 173a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon mListeners.remove(listener); 174a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon } 175a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon 176a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon /** 177e7be13fb0556e62b07bc271b130412d82d7f7521Santos Cordon * TODO: Change so that this function is not needed. Instead of assuming there is an active 178a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon * call, the code should rely on the status of a specific Call and allow the presenters to 179a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon * update the Call object when the active call changes. 180a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon */ 181a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon public Call getIncomingOrActive() { 182150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon Call retval = getIncomingCall(); 183150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon if (retval == null) { 184671b221ccadea34fb9327ef5342b26419eb5ca99Santos Cordon retval = getActiveCall(); 185150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon } 186150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon return retval; 187150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon } 188150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon 189e7be13fb0556e62b07bc271b130412d82d7f7521Santos Cordon public Call getOutgoingCall() { 190e7be13fb0556e62b07bc271b130412d82d7f7521Santos Cordon return getFirstCallWithState(Call.State.DIALING); 191e7be13fb0556e62b07bc271b130412d82d7f7521Santos Cordon } 192e7be13fb0556e62b07bc271b130412d82d7f7521Santos Cordon 193671b221ccadea34fb9327ef5342b26419eb5ca99Santos Cordon public Call getActiveCall() { 194671b221ccadea34fb9327ef5342b26419eb5ca99Santos Cordon return getFirstCallWithState(Call.State.ACTIVE); 195671b221ccadea34fb9327ef5342b26419eb5ca99Santos Cordon } 196671b221ccadea34fb9327ef5342b26419eb5ca99Santos Cordon 197150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon public Call getBackgroundCall() { 198150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon return getFirstCallWithState(Call.State.ONHOLD); 199150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon } 200150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon 201a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon public Call getDisconnectedCall() { 202a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon return getFirstCallWithState(Call.State.DISCONNECTED); 203a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon } 204a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon 205efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon public Call getSecondBackgroundCall() { 206efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon return getCallWithState(Call.State.ONHOLD, 1); 207efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon } 208efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon 209efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon public Call getActiveOrBackgroundCall() { 210efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon Call call = getActiveCall(); 211efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon if (call == null) { 212efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon call = getBackgroundCall(); 213efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon } 214efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon return call; 215efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon } 216efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon 217150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon public Call getIncomingCall() { 218efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon Call call = getFirstCallWithState(Call.State.INCOMING); 219efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon if (call == null) { 220efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon call = getFirstCallWithState(Call.State.CALL_WAITING); 221efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon } 222efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon 223efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon return call; 224150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon } 225a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon 2268b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng 2278b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng public Call getFirstCall() { 2288b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng // TODO: should we switch to a simple list and pull the first one? 2298b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng Call result = getIncomingCall(); 2308b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng if (result == null) { 2318b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng result = getFirstCallWithState(Call.State.DIALING); 2328b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng } 2338b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng if (result == null) { 2348b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng result = getFirstCallWithState(Call.State.ACTIVE); 2358b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng } 2368b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng return result; 2378b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng } 2388b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng 23914dbc3f0ca3a7998c9c5d28aba637487724f529fSantos Cordon public Call getCall(int callId) { 24014dbc3f0ca3a7998c9c5d28aba637487724f529fSantos Cordon return mCallMap.get(callId); 24114dbc3f0ca3a7998c9c5d28aba637487724f529fSantos Cordon } 24214dbc3f0ca3a7998c9c5d28aba637487724f529fSantos Cordon 243150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon public boolean existsLiveCall() { 244a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon for (Call call : mCallMap.values()) { 245150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon if (!isCallDead(call)) { 246150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon return true; 247150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon } 248150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon } 249150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon return false; 250150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon } 251150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon 2528b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng public ArrayList<String> getTextResponses(int callId) { 2538b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng return mCallTextReponsesMap.get(callId); 25410196fd2f7cc922693e2a7c6c932725a52157943Christine Chen } 25510196fd2f7cc922693e2a7c6c932725a52157943Christine Chen 256150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon /** 257150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon * Returns first call found in the call map with the specified state. 258150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon */ 259150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon public Call getFirstCallWithState(int state) { 260efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon return getCallWithState(state, 0); 261efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon } 262efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon 263efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon /** 264efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon * Returns the [position]th call found in the call map with the specified state. 265efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon * TODO(klp): Improve this logic to sort by call time. 266efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon */ 267efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon public Call getCallWithState(int state, int positionToFind) { 268150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon Call retval = null; 269efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon int position = 0; 270150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon for (Call call : mCallMap.values()) { 271150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon if (call.getState() == state) { 272efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon if (position >= positionToFind) { 273efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon retval = call; 274efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon break; 275efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon } else { 276efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon position++; 277efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon } 278a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon } 279a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon } 280a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon 281a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon return retval; 282a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon } 283a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon 284a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon /** 2853c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon * This is called when the service disconnects, either expectedly or unexpectedly. 2863c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon * For the expected case, it's because we have no calls left. For the unexpected case, 2873c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon * it is likely a crash of phone and we need to clean up our calls manually. Without phone, 2883c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon * there can be no active calls, so this is relatively safe thing to do. 2893c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon */ 2903c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon public void clearOnDisconnect() { 2913c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon for (Call call : mCallMap.values()) { 2923c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon final int state = call.getState(); 2933c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon if (state != Call.State.IDLE && 2943c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon state != Call.State.INVALID && 2953c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon state != Call.State.DISCONNECTED) { 2963c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon call.setState(Call.State.DISCONNECTED); 2973c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon updateCallInMap(call); 2983c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon } 2993c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon } 3003c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon notifyListenersOfChange(); 3013c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon } 3023c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon 3033c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon /** 304a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon * Sends a generic notification to all listeners that something has changed. 305a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon * It is up to the listeners to call back to determine what changed. 306a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon */ 307a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon private void notifyListenersOfChange() { 308a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon for (Listener listener : mListeners) { 309a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon listener.onCallListChange(this); 310a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon } 31196b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon } 31296b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon 31396b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon private void updateCallInMap(Call call) { 31496b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon Preconditions.checkNotNull(call); 31596b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon 31696b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon final Integer id = new Integer(call.getCallId()); 31796b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon 318a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon if (call.getState() == Call.State.DISCONNECTED) { 319a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon 3201df52df7a0248814fbd4575103059a8b427f5d9aSantos Cordon // update existing (but do not add!!) disconnected calls 3211df52df7a0248814fbd4575103059a8b427f5d9aSantos Cordon if (mCallMap.containsKey(id)) { 322a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon 3231df52df7a0248814fbd4575103059a8b427f5d9aSantos Cordon // For disconnected calls, we want to keep them alive for a few seconds so that the 3241df52df7a0248814fbd4575103059a8b427f5d9aSantos Cordon // UI has a chance to display anything it needs when a call is disconnected. 3251df52df7a0248814fbd4575103059a8b427f5d9aSantos Cordon 3261df52df7a0248814fbd4575103059a8b427f5d9aSantos Cordon // Set up a timer to destroy the call after X seconds. 3271df52df7a0248814fbd4575103059a8b427f5d9aSantos Cordon final Message msg = mHandler.obtainMessage(EVENT_DISCONNECTED_TIMEOUT, call); 328ea83891eb10811d5020cc5fdaf34b711012dab32Santos Cordon mHandler.sendMessageDelayed(msg, getDelayForDisconnect(call)); 329a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon 330a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon mCallMap.put(id, call); 331a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon } 332a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon } else if (!isCallDead(call)) { 333a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon mCallMap.put(id, call); 334a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon } else if (mCallMap.containsKey(id)) { 335a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon mCallMap.remove(id); 33696b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon } 33796b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon } 33896b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon 339ea83891eb10811d5020cc5fdaf34b711012dab32Santos Cordon private int getDelayForDisconnect(Call call) { 340ea83891eb10811d5020cc5fdaf34b711012dab32Santos Cordon Preconditions.checkState(call.getState() == Call.State.DISCONNECTED); 341ea83891eb10811d5020cc5fdaf34b711012dab32Santos Cordon 342ea83891eb10811d5020cc5fdaf34b711012dab32Santos Cordon 343ea83891eb10811d5020cc5fdaf34b711012dab32Santos Cordon final Call.DisconnectCause cause = call.getDisconnectCause(); 344ea83891eb10811d5020cc5fdaf34b711012dab32Santos Cordon final int delay; 345ea83891eb10811d5020cc5fdaf34b711012dab32Santos Cordon switch (cause) { 346ea83891eb10811d5020cc5fdaf34b711012dab32Santos Cordon case LOCAL: 347ea83891eb10811d5020cc5fdaf34b711012dab32Santos Cordon delay = DISCONNECTED_CALL_SHORT_TIMEOUT_MS; 348ea83891eb10811d5020cc5fdaf34b711012dab32Santos Cordon break; 349ea83891eb10811d5020cc5fdaf34b711012dab32Santos Cordon case NORMAL: 350ea83891eb10811d5020cc5fdaf34b711012dab32Santos Cordon delay = DISCONNECTED_CALL_MEDIUM_TIMEOUT_MS; 351ea83891eb10811d5020cc5fdaf34b711012dab32Santos Cordon break; 352ea83891eb10811d5020cc5fdaf34b711012dab32Santos Cordon case INCOMING_REJECTED: 353ea83891eb10811d5020cc5fdaf34b711012dab32Santos Cordon case INCOMING_MISSED: 354ea83891eb10811d5020cc5fdaf34b711012dab32Santos Cordon // no delay for missed/rejected incoming calls 355ea83891eb10811d5020cc5fdaf34b711012dab32Santos Cordon delay = 0; 356ea83891eb10811d5020cc5fdaf34b711012dab32Santos Cordon break; 357ea83891eb10811d5020cc5fdaf34b711012dab32Santos Cordon default: 358ea83891eb10811d5020cc5fdaf34b711012dab32Santos Cordon delay = DISCONNECTED_CALL_LONG_TIMEOUT_MS; 359ea83891eb10811d5020cc5fdaf34b711012dab32Santos Cordon break; 360ea83891eb10811d5020cc5fdaf34b711012dab32Santos Cordon } 361ea83891eb10811d5020cc5fdaf34b711012dab32Santos Cordon 362ea83891eb10811d5020cc5fdaf34b711012dab32Santos Cordon return delay; 363ea83891eb10811d5020cc5fdaf34b711012dab32Santos Cordon } 364ea83891eb10811d5020cc5fdaf34b711012dab32Santos Cordon 36510196fd2f7cc922693e2a7c6c932725a52157943Christine Chen private void updateCallTextMap(Call call, List<String> textResponses) { 36610196fd2f7cc922693e2a7c6c932725a52157943Christine Chen Preconditions.checkNotNull(call); 36710196fd2f7cc922693e2a7c6c932725a52157943Christine Chen 36810196fd2f7cc922693e2a7c6c932725a52157943Christine Chen final Integer id = new Integer(call.getCallId()); 36910196fd2f7cc922693e2a7c6c932725a52157943Christine Chen 37010196fd2f7cc922693e2a7c6c932725a52157943Christine Chen if (!isCallDead(call)) { 37110196fd2f7cc922693e2a7c6c932725a52157943Christine Chen if (textResponses != null) { 37210196fd2f7cc922693e2a7c6c932725a52157943Christine Chen mCallTextReponsesMap.put(id, (ArrayList<String>) textResponses); 37310196fd2f7cc922693e2a7c6c932725a52157943Christine Chen } 37410196fd2f7cc922693e2a7c6c932725a52157943Christine Chen } else if (mCallMap.containsKey(id)) { 37510196fd2f7cc922693e2a7c6c932725a52157943Christine Chen mCallTextReponsesMap.remove(id); 37610196fd2f7cc922693e2a7c6c932725a52157943Christine Chen } 37710196fd2f7cc922693e2a7c6c932725a52157943Christine Chen } 37810196fd2f7cc922693e2a7c6c932725a52157943Christine Chen 379a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon private boolean isCallDead(Call call) { 38096b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon final int state = call.getState(); 381a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon return Call.State.IDLE == state || Call.State.INVALID == state; 382a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon } 383a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon 384671b221ccadea34fb9327ef5342b26419eb5ca99Santos Cordon /** 385a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon * Sets up a call for deletion and notifies listeners of change. 386a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon */ 387a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon private void finishDisconnectedCall(Call call) { 388a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon call.setState(Call.State.IDLE); 389a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon updateCallInMap(call); 390a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon notifyListenersOfChange(); 391a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon } 392a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon 393a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon /** 394a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon * Handles the timeout for destroying disconnected calls. 395a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon */ 396a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon private Handler mHandler = new Handler() { 397a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon @Override 398a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon public void handleMessage(Message msg) { 399a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon switch (msg.what) { 400a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon case EVENT_DISCONNECTED_TIMEOUT: 4011a7f2bcab2d2023f2ee4cfb0bc57bc265b5aab87Chiao Cheng Log.d(this, "EVENT_DISCONNECTED_TIMEOUT ", msg.obj); 402a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon finishDisconnectedCall((Call) msg.obj); 403a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon break; 404a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon default: 4051a7f2bcab2d2023f2ee4cfb0bc57bc265b5aab87Chiao Cheng Log.wtf(this, "Message not expected: " + msg.what); 406a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon break; 407a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon } 408a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon } 409a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon }; 410a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon 411a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon /** 412a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon * Listener interface for any class that wants to be notified of changes 413a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon * to the call list. 414a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon */ 415a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon public interface Listener { 416a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon public void onCallListChange(CallList callList); 4178b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng public void onIncomingCall(Call call); 4188b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng } 4198b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng 4208b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng public interface CallUpdateListener { 4218b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng // TODO: refactor and limit arg to be call state. Caller info is not needed. 4228b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng public void onCallStateChanged(Call call); 42396b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon } 42496b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon} 425