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 
17 package com.android.incallui;
18 
19 import android.content.Context;
20 
21 import com.android.dialer.compat.UserManagerCompat;
22 import com.android.dialer.util.TelecomUtil;
23 import com.android.incallui.InCallPresenter.InCallState;
24 
25 import 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  */
36 public 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
onUiShowing(boolean showing)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                 showAnswerUi(true);
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
onIncomingCall(InCallState oldState, InCallState newState, Call call)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();
82         }
83         if (!call.getId().equals(mCallId)) {
84             // A new call is coming in.
85             processIncomingCall(call);
86         }
87     }
88 
89     @Override
onIncomingCall(Call call)90     public void onIncomingCall(Call call) {
91     }
92 
93     @Override
onCallListChange(CallList list)94     public void onCallListChange(CallList list) {
95     }
96 
97     @Override
onDisconnect(Call call)98     public void onDisconnect(Call call) {
99         // no-op
100     }
101 
onSessionModificationStateChange(int sessionModificationState)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
onLastForwardedNumberChange()114     public void onLastForwardedNumberChange() {
115         // no-op
116     }
117 
118     @Override
onChildNumberChange()119     public void onChildNumberChange() {
120         // no-op
121     }
122 
isVideoUpgradePending(Call call)123     private boolean isVideoUpgradePending(Call call) {
124         return call.getSessionModificationState()
125                 == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST;
126     }
127 
128     @Override
onUpgradeToVideo(Call call)129     public void onUpgradeToVideo(Call call) {
130         Log.d(this, "onUpgradeToVideo: " + this + " call=" + call);
131         if (getUi() == null) {
132             Log.d(this, "onUpgradeToVideo ui is null");
133             return;
134         }
135         boolean isUpgradePending = isVideoUpgradePending(call);
136         InCallPresenter inCallPresenter = InCallPresenter.getInstance();
137         if (isUpgradePending
138                 && inCallPresenter.getInCallState() == InCallPresenter.InCallState.INCOMING) {
139             Log.d(this, "declining upgrade request");
140             //If there is incoming call reject upgrade request
141             inCallPresenter.declineUpgradeRequest(getUi().getContext());
142         } else if (isUpgradePending) {
143             Log.d(this, "process upgrade request as no MT call");
144             processVideoUpgradeRequestCall(call);
145         }
146     }
147 
processIncomingCall(Call call)148     private void processIncomingCall(Call call) {
149         mCallId = call.getId();
150         mCall = call;
151 
152         // Listen for call updates for the current call.
153         CallList.getInstance().addCallUpdateListener(mCallId, this);
154 
155         Log.d(TAG, "Showing incoming for call id: " + mCallId + " " + this);
156         if (showAnswerUi(true)) {
157             final List<String> textMsgs = CallList.getInstance().getTextResponses(call.getId());
158             configureAnswerTargetsForSms(call, textMsgs);
159         }
160     }
161 
showAnswerUi(boolean show)162     private boolean showAnswerUi(boolean show) {
163         final InCallActivity activity = InCallPresenter.getInstance().getActivity();
164         if (activity != null) {
165             activity.showAnswerFragment(show);
166             if (getUi() != null) {
167                 getUi().onShowAnswerUi(show);
168             }
169             return true;
170         } else {
171             return false;
172         }
173     }
174 
processVideoUpgradeRequestCall(Call call)175     private void processVideoUpgradeRequestCall(Call call) {
176         Log.d(this, " processVideoUpgradeRequestCall call=" + call);
177         mCallId = call.getId();
178         mCall = call;
179 
180         // Listen for call updates for the current call.
181         CallList.getInstance().addCallUpdateListener(mCallId, this);
182 
183         final int currentVideoState = call.getVideoState();
184         final int modifyToVideoState = call.getRequestedVideoState();
185 
186         if (currentVideoState == modifyToVideoState) {
187             Log.w(this, "processVideoUpgradeRequestCall: Video states are same. Return.");
188             return;
189         }
190 
191         AnswerUi ui = getUi();
192 
193         if (ui == null) {
194             Log.e(this, "Ui is null. Can't process upgrade request");
195             return;
196         }
197         showAnswerUi(true);
198         ui.showTargets(AnswerFragment.TARGET_SET_FOR_VIDEO_ACCEPT_REJECT_REQUEST,
199                 modifyToVideoState);
200     }
201 
isEnabled(int videoState, int mask)202     private boolean isEnabled(int videoState, int mask) {
203         return (videoState & mask) == mask;
204     }
205 
206     @Override
onCallChanged(Call call)207     public void onCallChanged(Call call) {
208         Log.d(this, "onCallStateChange() " + call + " " + this);
209         if (call.getState() != Call.State.INCOMING) {
210             boolean isUpgradePending = isVideoUpgradePending(call);
211             if (!isUpgradePending) {
212                 // Stop listening for updates.
213                 CallList.getInstance().removeCallUpdateListener(mCallId, this);
214             }
215 
216             final Call incall = CallList.getInstance().getIncomingCall();
217             if (incall != null || isUpgradePending) {
218                 showAnswerUi(true);
219             } else {
220                 showAnswerUi(false);
221             }
222 
223             mHasTextMessages = false;
224         } else if (!mHasTextMessages) {
225             final List<String> textMsgs = CallList.getInstance().getTextResponses(call.getId());
226             if (textMsgs != null) {
227                 configureAnswerTargetsForSms(call, textMsgs);
228             }
229         }
230     }
231 
onAnswer(int videoState, Context context)232     public void onAnswer(int videoState, Context context) {
233         if (mCallId == null) {
234             return;
235         }
236 
237         if (mCall.getSessionModificationState()
238                 == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
239             Log.d(this, "onAnswer (upgradeCall) mCallId=" + mCallId + " videoState=" + videoState);
240             InCallPresenter.getInstance().acceptUpgradeRequest(videoState, context);
241         } else {
242             Log.d(this, "onAnswer (answerCall) mCallId=" + mCallId + " videoState=" + videoState);
243             TelecomAdapter.getInstance().answerCall(mCall.getId(), videoState);
244         }
245     }
246 
247     /**
248      * TODO: We are using reject and decline interchangeably. We should settle on
249      * reject since it seems to be more prevalent.
250      */
onDecline(Context context)251     public void onDecline(Context context) {
252         Log.d(this, "onDecline " + mCallId);
253         if (mCall.getSessionModificationState()
254                 == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
255             InCallPresenter.getInstance().declineUpgradeRequest(context);
256         } else {
257             TelecomAdapter.getInstance().rejectCall(mCall.getId(), false, null);
258         }
259     }
260 
onText()261     public void onText() {
262         if (getUi() != null) {
263             TelecomUtil.silenceRinger(getUi().getContext());
264             getUi().showMessageDialog();
265         }
266     }
267 
rejectCallWithMessage(String message)268     public void rejectCallWithMessage(String message) {
269         Log.d(this, "sendTextToDefaultActivity()...");
270         TelecomAdapter.getInstance().rejectCall(mCall.getId(), true, message);
271 
272         onDismissDialog();
273     }
274 
onDismissDialog()275     public void onDismissDialog() {
276         InCallPresenter.getInstance().onDismissDialog();
277     }
278 
configureAnswerTargetsForSms(Call call, List<String> textMsgs)279     private void configureAnswerTargetsForSms(Call call, List<String> textMsgs) {
280         if (getUi() == null) {
281             return;
282         }
283         mHasTextMessages = textMsgs != null;
284         boolean withSms = UserManagerCompat.isUserUnlocked(getUi().getContext())
285                 && call.can(android.telecom.Call.Details.CAPABILITY_RESPOND_VIA_TEXT)
286                 && mHasTextMessages;
287 
288         // Only present the user with the option to answer as a video call if the incoming call is
289         // a bi-directional video call.
290         if (VideoUtils.isBidirectionalVideoCall(call)) {
291             if (withSms) {
292                 getUi().showTargets(AnswerFragment.TARGET_SET_FOR_VIDEO_WITH_SMS);
293                 getUi().configureMessageDialog(textMsgs);
294             } else {
295                 getUi().showTargets(AnswerFragment.TARGET_SET_FOR_VIDEO_WITHOUT_SMS);
296             }
297         } else {
298             if (withSms) {
299                 getUi().showTargets(AnswerFragment.TARGET_SET_FOR_AUDIO_WITH_SMS);
300                 getUi().configureMessageDialog(textMsgs);
301             } else {
302                 getUi().showTargets(AnswerFragment.TARGET_SET_FOR_AUDIO_WITHOUT_SMS);
303             }
304         }
305     }
306 
307     interface AnswerUi extends Ui {
onShowAnswerUi(boolean shown)308         public void onShowAnswerUi(boolean shown);
showTargets(int targetSet)309         public void showTargets(int targetSet);
showTargets(int targetSet, int videoState)310         public void showTargets(int targetSet, int videoState);
showMessageDialog()311         public void showMessageDialog();
configureMessageDialog(List<String> textResponses)312         public void configureMessageDialog(List<String> textResponses);
getContext()313         public Context getContext();
314     }
315 }
316