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