CallList.java revision 8b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0c
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
41930ea81045240d6b690663f94c4c86ea045229d1Santos Cordon    private static final int DISCONNECTED_CALL_TIMEOUT_MS = 2000;
42a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon
43a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon    private static final int EVENT_DISCONNECTED_TIMEOUT = 1;
44a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon
458b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng    private static CallList sInstance = new CallList();
46a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon
47a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon    private final HashMap<Integer, Call> mCallMap = Maps.newHashMap();
4810196fd2f7cc922693e2a7c6c932725a52157943Christine Chen    private final HashMap<Integer, ArrayList<String>> mCallTextReponsesMap =
4910196fd2f7cc922693e2a7c6c932725a52157943Christine Chen            Maps.newHashMap();
50a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon    private final Set<Listener> mListeners = Sets.newArraySet();
518b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng    private final HashMap<Integer, List<CallUpdateListener>> mCallUpdateListenerMap = Maps
528b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng            .newHashMap();
538b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng
54a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon
55a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon    /**
56a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon     * Static singleton accessor method.
57a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon     */
588b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng    public static CallList getInstance() {
59a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon        return sInstance;
608b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng    }
6196b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon
62a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon    /**
63a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon     * Private constructor.  Instance should only be acquired through getInstance().
64a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon     */
658b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng    private CallList() {
66a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon    }
6796b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon
68a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon    /**
69a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon     * Called when a single call has changed.
70a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon     */
7196b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon    public void onUpdate(Call call) {
721a7f2bcab2d2023f2ee4cfb0bc57bc265b5aab87Chiao Cheng        Log.d(this, "onUpdate - ", call);
73a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon
74a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon        updateCallInMap(call);
75a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon        notifyListenersOfChange();
76a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon    }
77a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon
78a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon    /**
79a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon     * Called when a single call disconnects.
80a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon     */
81a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon    public void onDisconnect(Call call) {
821a7f2bcab2d2023f2ee4cfb0bc57bc265b5aab87Chiao Cheng        Log.d(this, "onDisconnect: ", call);
83a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon
84671b221ccadea34fb9327ef5342b26419eb5ca99Santos Cordon        updateCallInMap(call);
8510196fd2f7cc922693e2a7c6c932725a52157943Christine Chen
8610196fd2f7cc922693e2a7c6c932725a52157943Christine Chen        notifyListenersOfChange();
8710196fd2f7cc922693e2a7c6c932725a52157943Christine Chen    }
8810196fd2f7cc922693e2a7c6c932725a52157943Christine Chen
8910196fd2f7cc922693e2a7c6c932725a52157943Christine Chen    /**
9010196fd2f7cc922693e2a7c6c932725a52157943Christine Chen     * Called when a single call has changed.
9110196fd2f7cc922693e2a7c6c932725a52157943Christine Chen     */
928b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng    public void onIncoming(Call call, List<String> textMessages) {
938b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng        Log.d(this, "onIncoming - " + call);
9410196fd2f7cc922693e2a7c6c932725a52157943Christine Chen
958b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng        updateCallInMap(call);
968b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng        updateCallTextMap(call, textMessages);
9710196fd2f7cc922693e2a7c6c932725a52157943Christine Chen
988b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng        for (Listener listener : mListeners) {
998b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng            listener.onIncomingCall(call);
1008b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng        }
10196b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon    }
10296b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon
103a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon    /**
104a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon     * Called when multiple calls have changed.
105a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon     */
10696b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon    public void onUpdate(List<Call> callsToUpdate) {
1071a7f2bcab2d2023f2ee4cfb0bc57bc265b5aab87Chiao Cheng        Log.d(this, "onUpdate(...)");
108671b221ccadea34fb9327ef5342b26419eb5ca99Santos Cordon
10996b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon        Preconditions.checkNotNull(callsToUpdate);
11096b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon        for (Call call : callsToUpdate) {
1111a7f2bcab2d2023f2ee4cfb0bc57bc265b5aab87Chiao Cheng            Log.d(this, "\t" + call);
112671b221ccadea34fb9327ef5342b26419eb5ca99Santos Cordon
11396b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon            updateCallInMap(call);
11410196fd2f7cc922693e2a7c6c932725a52157943Christine Chen            updateCallTextMap(call, null);
1158b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng
1168b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng            notifyCallUpdateListeners(call);
11796b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon        }
118a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon
119a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon        notifyListenersOfChange();
120a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon    }
121a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon
1228b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng    public void notifyCallUpdateListeners(Call call) {
1238b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng        final List<CallUpdateListener> listeners = mCallUpdateListenerMap.get(call.getCallId());
1248b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng        if (listeners != null) {
1258b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng            for (CallUpdateListener listener : listeners) {
1268b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng                listener.onCallStateChanged(call);
1278b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng            }
1288b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng        }
1298b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng    }
1308b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng
1318b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng    /**
1328b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng     * Add a call update listener for a call id.
1338b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng     *
1348b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng     * @param callId The call id to get updates for.
1358b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng     * @param listener The listener to add.
1368b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng     */
1378b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng    public void addCallUpdateListener(int callId, CallUpdateListener listener) {
1388b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng        List<CallUpdateListener> listeners = mCallUpdateListenerMap.get(callId);
1398b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng        if (listeners == null) {
1408b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng            listeners = Lists.newArrayList();
1418b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng            mCallUpdateListenerMap.put(callId, listeners);
1428b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng        }
1438b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng        listeners.add(listener);
1448b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng    }
1458b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng
1468b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng    /**
1478b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng     * Remove a call update listener for a call id.
1488b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng     *
1498b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng     * @param callId The call id to remove the listener for.
1508b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng     * @param listener The listener to remove.
1518b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng     */
1528b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng    public void removeCallUpdateListener(int callId, CallUpdateListener listener) {
1538b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng        List<CallUpdateListener> listeners = mCallUpdateListenerMap.get(callId);
1548b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng        if (listeners != null) {
1558b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng            listeners.remove(listener);
1568b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng        }
1578b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng    }
1588b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng
159a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon    public void addListener(Listener listener) {
160a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon        Preconditions.checkNotNull(listener);
161150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon
162a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon        mListeners.add(listener);
163150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon
164150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon        // Let the listener know about the active calls immediately.
165150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon        listener.onCallListChange(this);
166a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon    }
167a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon
168a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon    public void removeListener(Listener listener) {
169a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon        Preconditions.checkNotNull(listener);
170a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon        mListeners.remove(listener);
171a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon    }
172a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon
173a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon    /**
174e7be13fb0556e62b07bc271b130412d82d7f7521Santos Cordon     * TODO: Change so that this function is not needed. Instead of assuming there is an active
175a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon     * call, the code should rely on the status of a specific Call and allow the presenters to
176a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon     * update the Call object when the active call changes.
177a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon     */
178a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon    public Call getIncomingOrActive() {
179150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon        Call retval = getIncomingCall();
180150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon        if (retval == null) {
181671b221ccadea34fb9327ef5342b26419eb5ca99Santos Cordon            retval = getActiveCall();
182150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon        }
183150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon        return retval;
184150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon    }
185150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon
186e7be13fb0556e62b07bc271b130412d82d7f7521Santos Cordon    public Call getOutgoingCall() {
187e7be13fb0556e62b07bc271b130412d82d7f7521Santos Cordon        return getFirstCallWithState(Call.State.DIALING);
188e7be13fb0556e62b07bc271b130412d82d7f7521Santos Cordon    }
189e7be13fb0556e62b07bc271b130412d82d7f7521Santos Cordon
190671b221ccadea34fb9327ef5342b26419eb5ca99Santos Cordon    public Call getActiveCall() {
191671b221ccadea34fb9327ef5342b26419eb5ca99Santos Cordon        return getFirstCallWithState(Call.State.ACTIVE);
192671b221ccadea34fb9327ef5342b26419eb5ca99Santos Cordon    }
193671b221ccadea34fb9327ef5342b26419eb5ca99Santos Cordon
194150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon    public Call getBackgroundCall() {
195150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon        return getFirstCallWithState(Call.State.ONHOLD);
196150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon    }
197150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon
198a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon    public Call getDisconnectedCall() {
199a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon        return getFirstCallWithState(Call.State.DISCONNECTED);
200a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon    }
201a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon
202efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon    public Call getSecondBackgroundCall() {
203efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon        return getCallWithState(Call.State.ONHOLD, 1);
204efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon    }
205efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon
206efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon    public Call getActiveOrBackgroundCall() {
207efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon        Call call = getActiveCall();
208efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon        if (call == null) {
209efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon            call = getBackgroundCall();
210efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon        }
211efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon        return call;
212efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon    }
213efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon
214150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon    public Call getIncomingCall() {
215efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon        Call call = getFirstCallWithState(Call.State.INCOMING);
216efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon        if (call == null) {
217efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon            call = getFirstCallWithState(Call.State.CALL_WAITING);
218efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon        }
219efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon
220efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon        return call;
221150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon    }
222a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon
2238b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng
2248b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng    public Call getFirstCall() {
2258b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng        // TODO: should we switch to a simple list and pull the first one?
2268b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng        Call result = getIncomingCall();
2278b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng        if (result == null) {
2288b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng            result = getFirstCallWithState(Call.State.DIALING);
2298b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng        }
2308b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng        if (result == null) {
2318b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng            result = getFirstCallWithState(Call.State.ACTIVE);
2328b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng        }
2338b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng        return result;
2348b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng    }
2358b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng
236150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon    public boolean existsLiveCall() {
237a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon        for (Call call : mCallMap.values()) {
238150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon            if (!isCallDead(call)) {
239150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon                return true;
240150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon            }
241150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon        }
242150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon        return false;
243150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon    }
244150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon
2458b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng    public ArrayList<String> getTextResponses(int callId) {
2468b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng        return mCallTextReponsesMap.get(callId);
24710196fd2f7cc922693e2a7c6c932725a52157943Christine Chen    }
24810196fd2f7cc922693e2a7c6c932725a52157943Christine Chen
249150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon    /**
250150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon     * Returns first call found in the call map with the specified state.
251150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon     */
252150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon    public Call getFirstCallWithState(int state) {
253efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon        return getCallWithState(state, 0);
254efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon    }
255efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon
256efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon    /**
257efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon     * Returns the [position]th call found in the call map with the specified state.
258efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon     * TODO(klp): Improve this logic to sort by call time.
259efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon     */
260efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon    public Call getCallWithState(int state, int positionToFind) {
261150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon        Call retval = null;
262efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon        int position = 0;
263150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon        for (Call call : mCallMap.values()) {
264150a5c58c67f230c8fd7293b180bbf50aa761480Santos Cordon            if (call.getState() == state) {
265efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon                if (position >= positionToFind) {
266efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon                    retval = call;
267efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon                    break;
268efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon                } else {
269efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon                    position++;
270efd4282ec4221ec5eefd4155a4ad915adcedca70Santos Cordon                }
271a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon            }
272a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon        }
273a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon
274a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon        return retval;
275a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon    }
276a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon
277a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon    /**
2783c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon     * This is called when the service disconnects, either expectedly or unexpectedly.
2793c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon     * For the expected case, it's because we have no calls left.  For the unexpected case,
2803c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon     * it is likely a crash of phone and we need to clean up our calls manually.  Without phone,
2813c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon     * there can be no active calls, so this is relatively safe thing to do.
2823c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon     */
2833c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon    public void clearOnDisconnect() {
2843c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon        for (Call call : mCallMap.values()) {
2853c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon            final int state = call.getState();
2863c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon            if (state != Call.State.IDLE &&
2873c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon                    state != Call.State.INVALID &&
2883c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon                    state != Call.State.DISCONNECTED) {
2893c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon                call.setState(Call.State.DISCONNECTED);
2903c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon                updateCallInMap(call);
2913c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon            }
2923c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon        }
2933c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon        notifyListenersOfChange();
2943c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon    }
2953c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon
2963c5581a67ba89fd96fc19e64f6c4780d0a58f640Santos Cordon    /**
297a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon     * Sends a generic notification to all listeners that something has changed.
298a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon     * It is up to the listeners to call back to determine what changed.
299a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon     */
300a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon    private void notifyListenersOfChange() {
301a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon        for (Listener listener : mListeners) {
302a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon            listener.onCallListChange(this);
303a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon        }
30496b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon    }
30596b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon
30696b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon    private void updateCallInMap(Call call) {
30796b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon        Preconditions.checkNotNull(call);
30896b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon
30996b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon        final Integer id = new Integer(call.getCallId());
31096b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon
311a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon        if (call.getState() == Call.State.DISCONNECTED) {
312a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon
3131df52df7a0248814fbd4575103059a8b427f5d9aSantos Cordon            // update existing (but do not add!!) disconnected calls
3141df52df7a0248814fbd4575103059a8b427f5d9aSantos Cordon            if (mCallMap.containsKey(id)) {
315a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon
3161df52df7a0248814fbd4575103059a8b427f5d9aSantos Cordon                // For disconnected calls, we want to keep them alive for a few seconds so that the
3171df52df7a0248814fbd4575103059a8b427f5d9aSantos Cordon                // UI has a chance to display anything it needs when a call is disconnected.
3181df52df7a0248814fbd4575103059a8b427f5d9aSantos Cordon
3191df52df7a0248814fbd4575103059a8b427f5d9aSantos Cordon                // Set up a timer to destroy the call after X seconds.
3201df52df7a0248814fbd4575103059a8b427f5d9aSantos Cordon                final Message msg = mHandler.obtainMessage(EVENT_DISCONNECTED_TIMEOUT, call);
3211df52df7a0248814fbd4575103059a8b427f5d9aSantos Cordon                mHandler.sendMessageDelayed(msg, DISCONNECTED_CALL_TIMEOUT_MS);
322a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon
323a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon                mCallMap.put(id, call);
324a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon            }
325a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon        } else if (!isCallDead(call)) {
326a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon            mCallMap.put(id, call);
327a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon        } else if (mCallMap.containsKey(id)) {
328a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon            mCallMap.remove(id);
32996b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon        }
33096b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon    }
33196b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon
33210196fd2f7cc922693e2a7c6c932725a52157943Christine Chen    private void updateCallTextMap(Call call, List<String> textResponses) {
33310196fd2f7cc922693e2a7c6c932725a52157943Christine Chen        Preconditions.checkNotNull(call);
33410196fd2f7cc922693e2a7c6c932725a52157943Christine Chen
33510196fd2f7cc922693e2a7c6c932725a52157943Christine Chen        final Integer id = new Integer(call.getCallId());
33610196fd2f7cc922693e2a7c6c932725a52157943Christine Chen
33710196fd2f7cc922693e2a7c6c932725a52157943Christine Chen        if (!isCallDead(call)) {
33810196fd2f7cc922693e2a7c6c932725a52157943Christine Chen            if (textResponses != null) {
33910196fd2f7cc922693e2a7c6c932725a52157943Christine Chen                mCallTextReponsesMap.put(id, (ArrayList<String>) textResponses);
34010196fd2f7cc922693e2a7c6c932725a52157943Christine Chen            }
34110196fd2f7cc922693e2a7c6c932725a52157943Christine Chen        } else if (mCallMap.containsKey(id)) {
34210196fd2f7cc922693e2a7c6c932725a52157943Christine Chen            mCallTextReponsesMap.remove(id);
34310196fd2f7cc922693e2a7c6c932725a52157943Christine Chen        }
34410196fd2f7cc922693e2a7c6c932725a52157943Christine Chen    }
34510196fd2f7cc922693e2a7c6c932725a52157943Christine Chen
346a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon    private boolean isCallDead(Call call) {
34796b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon        final int state = call.getState();
348a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon        return Call.State.IDLE == state || Call.State.INVALID == state;
349a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon    }
350a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon
351671b221ccadea34fb9327ef5342b26419eb5ca99Santos Cordon    /**
352a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon     * Sets up a call for deletion and notifies listeners of change.
353a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon     */
354a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon    private void finishDisconnectedCall(Call call) {
355a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon        call.setState(Call.State.IDLE);
356a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon        updateCallInMap(call);
357a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon        notifyListenersOfChange();
358a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon    }
359a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon
360a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon    /**
361a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon     * Handles the timeout for destroying disconnected calls.
362a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon     */
363a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon    private Handler mHandler = new Handler() {
364a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon        @Override
365a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon        public void handleMessage(Message msg) {
366a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon            switch (msg.what) {
367a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon                case EVENT_DISCONNECTED_TIMEOUT:
3681a7f2bcab2d2023f2ee4cfb0bc57bc265b5aab87Chiao Cheng                    Log.d(this, "EVENT_DISCONNECTED_TIMEOUT ", msg.obj);
369a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon                    finishDisconnectedCall((Call) msg.obj);
370a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon                    break;
371a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon                default:
3721a7f2bcab2d2023f2ee4cfb0bc57bc265b5aab87Chiao Cheng                    Log.wtf(this, "Message not expected: " + msg.what);
373a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon                    break;
374a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon            }
375a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon        }
376a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon    };
377a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon
378a12f2589f424e26c63c8ee5b6fad89e30b69d7fbSantos Cordon    /**
379a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon     * Listener interface for any class that wants to be notified of changes
380a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon     * to the call list.
381a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon     */
382a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon    public interface Listener {
383a012c22dfbfedffe2e751f9b1ab776baa325a26fSantos Cordon        public void onCallListChange(CallList callList);
3848b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng        public void onIncomingCall(Call call);
3858b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng    }
3868b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng
3878b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng    public interface CallUpdateListener {
3888b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng        // TODO: refactor and limit arg to be call state.  Caller info is not needed.
3898b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng        public void onCallStateChanged(Call call);
39096b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon    }
39196b8ba57ac2399336478ffa7a2ccc9423bda0a52Santos Cordon}
392