Home | History | Annotate | Download | only in incallui
      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