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