AnswerPresenter.java revision fa0c573d689a16401f1bf801d93b7b37d2380c5c
1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.incallui;
18
19import android.content.Context;
20
21import com.android.dialer.util.TelecomUtil;
22import com.android.incallui.InCallPresenter.InCallState;
23
24import android.telecom.VideoProfile;
25
26import java.util.List;
27
28/**
29 * Presenter for the Incoming call widget. The {@link AnswerPresenter} handles the logic during
30 * incoming calls. It is also in charge of responding to incoming calls, so there needs to be
31 * an instance alive so that it can receive onIncomingCall callbacks.
32 *
33 * An instance of {@link AnswerPresenter} is created by InCallPresenter at startup, registers
34 * for callbacks via InCallPresenter, and shows/hides the {@link AnswerFragment} via IncallActivity.
35 *
36 */
37public class AnswerPresenter extends Presenter<AnswerPresenter.AnswerUi>
38        implements CallList.CallUpdateListener, InCallPresenter.InCallUiListener,
39                InCallPresenter.IncomingCallListener,
40                CallList.Listener {
41
42    private static final String TAG = AnswerPresenter.class.getSimpleName();
43
44    private String mCallId;
45    private Call mCall = null;
46    private boolean mHasTextMessages = false;
47
48    @Override
49    public void onUiShowing(boolean showing) {
50        if (showing) {
51            CallList.getInstance().addListener(this);
52            final CallList calls = CallList.getInstance();
53            Call call;
54            call = calls.getIncomingCall();
55            if (call != null) {
56                processIncomingCall(call);
57            }
58            call = calls.getVideoUpgradeRequestCall();
59            Log.d(this, "getVideoUpgradeRequestCall call =" + call);
60            if (call != null) {
61                processVideoUpgradeRequestCall(call);
62            }
63        } else {
64            CallList.getInstance().removeListener(this);
65            // This is necessary because the activity can be destroyed while an incoming call exists.
66            // This happens when back button is pressed while incoming call is still being shown.
67            if (mCallId != null) {
68                CallList.getInstance().removeCallUpdateListener(mCallId, this);
69            }
70        }
71    }
72
73    @Override
74    public void onIncomingCall(InCallState oldState, InCallState newState, Call call) {
75        Log.d(this, "onIncomingCall: " + this);
76        Call modifyCall = CallList.getInstance().getVideoUpgradeRequestCall();
77        if (modifyCall != null) {
78            showAnswerUi(false);
79            Log.d(this, "declining upgrade request id: ");
80            CallList.getInstance().removeCallUpdateListener(mCallId, this);
81            InCallPresenter.getInstance().declineUpgradeRequest(getUi().getContext());
82        }
83        if (!call.getId().equals(mCallId)) {
84            // A new call is coming in.
85            processIncomingCall(call);
86        }
87    }
88
89    @Override
90    public void onIncomingCall(Call call) {
91    }
92
93    @Override
94    public void onCallListChange(CallList list) {
95    }
96
97    @Override
98    public void onDisconnect(Call call) {
99        // no-op
100    }
101
102    public void onSessionModificationStateChange(int sessionModificationState) {
103        boolean isUpgradePending = sessionModificationState ==
104                Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST;
105
106        if (!isUpgradePending) {
107            // Stop listening for updates.
108            CallList.getInstance().removeCallUpdateListener(mCallId, this);
109            showAnswerUi(false);
110        }
111    }
112
113    @Override
114    public void onLastForwardedNumberChange() {
115        // no-op
116    }
117
118    private boolean isVideoUpgradePending(Call call) {
119        return call.getSessionModificationState()
120                == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST;
121    }
122
123    @Override
124    public void onUpgradeToVideo(Call call) {
125        Log.d(this, "onUpgradeToVideo: " + this + " call=" + call);
126        if (getUi() == null) {
127            Log.d(this, "onUpgradeToVideo ui is null");
128            return;
129        }
130        boolean isUpgradePending = isVideoUpgradePending(call);
131        InCallPresenter inCallPresenter = InCallPresenter.getInstance();
132        if (isUpgradePending
133                && inCallPresenter.getInCallState() == InCallPresenter.InCallState.INCOMING) {
134            Log.d(this, "declining upgrade request");
135            //If there is incoming call reject upgrade request
136            inCallPresenter.declineUpgradeRequest(getUi().getContext());
137        } else if (isUpgradePending) {
138            Log.d(this, "process upgrade request as no MT call");
139            processVideoUpgradeRequestCall(call);
140        }
141    }
142
143    private void processIncomingCall(Call call) {
144        mCallId = call.getId();
145        mCall = call;
146
147        // Listen for call updates for the current call.
148        CallList.getInstance().addCallUpdateListener(mCallId, this);
149
150        Log.d(TAG, "Showing incoming for call id: " + mCallId + " " + this);
151        if (showAnswerUi(true)) {
152            final List<String> textMsgs = CallList.getInstance().getTextResponses(call.getId());
153            configureAnswerTargetsForSms(call, textMsgs);
154        }
155    }
156
157    private boolean showAnswerUi(boolean show) {
158        final InCallActivity activity = InCallPresenter.getInstance().getActivity();
159        if (activity != null) {
160            activity.showAnswerFragment(show);
161            if (getUi() != null) {
162                getUi().onShowAnswerUi(show);
163            }
164            return true;
165        } else {
166            return false;
167        }
168    }
169
170    private void processVideoUpgradeRequestCall(Call call) {
171        Log.d(this, " processVideoUpgradeRequestCall call=" + call);
172        mCallId = call.getId();
173        mCall = call;
174
175        // Listen for call updates for the current call.
176        CallList.getInstance().addCallUpdateListener(mCallId, this);
177
178        final int currentVideoState = call.getVideoState();
179        final int modifyToVideoState = call.getModifyToVideoState();
180
181        if (currentVideoState == modifyToVideoState) {
182            Log.w(this, "processVideoUpgradeRequestCall: Video states are same. Return.");
183            return;
184        }
185
186        AnswerUi ui = getUi();
187
188        if (ui == null) {
189            Log.e(this, "Ui is null. Can't process upgrade request");
190            return;
191        }
192        showAnswerUi(true);
193        ui.showTargets(AnswerFragment.TARGET_SET_FOR_VIDEO_ACCEPT_REJECT_REQUEST,
194                modifyToVideoState);
195    }
196
197    private boolean isEnabled(int videoState, int mask) {
198        return (videoState & mask) == mask;
199    }
200
201    @Override
202    public void onCallChanged(Call call) {
203        Log.d(this, "onCallStateChange() " + call + " " + this);
204        if (call.getState() != Call.State.INCOMING) {
205            boolean isUpgradePending = isVideoUpgradePending(call);
206            if (!isUpgradePending) {
207                // Stop listening for updates.
208                CallList.getInstance().removeCallUpdateListener(mCallId, this);
209            }
210
211            final Call incall = CallList.getInstance().getIncomingCall();
212            if (incall != null || isUpgradePending) {
213                showAnswerUi(true);
214            } else {
215                showAnswerUi(false);
216            }
217
218            mHasTextMessages = false;
219        } else if (!mHasTextMessages) {
220            final List<String> textMsgs = CallList.getInstance().getTextResponses(call.getId());
221            if (textMsgs != null) {
222                configureAnswerTargetsForSms(call, textMsgs);
223            }
224        }
225    }
226
227    public void onAnswer(int videoState, Context context) {
228        if (mCallId == null) {
229            return;
230        }
231
232        if (mCall.getSessionModificationState()
233                == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
234            Log.d(this, "onAnswer (upgradeCall) mCallId=" + mCallId + " videoState=" + videoState);
235            InCallPresenter.getInstance().acceptUpgradeRequest(videoState, context);
236        } else {
237            Log.d(this, "onAnswer (answerCall) mCallId=" + mCallId + " videoState=" + videoState);
238            TelecomAdapter.getInstance().answerCall(mCall.getId(), videoState);
239        }
240    }
241
242    /**
243     * TODO: We are using reject and decline interchangeably. We should settle on
244     * reject since it seems to be more prevalent.
245     */
246    public void onDecline(Context context) {
247        Log.d(this, "onDecline " + mCallId);
248        if (mCall.getSessionModificationState()
249                == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
250            InCallPresenter.getInstance().declineUpgradeRequest(context);
251        } else {
252            TelecomAdapter.getInstance().rejectCall(mCall.getId(), false, null);
253        }
254    }
255
256    public void onText() {
257        if (getUi() != null) {
258            TelecomUtil.silenceRinger(getUi().getContext());
259            getUi().showMessageDialog();
260        }
261    }
262
263    public void rejectCallWithMessage(String message) {
264        Log.d(this, "sendTextToDefaultActivity()...");
265        TelecomAdapter.getInstance().rejectCall(mCall.getId(), true, message);
266
267        onDismissDialog();
268    }
269
270    public void onDismissDialog() {
271        InCallPresenter.getInstance().onDismissDialog();
272    }
273
274    private void configureAnswerTargetsForSms(Call call, List<String> textMsgs) {
275        if (getUi() == null) {
276            return;
277        }
278        mHasTextMessages = textMsgs != null;
279        boolean withSms =
280                call.can(android.telecom.Call.Details.CAPABILITY_RESPOND_VIA_TEXT)
281                && mHasTextMessages;
282
283        // Only present the user with the option to answer as a video call if the incoming call is
284        // a bi-directional video call.
285        if (CallUtils.isBidirectionalVideoCall(call)) {
286            if (withSms) {
287                getUi().showTargets(AnswerFragment.TARGET_SET_FOR_VIDEO_WITH_SMS);
288                getUi().configureMessageDialog(textMsgs);
289            } else {
290                getUi().showTargets(AnswerFragment.TARGET_SET_FOR_VIDEO_WITHOUT_SMS);
291            }
292        } else {
293            if (withSms) {
294                getUi().showTargets(AnswerFragment.TARGET_SET_FOR_AUDIO_WITH_SMS);
295                getUi().configureMessageDialog(textMsgs);
296            } else {
297                getUi().showTargets(AnswerFragment.TARGET_SET_FOR_AUDIO_WITHOUT_SMS);
298            }
299        }
300    }
301
302    interface AnswerUi extends Ui {
303        public void onShowAnswerUi(boolean shown);
304        public void showTargets(int targetSet);
305        public void showTargets(int targetSet, int videoState);
306        public void showMessageDialog();
307        public void configureMessageDialog(List<String> textResponses);
308        public Context getContext();
309    }
310}
311