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