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