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