/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License */ package com.android.incallui; import android.content.Context; import com.android.dialer.compat.UserManagerCompat; import com.android.dialer.util.TelecomUtil; import com.android.incallui.InCallPresenter.InCallState; import java.util.List; /** * Presenter for the Incoming call widget. The {@link AnswerPresenter} handles the logic during * incoming calls. It is also in charge of responding to incoming calls, so there needs to be * an instance alive so that it can receive onIncomingCall callbacks. * * An instance of {@link AnswerPresenter} is created by InCallPresenter at startup, registers * for callbacks via InCallPresenter, and shows/hides the {@link AnswerFragment} via IncallActivity. * */ public class AnswerPresenter extends Presenter implements CallList.CallUpdateListener, InCallPresenter.InCallUiListener, InCallPresenter.IncomingCallListener, CallList.Listener { private static final String TAG = AnswerPresenter.class.getSimpleName(); private String mCallId; private Call mCall = null; private boolean mHasTextMessages = false; @Override public void onUiShowing(boolean showing) { if (showing) { CallList.getInstance().addListener(this); final CallList calls = CallList.getInstance(); Call call; call = calls.getIncomingCall(); if (call != null) { processIncomingCall(call); } call = calls.getVideoUpgradeRequestCall(); Log.d(this, "getVideoUpgradeRequestCall call =" + call); if (call != null) { showAnswerUi(true); processVideoUpgradeRequestCall(call); } } else { CallList.getInstance().removeListener(this); // This is necessary because the activity can be destroyed while an incoming call exists. // This happens when back button is pressed while incoming call is still being shown. if (mCallId != null) { CallList.getInstance().removeCallUpdateListener(mCallId, this); } } } @Override public void onIncomingCall(InCallState oldState, InCallState newState, Call call) { Log.d(this, "onIncomingCall: " + this); Call modifyCall = CallList.getInstance().getVideoUpgradeRequestCall(); if (modifyCall != null) { showAnswerUi(false); Log.d(this, "declining upgrade request id: "); CallList.getInstance().removeCallUpdateListener(mCallId, this); InCallPresenter.getInstance().declineUpgradeRequest(); } if (!call.getId().equals(mCallId)) { // A new call is coming in. processIncomingCall(call); } } @Override public void onIncomingCall(Call call) { } @Override public void onCallListChange(CallList list) { } @Override public void onDisconnect(Call call) { // no-op } public void onSessionModificationStateChange(int sessionModificationState) { boolean isUpgradePending = sessionModificationState == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST; if (!isUpgradePending) { // Stop listening for updates. CallList.getInstance().removeCallUpdateListener(mCallId, this); showAnswerUi(false); } } @Override public void onLastForwardedNumberChange() { // no-op } @Override public void onChildNumberChange() { // no-op } private boolean isVideoUpgradePending(Call call) { return call.getSessionModificationState() == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST; } @Override public void onUpgradeToVideo(Call call) { Log.d(this, "onUpgradeToVideo: " + this + " call=" + call); showAnswerUi(true); boolean isUpgradePending = isVideoUpgradePending(call); InCallPresenter inCallPresenter = InCallPresenter.getInstance(); if (isUpgradePending && inCallPresenter.getInCallState() == InCallPresenter.InCallState.INCOMING) { Log.d(this, "declining upgrade request"); //If there is incoming call reject upgrade request inCallPresenter.declineUpgradeRequest(getUi().getContext()); } else if (isUpgradePending) { Log.d(this, "process upgrade request as no MT call"); processVideoUpgradeRequestCall(call); } } private void processIncomingCall(Call call) { mCallId = call.getId(); mCall = call; // Listen for call updates for the current call. CallList.getInstance().addCallUpdateListener(mCallId, this); Log.d(TAG, "Showing incoming for call id: " + mCallId + " " + this); if (showAnswerUi(true)) { final List textMsgs = CallList.getInstance().getTextResponses(call.getId()); configureAnswerTargetsForSms(call, textMsgs); } } private boolean showAnswerUi(boolean show) { final InCallActivity activity = InCallPresenter.getInstance().getActivity(); if (activity != null) { activity.showAnswerFragment(show); if (getUi() != null) { getUi().onShowAnswerUi(show); } return true; } else { return false; } } private void processVideoUpgradeRequestCall(Call call) { Log.d(this, " processVideoUpgradeRequestCall call=" + call); mCallId = call.getId(); mCall = call; // Listen for call updates for the current call. CallList.getInstance().addCallUpdateListener(mCallId, this); final int currentVideoState = call.getVideoState(); final int modifyToVideoState = call.getRequestedVideoState(); if (currentVideoState == modifyToVideoState) { Log.w(this, "processVideoUpgradeRequestCall: Video states are same. Return."); return; } AnswerUi ui = getUi(); if (ui == null) { Log.e(this, "Ui is null. Can't process upgrade request"); return; } showAnswerUi(true); ui.showTargets(AnswerFragment.TARGET_SET_FOR_VIDEO_ACCEPT_REJECT_REQUEST, modifyToVideoState); } private boolean isEnabled(int videoState, int mask) { return (videoState & mask) == mask; } @Override public void onCallChanged(Call call) { Log.d(this, "onCallStateChange() " + call + " " + this); if (call.getState() != Call.State.INCOMING) { boolean isUpgradePending = isVideoUpgradePending(call); if (!isUpgradePending) { // Stop listening for updates. CallList.getInstance().removeCallUpdateListener(mCallId, this); } final Call incall = CallList.getInstance().getIncomingCall(); if (incall != null || isUpgradePending) { showAnswerUi(true); } else { showAnswerUi(false); } mHasTextMessages = false; } else if (!mHasTextMessages) { final List textMsgs = CallList.getInstance().getTextResponses(call.getId()); if (textMsgs != null) { configureAnswerTargetsForSms(call, textMsgs); } } } public void onAnswer(int videoState, Context context) { if (mCallId == null) { return; } if (mCall.getSessionModificationState() == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) { Log.d(this, "onAnswer (upgradeCall) mCallId=" + mCallId + " videoState=" + videoState); InCallPresenter.getInstance().acceptUpgradeRequest(videoState, context); } else { Log.d(this, "onAnswer (answerCall) mCallId=" + mCallId + " videoState=" + videoState); TelecomAdapter.getInstance().answerCall(mCall.getId(), videoState); } } /** * TODO: We are using reject and decline interchangeably. We should settle on * reject since it seems to be more prevalent. */ public void onDecline(Context context) { Log.d(this, "onDecline " + mCallId); if (mCall.getSessionModificationState() == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) { InCallPresenter.getInstance().declineUpgradeRequest(context); } else { TelecomAdapter.getInstance().rejectCall(mCall.getId(), false, null); } } public void onText() { if (getUi() != null) { TelecomUtil.silenceRinger(getUi().getContext()); getUi().showMessageDialog(); } } public void rejectCallWithMessage(String message) { Log.d(this, "sendTextToDefaultActivity()..."); TelecomAdapter.getInstance().rejectCall(mCall.getId(), true, message); onDismissDialog(); } public void onDismissDialog() { InCallPresenter.getInstance().onDismissDialog(); } private void configureAnswerTargetsForSms(Call call, List textMsgs) { if (getUi() == null) { return; } mHasTextMessages = textMsgs != null; boolean withSms = UserManagerCompat.isUserUnlocked(getUi().getContext()) && call.can(android.telecom.Call.Details.CAPABILITY_RESPOND_VIA_TEXT) && mHasTextMessages; // Only present the user with the option to answer as a video call if the incoming call is // a bi-directional video call. if (VideoUtils.isBidirectionalVideoCall(call)) { if (withSms) { getUi().showTargets(AnswerFragment.TARGET_SET_FOR_VIDEO_WITH_SMS); getUi().configureMessageDialog(textMsgs); } else { getUi().showTargets(AnswerFragment.TARGET_SET_FOR_VIDEO_WITHOUT_SMS); } } else { if (withSms) { getUi().showTargets(AnswerFragment.TARGET_SET_FOR_AUDIO_WITH_SMS); getUi().configureMessageDialog(textMsgs); } else { getUi().showTargets(AnswerFragment.TARGET_SET_FOR_AUDIO_WITHOUT_SMS); } } } interface AnswerUi extends Ui { public void onShowAnswerUi(boolean shown); public void showTargets(int targetSet); public void showTargets(int targetSet, int videoState); public void showMessageDialog(); public void configureMessageDialog(List textResponses); public Context getContext(); } }