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.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