diff options
Diffstat (limited to 'java/com/android/dialer/simulator/impl')
9 files changed, 488 insertions, 27 deletions
diff --git a/java/com/android/dialer/simulator/impl/SimulatorConference.java b/java/com/android/dialer/simulator/impl/SimulatorConference.java new file mode 100644 index 000000000..7468b56b5 --- /dev/null +++ b/java/com/android/dialer/simulator/impl/SimulatorConference.java @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2017 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.dialer.simulator.impl; + +import android.support.annotation.NonNull; +import android.telecom.CallAudioState; +import android.telecom.Conference; +import android.telecom.Connection; +import android.telecom.PhoneAccountHandle; +import com.android.dialer.common.Assert; +import com.android.dialer.common.LogUtil; +import com.android.dialer.simulator.Simulator; +import com.android.dialer.simulator.Simulator.Event; +import java.util.ArrayList; +import java.util.List; + +/** + * Represents a conference call. When a user merges two phone calls we create an instance of this + * conference object and add it to the connection service. All operations such as hold and DTMF are + * then performed on this object. + */ +public final class SimulatorConference extends Conference implements SimulatorConnection.Listener { + static final int PROPERTY_GENERIC_CONFERENCE = 1 << 1; + + private final List<Listener> listeners = new ArrayList<>(); + private final List<Event> events = new ArrayList<>(); + private final int conferenceType; + + private SimulatorConference( + PhoneAccountHandle handle, @Simulator.ConferenceType int conferenceType) { + super(handle); + this.conferenceType = conferenceType; + setActive(); + } + + static SimulatorConference newGsmConference(PhoneAccountHandle handle) { + SimulatorConference simulatorConference = + new SimulatorConference(handle, Simulator.CONFERENCE_TYPE_GSM); + simulatorConference.setConnectionCapabilities( + Connection.CAPABILITY_MUTE + | Connection.CAPABILITY_SUPPORT_HOLD + | Connection.CAPABILITY_HOLD + | Connection.CAPABILITY_MANAGE_CONFERENCE); + return simulatorConference; + } + + public void addListener(@NonNull Listener listener) { + listeners.add(Assert.isNotNull(listener)); + } + + public void removeListener(@NonNull Listener listener) { + listeners.remove(Assert.isNotNull(listener)); + } + + @NonNull + public List<Event> getEvents() { + return events; + } + + @Override + public void onCallAudioStateChanged(CallAudioState state) { + LogUtil.enterBlock("SimulatorConference.onCallAudioStateChanged"); + onEvent(new Event(Event.CALL_AUDIO_STATE_CHANGED)); + } + + @Override + public void onConnectionAdded(Connection connection) { + LogUtil.enterBlock("SimulatorConference.onConnectionAdded"); + onEvent( + new Event( + Event.CONNECTION_ADDED, SimulatorSimCallManager.getConnectionTag(connection), null)); + ((SimulatorConnection) connection).addListener(this); + } + + @Override + public void onDisconnect() { + LogUtil.enterBlock("SimulatorConference.onDisconnect"); + onEvent(new Event(Event.DISCONNECT)); + } + + @Override + public void onHold() { + LogUtil.enterBlock("SimulatorConference.onHold"); + onEvent(new Event(Event.HOLD)); + } + + @Override + public void onMerge(Connection connection) { + LogUtil.i("SimulatorConference.onMerge", "connection: " + connection); + onEvent(new Event(Event.MERGE, SimulatorSimCallManager.getConnectionTag(connection), null)); + } + + @Override + public void onMerge() { + LogUtil.enterBlock("SimulatorConference.onMerge"); + onEvent(new Event(Event.MERGE)); + } + + @Override + public void onPlayDtmfTone(char c) { + LogUtil.enterBlock("SimulatorConference.onPlayDtmfTone"); + onEvent(new Event(Event.DTMF, Character.toString(c), null)); + } + + @Override + public void onSeparate(Connection connection) { + LogUtil.i("SimulatorConference.onSeparate", "connection: " + connection); + onEvent(new Event(Event.SEPARATE, SimulatorSimCallManager.getConnectionTag(connection), null)); + } + + @Override + public void onSwap() { + LogUtil.enterBlock("SimulatorConference.onSwap"); + onEvent(new Event(Event.SWAP)); + } + + @Override + public void onUnhold() { + LogUtil.enterBlock("SimulatorConference.onUnhold"); + onEvent(new Event(Event.UNHOLD)); + } + + @Override + public void onEvent(@NonNull SimulatorConnection connection, @NonNull Event event) { + if (conferenceType == Simulator.CONFERENCE_TYPE_GSM) { + onGsmEvent(connection, event); + } + } + + private void onGsmEvent(@NonNull SimulatorConnection connection, @NonNull Event event) { + if (event.type == Event.STATE_CHANGE + && Connection.stateToString(Connection.STATE_DISCONNECTED).equals(event.data2)) { + removeConnection(connection); + connection.removeListener(this); + if (getConnections().size() <= 1) { + // When only one connection exists, it's not conference call anymore + setDisconnected(connection.getDisconnectCause()); + destroy(); + } + } + } + + void onEvent(@NonNull Event event) { + events.add(Assert.isNotNull(event)); + for (Listener listener : new ArrayList<>(listeners)) { + listener.onEvent(this, event); + } + } + + /** Callback for when a new event arrives. */ + public interface Listener { + void onEvent(@NonNull SimulatorConference conference, @NonNull Event event); + } +} diff --git a/java/com/android/dialer/simulator/impl/SimulatorConferenceCreator.java b/java/com/android/dialer/simulator/impl/SimulatorConferenceCreator.java new file mode 100644 index 000000000..838b58dc2 --- /dev/null +++ b/java/com/android/dialer/simulator/impl/SimulatorConferenceCreator.java @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2017 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.dialer.simulator.impl; + +import android.content.Context; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.telecom.Conferenceable; +import android.telecom.Connection; +import android.telecom.DisconnectCause; +import com.android.dialer.common.Assert; +import com.android.dialer.common.LogUtil; +import com.android.dialer.common.concurrent.ThreadUtil; +import com.android.dialer.simulator.Simulator; +import com.android.dialer.simulator.Simulator.Event; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +/** Creates a conference with a given number of participants. */ +final class SimulatorConferenceCreator + implements SimulatorConnectionService.Listener, + SimulatorConnection.Listener, + SimulatorConference.Listener { + private static final String EXTRA_CALL_COUNT = "call_count"; + + @NonNull private final Context context; + @NonNull private final List<String> connectionTags = new ArrayList<>(); + @Simulator.ConferenceType private final int conferenceType; + + public SimulatorConferenceCreator( + @NonNull Context context, @Simulator.ConferenceType int conferenceType) { + this.context = Assert.isNotNull(context); + this.conferenceType = conferenceType; + } + + void start(int callCount) { + SimulatorConnectionService.addListener(this); + addNextCall(callCount); + } + + private void addNextCall(int callCount) { + LogUtil.i("SimulatorConferenceCreator.addNextIncomingCall", "callCount: " + callCount); + if (callCount <= 0) { + LogUtil.i("SimulatorConferenceCreator.addNextCall", "done adding calls"); + return; + } + + String callerId = String.format(Locale.US, "+1-650-234%04d", callCount); + Bundle extras = new Bundle(); + extras.putInt(EXTRA_CALL_COUNT, callCount - 1); + connectionTags.add( + SimulatorSimCallManager.addNewIncomingCall(context, callerId, false /* isVideo */, extras)); + } + + @Override + public void onNewIncomingConnection(@NonNull SimulatorConnection connection) { + if (!isMyConnection(connection)) { + LogUtil.i("SimulatorConferenceCreator.onNewOutgoingConnection", "unknown connection"); + return; + } + + LogUtil.i("SimulatorConferenceCreator.onNewOutgoingConnection", "connection created"); + connection.addListener(this); + + // Telecom will force the connection to switch to DIALING when we return it. Wait until after + // we're returned it before changing call state. + ThreadUtil.postOnUiThread(() -> connection.setActive()); + + // Once the connection is active, go ahead and conference it and add the next call. + ThreadUtil.postDelayedOnUiThread( + () -> { + SimulatorConference conference = findCurrentConference(); + if (conference == null) { + Assert.checkArgument(conferenceType == Simulator.CONFERENCE_TYPE_GSM); + conference = + SimulatorConference.newGsmConference( + SimulatorSimCallManager.getSystemPhoneAccountHandle(context)); + conference.addListener(this); + SimulatorConnectionService.getInstance().addConference(conference); + } + updateConferenceableConnections(); + conference.addConnection(connection); + addNextCall(getCallCount(connection)); + }, + 1000); + } + + @Override + public void onNewOutgoingConnection(@NonNull SimulatorConnection connection) {} + + /** + * This is called when the user clicks the merge button. We create the initial conference + * automatically but with this method we can let the user split and merge calls as desired. + */ + @Override + public void onConference( + @NonNull SimulatorConnection connection1, @NonNull SimulatorConnection connection2) { + LogUtil.enterBlock("SimulatorConferenceCreator.onConference"); + if (!isMyConnection(connection1) || !isMyConnection(connection2)) { + LogUtil.i("SimulatorConferenceCreator.onConference", "unknown connections, ignoring"); + return; + } + + if (connection1.getConference() != null) { + connection1.getConference().addConnection(connection2); + } else if (connection2.getConference() != null) { + connection2.getConference().addConnection(connection1); + } else { + Assert.checkArgument(conferenceType == Simulator.CONFERENCE_TYPE_GSM); + SimulatorConference conference = + SimulatorConference.newGsmConference( + SimulatorSimCallManager.getSystemPhoneAccountHandle(context)); + conference.addConnection(connection1); + conference.addConnection(connection2); + conference.addListener(this); + SimulatorConnectionService.getInstance().addConference(conference); + } + } + + private boolean isMyConnection(@NonNull Connection connection) { + for (String connectionTag : connectionTags) { + if (connection.getExtras().getBoolean(connectionTag)) { + return true; + } + } + return false; + } + + private void updateConferenceableConnections() { + LogUtil.enterBlock("SimulatorConferenceCreator.updateConferenceableConnections"); + for (String connectionTag : connectionTags) { + SimulatorConnection connection = SimulatorSimCallManager.findConnectionByTag(connectionTag); + List<Conferenceable> conferenceables = getMyConferenceables(); + conferenceables.remove(connection); + conferenceables.remove(connection.getConference()); + connection.setConferenceables(conferenceables); + } + } + + private List<Conferenceable> getMyConferenceables() { + List<Conferenceable> conferenceables = new ArrayList<>(); + for (String connectionTag : connectionTags) { + SimulatorConnection connection = SimulatorSimCallManager.findConnectionByTag(connectionTag); + conferenceables.add(connection); + if (connection.getConference() != null + && !conferenceables.contains(connection.getConference())) { + conferenceables.add(connection.getConference()); + } + } + return conferenceables; + } + + @Nullable + private SimulatorConference findCurrentConference() { + for (String connectionTag : connectionTags) { + SimulatorConnection connection = SimulatorSimCallManager.findConnectionByTag(connectionTag); + if (connection.getConference() != null) { + return (SimulatorConference) connection.getConference(); + } + } + return null; + } + + private static int getCallCount(@NonNull Connection connection) { + return connection.getExtras().getInt(EXTRA_CALL_COUNT); + } + + @Override + public void onEvent(@NonNull SimulatorConnection connection, @NonNull Event event) { + switch (event.type) { + case Event.NONE: + throw Assert.createIllegalStateFailException(); + case Event.HOLD: + connection.setOnHold(); + break; + case Event.UNHOLD: + connection.setActive(); + break; + case Event.DISCONNECT: + connection.setDisconnected(new DisconnectCause(DisconnectCause.LOCAL)); + break; + default: + LogUtil.i( + "SimulatorConferenceCreator.onEvent", "unexpected conference event: " + event.type); + break; + } + } + + @Override + public void onEvent(@NonNull SimulatorConference conference, @NonNull Event event) { + switch (event.type) { + case Event.MERGE: + int capabilities = conference.getConnectionCapabilities(); + capabilities |= Connection.CAPABILITY_SWAP_CONFERENCE; + conference.setConnectionCapabilities(capabilities); + break; + case Event.SEPARATE: + SimulatorConnection connectionToRemove = + SimulatorSimCallManager.findConnectionByTag(event.data1); + conference.removeConnection(connectionToRemove); + break; + case Event.DISCONNECT: + for (Connection connection : new ArrayList<>(conference.getConnections())) { + connection.setDisconnected(new DisconnectCause(DisconnectCause.LOCAL)); + } + break; + default: + LogUtil.i( + "SimulatorConferenceCreator.onEvent", "unexpected conference event: " + event.type); + break; + } + } +} diff --git a/java/com/android/dialer/simulator/impl/SimulatorConnection.java b/java/com/android/dialer/simulator/impl/SimulatorConnection.java index 70c1095dc..e4a34b51b 100644 --- a/java/com/android/dialer/simulator/impl/SimulatorConnection.java +++ b/java/com/android/dialer/simulator/impl/SimulatorConnection.java @@ -41,7 +41,9 @@ public final class SimulatorConnection extends Connection { CAPABILITY_MUTE | CAPABILITY_SUPPORT_HOLD | CAPABILITY_HOLD - | CAPABILITY_CAN_UPGRADE_TO_VIDEO); + | CAPABILITY_CAN_UPGRADE_TO_VIDEO + | CAPABILITY_DISCONNECT_FROM_CONFERENCE + | CAPABILITY_SEPARATE_FROM_CONFERENCE); setVideoProvider(new SimulatorVideoProvider(context, this)); } @@ -108,7 +110,7 @@ public final class SimulatorConnection extends Connection { void onEvent(@NonNull Event event) { events.add(Assert.isNotNull(event)); - for (Listener listener : listeners) { + for (Listener listener : new ArrayList<>(listeners)) { listener.onEvent(this, event); } } diff --git a/java/com/android/dialer/simulator/impl/SimulatorConnectionService.java b/java/com/android/dialer/simulator/impl/SimulatorConnectionService.java index 25d4a7240..465890cf0 100644 --- a/java/com/android/dialer/simulator/impl/SimulatorConnectionService.java +++ b/java/com/android/dialer/simulator/impl/SimulatorConnectionService.java @@ -109,6 +109,19 @@ public class SimulatorConnectionService extends ConnectionService { return connection; } + @Override + public void onConference(Connection connection1, Connection connection2) { + LogUtil.i( + "SimulatorConnectionService.onConference", + "connection1: " + + SimulatorSimCallManager.getConnectionTag(connection1) + + ", connection2: " + + SimulatorSimCallManager.getConnectionTag(connection2)); + for (Listener listener : listeners) { + listener.onConference((SimulatorConnection) connection1, (SimulatorConnection) connection2); + } + } + private static Uri getPhoneNumber(ConnectionRequest request) { String phoneNumber = request.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER); return Uri.fromParts(PhoneAccount.SCHEME_TEL, phoneNumber, null); @@ -116,8 +129,11 @@ public class SimulatorConnectionService extends ConnectionService { /** Callback used to notify listeners when a new connection has been added. */ public interface Listener { - void onNewOutgoingConnection(SimulatorConnection connection); + void onNewOutgoingConnection(@NonNull SimulatorConnection connection); + + void onNewIncomingConnection(@NonNull SimulatorConnection connection); - void onNewIncomingConnection(SimulatorConnection connection); + void onConference( + @NonNull SimulatorConnection connection1, @NonNull SimulatorConnection connection2); } } diff --git a/java/com/android/dialer/simulator/impl/SimulatorMissedCallCreator.java b/java/com/android/dialer/simulator/impl/SimulatorMissedCallCreator.java index f85f46602..6d4a26278 100644 --- a/java/com/android/dialer/simulator/impl/SimulatorMissedCallCreator.java +++ b/java/com/android/dialer/simulator/impl/SimulatorMissedCallCreator.java @@ -62,6 +62,10 @@ final class SimulatorMissedCallCreator implements SimulatorConnectionService.Lis DISCONNECT_DELAY_MILLIS); } + @Override + public void onConference( + @NonNull SimulatorConnection connection1, @NonNull SimulatorConnection connection2) {} + private void addNextIncomingCall(int callCount) { if (callCount <= 0) { LogUtil.i("SimulatorMissedCallCreator.addNextIncomingCall", "done adding calls"); diff --git a/java/com/android/dialer/simulator/impl/SimulatorSimCallManager.java b/java/com/android/dialer/simulator/impl/SimulatorSimCallManager.java index 33eac51d1..00899fd69 100644 --- a/java/com/android/dialer/simulator/impl/SimulatorSimCallManager.java +++ b/java/com/android/dialer/simulator/impl/SimulatorSimCallManager.java @@ -21,6 +21,7 @@ import android.content.Context; import android.net.Uri; import android.os.Bundle; import android.support.annotation.NonNull; +import android.telecom.Connection; import android.telecom.ConnectionRequest; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; @@ -47,6 +48,7 @@ public class SimulatorSimCallManager { private static final String SIM_CALL_MANAGER_ACCOUNT_ID = "SIMULATOR_ACCOUNT_ID"; private static final String VIDEO_PROVIDER_ACCOUNT_ID = "SIMULATOR_VIDEO_ACCOUNT_ID"; private static final String EXTRA_IS_SIMULATOR_CONNECTION = "is_simulator_connection"; + private static final String EXTRA_CONNECTION_TAG = "connection_tag"; static void register(@NonNull Context context) { LogUtil.enterBlock("SimulatorSimCallManager.register"); @@ -85,9 +87,7 @@ public class SimulatorSimCallManager { register(context); extras = new Bundle(extras); - extras.putBoolean(EXTRA_IS_SIMULATOR_CONNECTION, true); - String connectionTag = createUniqueConnectionTag(); - extras.putBoolean(connectionTag, true); + extras.putAll(createSimulatorConnectionExtras()); Bundle outgoingCallExtras = new Bundle(); outgoingCallExtras.putBundle(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras); @@ -102,7 +102,7 @@ public class SimulatorSimCallManager { } catch (SecurityException e) { throw Assert.createIllegalStateFailException("Unable to place call: " + e); } - return connectionTag; + return extras.getString(EXTRA_CONNECTION_TAG); } @NonNull @@ -123,14 +123,12 @@ public class SimulatorSimCallManager { extras = new Bundle(extras); extras.putString(TelephonyManager.EXTRA_INCOMING_NUMBER, callerId); - extras.putBoolean(EXTRA_IS_SIMULATOR_CONNECTION, true); - String connectionTag = createUniqueConnectionTag(); - extras.putBoolean(connectionTag, true); + extras.putAll(createSimulatorConnectionExtras()); TelecomManager telecomManager = context.getSystemService(TelecomManager.class); telecomManager.addNewIncomingCall( isVideo ? getVideoProviderHandle(context) : getSystemPhoneAccountHandle(context), extras); - return connectionTag; + return extras.getString(EXTRA_CONNECTION_TAG); } @NonNull @@ -167,7 +165,7 @@ public class SimulatorSimCallManager { } @NonNull - private static PhoneAccountHandle getSystemPhoneAccountHandle(@NonNull Context context) { + public static PhoneAccountHandle getSystemPhoneAccountHandle(@NonNull Context context) { TelecomManager telecomManager = context.getSystemService(TelecomManager.class); List<PhoneAccountHandle> handles; try { @@ -190,10 +188,37 @@ public class SimulatorSimCallManager { } @NonNull + public static String getConnectionTag(@NonNull Connection connection) { + String connectionTag = connection.getExtras().getString(EXTRA_CONNECTION_TAG); + return Assert.isNotNull(connectionTag); + } + + @NonNull + public static SimulatorConnection findConnectionByTag(@NonNull String connectionTag) { + Assert.isNotNull(connectionTag); + for (Connection connection : SimulatorConnectionService.getInstance().getAllConnections()) { + if (connection.getExtras().getBoolean(connectionTag)) { + return (SimulatorConnection) connection; + } + } + throw Assert.createIllegalStateFailException(); + } + + @NonNull private static String createUniqueConnectionTag() { int callId = new Random().nextInt(); return String.format("simulator_phone_call_%x", Math.abs(callId)); } + @NonNull + static Bundle createSimulatorConnectionExtras() { + Bundle extras = new Bundle(); + extras.putBoolean(EXTRA_IS_SIMULATOR_CONNECTION, true); + String connectionTag = createUniqueConnectionTag(); + extras.putString(EXTRA_CONNECTION_TAG, connectionTag); + extras.putBoolean(connectionTag, true); + return extras; + } + private SimulatorSimCallManager() {} } diff --git a/java/com/android/dialer/simulator/impl/SimulatorSpamCallCreator.java b/java/com/android/dialer/simulator/impl/SimulatorSpamCallCreator.java index 757658ddf..a843ec03f 100644 --- a/java/com/android/dialer/simulator/impl/SimulatorSpamCallCreator.java +++ b/java/com/android/dialer/simulator/impl/SimulatorSpamCallCreator.java @@ -74,6 +74,10 @@ final class SimulatorSpamCallCreator implements SimulatorConnectionService.Liste DISCONNECT_DELAY_MILLIS); } + @Override + public void onConference( + @NonNull SimulatorConnection connection1, @NonNull SimulatorConnection connection2) {} + private void addNextIncomingCall(int callCount) { if (callCount <= 0) { LogUtil.i("SimulatorSpamCallCreator.addNextIncomingCall", "done adding calls"); diff --git a/java/com/android/dialer/simulator/impl/SimulatorVideoCall.java b/java/com/android/dialer/simulator/impl/SimulatorVideoCall.java index 3f00ab183..f7256a11c 100644 --- a/java/com/android/dialer/simulator/impl/SimulatorVideoCall.java +++ b/java/com/android/dialer/simulator/impl/SimulatorVideoCall.java @@ -109,6 +109,10 @@ final class SimulatorVideoCall } } + @Override + public void onConference( + @NonNull SimulatorConnection connection1, @NonNull SimulatorConnection connection2) {} + private boolean isVideoAccountEnabled() { SimulatorSimCallManager.register(context); return context @@ -150,15 +154,12 @@ final class SimulatorVideoCall case Event.DISCONNECT: connection.setDisconnected(new DisconnectCause(DisconnectCause.LOCAL)); break; - case Event.STATE_CHANGE: - break; - case Event.DTMF: - break; case Event.SESSION_MODIFY_REQUEST: ThreadUtil.postDelayedOnUiThread(() -> connection.handleSessionModifyRequest(event), 2000); break; default: - throw Assert.createIllegalStateFailException(); + LogUtil.i("SimulatorVideoCall.onEvent", "unexpected event: " + event.type); + break; } } } diff --git a/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java b/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java index 8eefb48d9..f478d55d0 100644 --- a/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java +++ b/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java @@ -25,6 +25,7 @@ import android.view.ActionProvider; import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; import com.android.dialer.common.concurrent.ThreadUtil; +import com.android.dialer.simulator.Simulator; import com.android.dialer.simulator.Simulator.Event; /** Entry point in the simulator to create voice calls. */ @@ -37,7 +38,10 @@ final class SimulatorVoiceCall return new SimulatorSubMenu(context) .addItem("Incoming call", () -> new SimulatorVoiceCall(context).addNewIncomingCall(false)) .addItem("Outgoing call", () -> new SimulatorVoiceCall(context).addNewOutgoingCall()) - .addItem("Spam call", () -> new SimulatorVoiceCall(context).addNewIncomingCall(true)); + .addItem("Spam call", () -> new SimulatorVoiceCall(context).addNewIncomingCall(true)) + .addItem( + "GSM conference", + () -> new SimulatorConferenceCreator(context, Simulator.CONFERENCE_TYPE_GSM).start(5)); } private SimulatorVoiceCall(@NonNull Context context) { @@ -62,21 +66,28 @@ final class SimulatorVoiceCall @Override public void onNewOutgoingConnection(@NonNull SimulatorConnection connection) { - if (connection.getExtras().getBoolean(connectionTag)) { + if (isMyConnection(connection)) { LogUtil.i("SimulatorVoiceCall.onNewOutgoingConnection", "connection created"); handleNewConnection(connection); - connection.setActive(); + + // Telecom will force the connection to switch to Dialing when we return it. Wait until after + // we're returned it before changing call state. + ThreadUtil.postOnUiThread(connection::setActive); } } @Override public void onNewIncomingConnection(@NonNull SimulatorConnection connection) { - if (connection.getExtras().getBoolean(connectionTag)) { + if (isMyConnection(connection)) { LogUtil.i("SimulatorVoiceCall.onNewIncomingConnection", "connection created"); handleNewConnection(connection); } } + @Override + public void onConference( + @NonNull SimulatorConnection connection1, @NonNull SimulatorConnection connection2) {} + private void handleNewConnection(@NonNull SimulatorConnection connection) { connection.addListener(this); connection.setConnectionCapabilities( @@ -85,6 +96,10 @@ final class SimulatorVoiceCall | Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL); } + private boolean isMyConnection(@NonNull Connection connection) { + return connection.getExtras().getBoolean(connectionTag); + } + @Override public void onEvent(@NonNull SimulatorConnection connection, @NonNull Event event) { switch (event.type) { @@ -105,15 +120,12 @@ final class SimulatorVoiceCall case Event.DISCONNECT: connection.setDisconnected(new DisconnectCause(DisconnectCause.LOCAL)); break; - case Event.STATE_CHANGE: - break; - case Event.DTMF: - break; case Event.SESSION_MODIFY_REQUEST: ThreadUtil.postDelayedOnUiThread(() -> connection.handleSessionModifyRequest(event), 2000); break; default: - throw Assert.createIllegalStateFailException(); + LogUtil.i("SimulatorVoiceCall.onEvent", "unexpected event: " + event.type); + break; } } } |