Home | History | Annotate | Download | only in impl
      1 /*
      2  * Copyright (C) 2017 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.dialer.simulator.impl;
     18 
     19 import android.content.Context;
     20 import android.os.Bundle;
     21 import android.support.annotation.NonNull;
     22 import android.support.annotation.Nullable;
     23 import android.support.v7.app.AppCompatActivity;
     24 import android.telecom.Connection;
     25 import android.telecom.Connection.RttModifyStatus;
     26 import android.telecom.DisconnectCause;
     27 import android.view.ActionProvider;
     28 import com.android.dialer.common.Assert;
     29 import com.android.dialer.common.LogUtil;
     30 import com.android.dialer.common.concurrent.DialerExecutorComponent;
     31 import com.android.dialer.common.concurrent.ThreadUtil;
     32 import com.android.dialer.enrichedcall.EnrichedCallComponent;
     33 import com.android.dialer.enrichedcall.EnrichedCallManager;
     34 import com.android.dialer.simulator.Simulator;
     35 import com.android.dialer.simulator.Simulator.Event;
     36 import com.android.dialer.simulator.SimulatorComponent;
     37 import com.android.dialer.simulator.SimulatorEnrichedCall;
     38 
     39 /** Entry point in the simulator to create voice calls. */
     40 final class SimulatorVoiceCall
     41     implements SimulatorConnectionService.Listener, SimulatorConnection.Listener {
     42   @NonNull private final Context context;
     43   @Nullable private String connectionTag;
     44   private final SimulatorEnrichedCall simulatorEnrichedCall;
     45 
     46   static ActionProvider getActionProvider(@NonNull AppCompatActivity activity) {
     47     return new SimulatorSubMenu(activity.getApplicationContext())
     48         .addItem(
     49             "Incoming call",
     50             () -> new SimulatorVoiceCall(activity.getApplicationContext()).addNewIncomingCall())
     51         .addItem(
     52             "Outgoing call",
     53             () -> new SimulatorVoiceCall(activity.getApplicationContext()).addNewOutgoingCall())
     54         .addItem(
     55             "Customized incoming call",
     56             () ->
     57                 new SimulatorVoiceCall(activity.getApplicationContext())
     58                     .addNewIncomingCall(activity))
     59         .addItem(
     60             "Customized outgoing call",
     61             () ->
     62                 new SimulatorVoiceCall(activity.getApplicationContext())
     63                     .addNewOutgoingCall(activity))
     64         .addItem(
     65             "Incoming enriched call",
     66             () -> new SimulatorVoiceCall(activity.getApplicationContext()).incomingEnrichedCall())
     67         .addItem(
     68             "Outgoing enriched call",
     69             () -> new SimulatorVoiceCall(activity.getApplicationContext()).outgoingEnrichedCall())
     70         .addItem(
     71             "Spam incoming call",
     72             () -> new SimulatorVoiceCall(activity.getApplicationContext()).addSpamIncomingCall())
     73         .addItem(
     74             "Emergency call back",
     75             () ->
     76                 new SimulatorVoiceCall(activity.getApplicationContext()).addNewEmergencyCallBack())
     77         .addItem(
     78             "GSM conference",
     79             () ->
     80                 new SimulatorConferenceCreator(
     81                         activity.getApplicationContext(), Simulator.CONFERENCE_TYPE_GSM)
     82                     .start(5))
     83         .addItem(
     84             "VoLTE conference",
     85             () ->
     86                 new SimulatorConferenceCreator(
     87                         activity.getApplicationContext(), Simulator.CONFERENCE_TYPE_VOLTE)
     88                     .start(5));
     89   }
     90 
     91   private SimulatorVoiceCall(@NonNull Context context) {
     92     this.context = Assert.isNotNull(context);
     93     simulatorEnrichedCall = SimulatorComponent.get(context).getSimulatorEnrichedCall();
     94     SimulatorConnectionService.addListener(this);
     95     SimulatorConnectionService.addListener(
     96         new SimulatorConferenceCreator(context, Simulator.CONFERENCE_TYPE_GSM));
     97   }
     98 
     99   private void incomingEnrichedCall() {
    100     simulatorEnrichedCall
    101         .setupIncomingEnrichedCall(Simulator.ENRICHED_CALL_INCOMING_NUMBER)
    102         .addListener(
    103             () -> {
    104               Bundle extras = new Bundle();
    105               extras.putBoolean(Simulator.IS_ENRICHED_CALL, true);
    106               connectionTag =
    107                   SimulatorSimCallManager.addNewIncomingCall(
    108                       context,
    109                       Simulator.ENRICHED_CALL_INCOMING_NUMBER,
    110                       SimulatorSimCallManager.CALL_TYPE_VOICE,
    111                       extras);
    112             },
    113             DialerExecutorComponent.get(context).uiExecutor());
    114   }
    115 
    116   private void outgoingEnrichedCall() {
    117     getEnrichedCallManager().registerStateChangedListener(simulatorEnrichedCall);
    118     simulatorEnrichedCall
    119         .setupOutgoingEnrichedCall(Simulator.ENRICHED_CALL_OUTGOING_NUMBER)
    120         .addListener(
    121             () -> {
    122               Bundle extras = new Bundle();
    123               extras.putBoolean(Simulator.IS_ENRICHED_CALL, true);
    124               connectionTag =
    125                   SimulatorSimCallManager.addNewOutgoingCall(
    126                       context,
    127                       Simulator.ENRICHED_CALL_OUTGOING_NUMBER,
    128                       SimulatorSimCallManager.CALL_TYPE_VOICE,
    129                       extras);
    130             },
    131             DialerExecutorComponent.get(context).uiExecutor());
    132   }
    133 
    134   private void addNewIncomingCall() {
    135     String callerId = "+44 (0) 20 7031 3000" /* Google London office */;
    136     connectionTag =
    137         SimulatorSimCallManager.addNewIncomingCall(
    138             context, callerId, SimulatorSimCallManager.CALL_TYPE_VOICE);
    139   }
    140 
    141   private void addNewIncomingCall(AppCompatActivity activity) {
    142     SimulatorDialogFragment.newInstance(
    143             (callerId, callerIdPresentation) -> {
    144               Bundle extras = new Bundle();
    145               extras.putInt(Simulator.PRESENTATION_CHOICE, callerIdPresentation);
    146               connectionTag =
    147                   SimulatorSimCallManager.addNewIncomingCall(
    148                       context, callerId, SimulatorSimCallManager.CALL_TYPE_VOICE, extras);
    149             })
    150         .show(activity.getSupportFragmentManager(), "SimulatorDialog");
    151   }
    152 
    153   private void addNewOutgoingCall() {
    154     String callerId = "+55-31-2128-6800"; // Brazil office.
    155     connectionTag =
    156         SimulatorSimCallManager.addNewOutgoingCall(
    157             context, callerId, SimulatorSimCallManager.CALL_TYPE_VOICE);
    158   }
    159 
    160   private void addNewOutgoingCall(AppCompatActivity activity) {
    161     SimulatorDialogFragment.newInstance(
    162             (callerId, callerIdPresentation) -> {
    163               Bundle extras = new Bundle();
    164               extras.putInt(Simulator.PRESENTATION_CHOICE, callerIdPresentation);
    165               connectionTag =
    166                   SimulatorSimCallManager.addNewOutgoingCall(
    167                       context, callerId, SimulatorSimCallManager.CALL_TYPE_VOICE, extras);
    168             })
    169         .show(activity.getSupportFragmentManager(), "SimulatorDialog");
    170   }
    171 
    172   private void addSpamIncomingCall() {
    173     String callerId = "+1-661-778-3020"; /* Blacklisted custom spam number */
    174     connectionTag =
    175         SimulatorSimCallManager.addNewIncomingCall(
    176             context, callerId, SimulatorSimCallManager.CALL_TYPE_VOICE);
    177   }
    178 
    179   private void addNewEmergencyCallBack() {
    180     String callerId = "911";
    181     connectionTag =
    182         SimulatorSimCallManager.addNewIncomingCall(
    183             context, callerId, SimulatorSimCallManager.CALL_TYPE_VOICE);
    184   }
    185 
    186   @Override
    187   public void onNewOutgoingConnection(@NonNull SimulatorConnection connection) {
    188     if (isMyConnection(connection)) {
    189       LogUtil.i("SimulatorVoiceCall.onNewOutgoingConnection", "connection created");
    190       handleNewConnection(connection);
    191 
    192       // Telecom will force the connection to switch to Dialing when we return it. Wait until after
    193       // we're returned it before changing call state.
    194       ThreadUtil.postOnUiThread(connection::setActive);
    195     }
    196   }
    197 
    198   @Override
    199   public void onNewIncomingConnection(@NonNull SimulatorConnection connection) {
    200     if (isMyConnection(connection)) {
    201       LogUtil.i("SimulatorVoiceCall.onNewIncomingConnection", "connection created");
    202       handleNewConnection(connection);
    203     }
    204   }
    205 
    206   @Override
    207   public void onConference(
    208       @NonNull SimulatorConnection connection1, @NonNull SimulatorConnection connection2) {}
    209 
    210   private void handleNewConnection(@NonNull SimulatorConnection connection) {
    211     connection.addListener(this);
    212     connection.setConnectionCapabilities(
    213         connection.getConnectionCapabilities()
    214             | Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL
    215             | Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL);
    216   }
    217 
    218   private boolean isMyConnection(@NonNull Connection connection) {
    219     return connection.getExtras().getBoolean(connectionTag);
    220   }
    221 
    222   @Override
    223   public void onEvent(@NonNull SimulatorConnection connection, @NonNull Event event) {
    224     switch (event.type) {
    225       case Event.NONE:
    226         throw Assert.createIllegalStateFailException();
    227       case Event.REJECT:
    228         connection.setDisconnected(new DisconnectCause(DisconnectCause.REJECTED));
    229         break;
    230       case Event.HOLD:
    231         connection.setOnHold();
    232         break;
    233       case Event.ANSWER:
    234       case Event.UNHOLD:
    235         connection.setActive();
    236         break;
    237       case Event.DISCONNECT:
    238         connection.setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
    239         if (connection.getExtras().getBoolean(Simulator.IS_ENRICHED_CALL)) {
    240           getEnrichedCallManager().unregisterStateChangedListener(simulatorEnrichedCall);
    241         }
    242         break;
    243       case Event.SESSION_MODIFY_REQUEST:
    244         ThreadUtil.postDelayedOnUiThread(() -> connection.handleSessionModifyRequest(event), 2000);
    245         break;
    246       case Event.START_RTT:
    247         // TODO(wangqi): Add random accept/decline.
    248         boolean accept = true;
    249         if (accept) {
    250           connection.sendRttInitiationSuccess();
    251         } else {
    252           connection.sendRttInitiationFailure(RttModifyStatus.SESSION_MODIFY_REQUEST_FAIL);
    253         }
    254         break;
    255       default:
    256         LogUtil.i("SimulatorVoiceCall.onEvent", "unexpected event: " + event.type);
    257         break;
    258     }
    259   }
    260 
    261   @NonNull
    262   private EnrichedCallManager getEnrichedCallManager() {
    263     return EnrichedCallComponent.get(context).getEnrichedCallManager();
    264   }
    265 }
    266